rsh.c revision 4ecbd6db44d79348bc815f31096e53104f50838b
1/*
2 * Copyright (c) 1997, 1998, 1999 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "rsh_locl.h"
35RCSID("$Id: rsh.c,v 1.46 1999/12/16 11:53:50 assar Exp $");
36
37enum auth_method auth_method;
38int do_encrypt;
39int do_forward;
40int do_forwardable;
41int do_unique_tkfile = 0;
42char *unique_tkfile  = NULL;
43char tkfile[MAXPATHLEN];
44krb5_context context;
45krb5_keyblock *keyblock;
46krb5_crypto crypto;
47des_key_schedule schedule;
48des_cblock iv;
49
50
51/*
52 *
53 */
54
55static int input = 1;		/* Read from stdin */
56
57static int
58loop (int s, int errsock)
59{
60    fd_set real_readset;
61    int count = 1;
62
63    FD_ZERO(&real_readset);
64    FD_SET(s, &real_readset);
65    if (errsock != -1) {
66	FD_SET(errsock, &real_readset);
67	++count;
68    }
69    if(input)
70	FD_SET(STDIN_FILENO, &real_readset);
71
72    for (;;) {
73	int ret;
74	fd_set readset;
75	char buf[RSH_BUFSIZ];
76
77	readset = real_readset;
78	ret = select (max(s, errsock) + 1, &readset, NULL, NULL, NULL);
79	if (ret < 0) {
80	    if (errno == EINTR)
81		continue;
82	    else
83		err (1, "select");
84	}
85	if (FD_ISSET(s, &readset)) {
86	    ret = do_read (s, buf, sizeof(buf));
87	    if (ret < 0)
88		err (1, "read");
89	    else if (ret == 0) {
90		close (s);
91		FD_CLR(s, &real_readset);
92		if (--count == 0)
93		    return 0;
94	    } else
95		net_write (STDOUT_FILENO, buf, ret);
96	}
97	if (errsock != -1 && FD_ISSET(errsock, &readset)) {
98	    ret = do_read (errsock, buf, sizeof(buf));
99	    if (ret < 0)
100		err (1, "read");
101	    else if (ret == 0) {
102		close (errsock);
103		FD_CLR(errsock, &real_readset);
104		if (--count == 0)
105		    return 0;
106	    } else
107		net_write (STDERR_FILENO, buf, ret);
108	}
109	if (FD_ISSET(STDIN_FILENO, &readset)) {
110	    ret = read (STDIN_FILENO, buf, sizeof(buf));
111	    if (ret < 0)
112		err (1, "read");
113	    else if (ret == 0) {
114		close (STDIN_FILENO);
115		FD_CLR(STDIN_FILENO, &real_readset);
116		shutdown (s, SHUT_WR);
117	    } else
118		do_write (s, buf, ret);
119	}
120    }
121}
122
123#ifdef KRB4
124static int
125send_krb4_auth(int s,
126	       struct sockaddr *thisaddr,
127	       struct sockaddr *thataddr,
128	       const char *hostname,
129	       const char *remote_user,
130	       const char *local_user,
131	       size_t cmd_len,
132	       const char *cmd)
133{
134    KTEXT_ST text;
135    CREDENTIALS cred;
136    MSG_DAT msg;
137    int status;
138    size_t len;
139
140    status = krb_sendauth (do_encrypt ? KOPT_DO_MUTUAL : 0,
141			   s, &text, "rcmd",
142			   (char *)hostname, krb_realmofhost (hostname),
143			   getpid(), &msg, &cred, schedule,
144			   (struct sockaddr_in *)thisaddr,
145			   (struct sockaddr_in *)thataddr,
146			   KCMD_VERSION);
147    if (status != KSUCCESS) {
148	warnx ("%s: %s", hostname, krb_get_err_text(status));
149	return 1;
150    }
151    memcpy (iv, cred.session, sizeof(iv));
152
153    len = strlen(remote_user) + 1;
154    if (net_write (s, remote_user, len) != len) {
155	warn("write");
156	return 1;
157    }
158    if (net_write (s, cmd, cmd_len) != cmd_len) {
159	warn("write");
160	return 1;
161    }
162    return 0;
163}
164#endif /* KRB4 */
165
166/*
167 * Send forward information on `s' for host `hostname', them being
168 * forwardable themselves if `forwardable'
169 */
170
171static int
172krb5_forward_cred (krb5_auth_context auth_context,
173		   int s,
174		   const char *hostname,
175		   int forwardable)
176{
177    krb5_error_code ret;
178    krb5_ccache     ccache;
179    krb5_creds      creds;
180    krb5_kdc_flags  flags;
181    krb5_data       out_data;
182    krb5_principal  principal;
183
184    memset (&creds, 0, sizeof(creds));
185
186    ret = krb5_cc_default (context, &ccache);
187    if (ret) {
188	warnx ("could not forward creds: krb5_cc_default: %s",
189	       krb5_get_err_text (context, ret));
190	return 1;
191    }
192
193    ret = krb5_cc_get_principal (context, ccache, &principal);
194    if (ret) {
195	warnx ("could not forward creds: krb5_cc_get_principal: %s",
196	       krb5_get_err_text (context, ret));
197	return 1;
198    }
199
200    creds.client = principal;
201
202    ret = krb5_build_principal (context,
203				&creds.server,
204				strlen(principal->realm),
205				principal->realm,
206				"krbtgt",
207				principal->realm,
208				NULL);
209
210    if (ret) {
211	warnx ("could not forward creds: krb5_build_principal: %s",
212	       krb5_get_err_text (context, ret));
213	return 1;
214    }
215
216    creds.times.endtime = 0;
217
218    flags.i = 0;
219    flags.b.forwarded   = 1;
220    flags.b.forwardable = forwardable;
221
222    ret = krb5_get_forwarded_creds (context,
223				    auth_context,
224				    ccache,
225				    flags.i,
226				    hostname,
227				    &creds,
228				    &out_data);
229    if (ret) {
230	warnx ("could not forward creds: krb5_get_forwarded_creds: %s",
231	       krb5_get_err_text (context, ret));
232	return 1;
233    }
234
235    ret = krb5_write_message (context,
236			      (void *)&s,
237			      &out_data);
238    krb5_data_free (&out_data);
239
240    if (ret)
241	warnx ("could not forward creds: krb5_write_message: %s",
242	       krb5_get_err_text (context, ret));
243    return 0;
244}
245
246static int
247send_krb5_auth(int s,
248	       struct sockaddr *thisaddr,
249	       struct sockaddr *thataddr,
250	       const char *hostname,
251	       const char *remote_user,
252	       const char *local_user,
253	       size_t cmd_len,
254	       const char *cmd)
255{
256    krb5_principal server;
257    krb5_data cksum_data;
258    int status;
259    size_t len;
260    krb5_auth_context auth_context = NULL;
261
262    status = krb5_sname_to_principal(context,
263				     hostname,
264				     "host",
265				     KRB5_NT_SRV_HST,
266				     &server);
267    if (status) {
268	warnx ("%s: %s", hostname, krb5_get_err_text(context, status));
269	return 1;
270    }
271
272    cksum_data.length = asprintf ((char **)&cksum_data.data,
273				  "%u:%s%s%s",
274				  ntohs(socket_get_port(thataddr)),
275				  do_encrypt ? "-x " : "",
276				  cmd,
277				  remote_user);
278
279    status = krb5_sendauth (context,
280			    &auth_context,
281			    &s,
282			    KCMD_VERSION,
283			    NULL,
284			    server,
285			    do_encrypt ? AP_OPTS_MUTUAL_REQUIRED : 0,
286			    &cksum_data,
287			    NULL,
288			    NULL,
289			    NULL,
290			    NULL,
291			    NULL);
292    if (status) {
293	warnx ("%s: %s", hostname, krb5_get_err_text(context, status));
294	return 1;
295    }
296
297    status = krb5_auth_con_getkey (context, auth_context, &keyblock);
298    if (status) {
299	warnx ("krb5_auth_con_getkey: %s", krb5_get_err_text(context, status));
300	return 1;
301    }
302
303    status = krb5_auth_con_setaddrs_from_fd (context,
304					     auth_context,
305					     &s);
306    if (status) {
307        warnx("krb5_auth_con_setaddrs_from_fd: %s",
308	      krb5_get_err_text(context, status));
309        return(1);
310    }
311
312    status = krb5_crypto_init(context, keyblock, 0, &crypto);
313    if(status) {
314	warnx ("krb5_crypto_init: %s", krb5_get_err_text(context, status));
315	return 1;
316    }
317
318    len = strlen(remote_user) + 1;
319    if (net_write (s, remote_user, len) != len) {
320	warn ("write");
321	return 1;
322    }
323    if (do_encrypt && net_write (s, "-x ", 3) != 3) {
324	warn ("write");
325	return 1;
326    }
327    if (net_write (s, cmd, cmd_len) != cmd_len) {
328	warn ("write");
329	return 1;
330    }
331
332    if (do_unique_tkfile) {
333	if (net_write (s, tkfile, strlen(tkfile)) != strlen(tkfile)) {
334	    warn ("write");
335	    return 1;
336	}
337    }
338    len = strlen(local_user) + 1;
339    if (net_write (s, local_user, len) != len) {
340	warn ("write");
341	return 1;
342    }
343
344    if (!do_forward
345	|| krb5_forward_cred (auth_context, s, hostname, do_forwardable)) {
346	/* Empty forwarding info */
347
348	u_char zero[4] = {0, 0, 0, 0};
349	write (s, &zero, 4);
350    }
351    krb5_auth_con_free (context, auth_context);
352    return 0;
353}
354
355static int
356send_broken_auth(int s,
357		 struct sockaddr *thisaddr,
358		 struct sockaddr *thataddr,
359		 const char *hostname,
360		 const char *remote_user,
361		 const char *local_user,
362		 size_t cmd_len,
363		 const char *cmd)
364{
365    size_t len;
366
367    len = strlen(local_user) + 1;
368    if (net_write (s, local_user, len) != len) {
369	warn ("write");
370	return 1;
371    }
372    len = strlen(remote_user) + 1;
373    if (net_write (s, remote_user, len) != len) {
374	warn ("write");
375	return 1;
376    }
377    if (net_write (s, cmd, cmd_len) != cmd_len) {
378	warn ("write");
379	return 1;
380    }
381    return 0;
382}
383
384static int
385proto (int s, int errsock,
386       const char *hostname, const char *local_user, const char *remote_user,
387       const char *cmd, size_t cmd_len,
388       int (*auth_func)(int s,
389			struct sockaddr *this, struct sockaddr *that,
390			const char *hostname, const char *remote_user,
391			const char *local_user, size_t cmd_len,
392			const char *cmd))
393{
394    int errsock2;
395    char buf[BUFSIZ];
396    char *p;
397    size_t len;
398    char reply;
399    struct sockaddr_storage thisaddr_ss;
400    struct sockaddr *thisaddr = (struct sockaddr *)&thisaddr_ss;
401    struct sockaddr_storage thataddr_ss;
402    struct sockaddr *thataddr = (struct sockaddr *)&thataddr_ss;
403    struct sockaddr_storage erraddr_ss;
404    struct sockaddr *erraddr = (struct sockaddr *)&erraddr_ss;
405    int addrlen;
406    int ret;
407
408    addrlen = sizeof(thisaddr_ss);
409    if (getsockname (s, thisaddr, &addrlen) < 0) {
410	warn ("getsockname(%s)", hostname);
411	return 1;
412    }
413    addrlen = sizeof(thataddr_ss);
414    if (getpeername (s, thataddr, &addrlen) < 0) {
415	warn ("getpeername(%s)", hostname);
416	return 1;
417    }
418
419    if (errsock != -1) {
420
421	addrlen = sizeof(erraddr_ss);
422	if (getsockname (errsock, erraddr, &addrlen) < 0) {
423	    warn ("getsockname");
424	    return 1;
425	}
426
427	if (listen (errsock, 1) < 0) {
428	    warn ("listen");
429	    return 1;
430	}
431
432	p = buf;
433	snprintf (p, sizeof(buf), "%u",
434		  ntohs(socket_get_port(erraddr)));
435	len = strlen(buf) + 1;
436	if(net_write (s, buf, len) != len) {
437	    warn ("write");
438	    close (errsock);
439	    return 1;
440	}
441
442	errsock2 = accept (errsock, NULL, NULL);
443	if (errsock2 < 0) {
444	    warn ("accept");
445	    close (errsock);
446	    return 1;
447	}
448	close (errsock);
449
450    } else {
451	if (net_write (s, "0", 2) != 2) {
452	    warn ("write");
453	    return 1;
454	}
455	errsock2 = -1;
456    }
457
458    if ((*auth_func)(s, thisaddr, thataddr, hostname,
459		     remote_user, local_user,
460		     cmd_len, cmd)) {
461	close (errsock2);
462	return 1;
463    }
464
465    ret = net_read (s, &reply, 1);
466    if (ret < 0) {
467	warn ("read");
468	close (errsock2);
469	return 1;
470    } else if (ret == 0) {
471	warnx ("unexpected EOF from %s", hostname);
472	close (errsock2);
473	return 1;
474    }
475    if (reply != 0) {
476
477	warnx ("Error from rshd at %s:", hostname);
478
479	while ((ret = read (s, buf, sizeof(buf))) > 0)
480	    write (STDOUT_FILENO, buf, ret);
481        write (STDOUT_FILENO,"\n",1);
482	close (errsock2);
483	return 1;
484    }
485
486    return loop (s, errsock2);
487}
488
489/*
490 * Return in `res' a copy of the concatenation of `argc, argv' into
491 * malloced space.
492 */
493
494static size_t
495construct_command (char **res, int argc, char **argv)
496{
497    int i;
498    size_t len = 0;
499    char *tmp;
500
501    for (i = 0; i < argc; ++i)
502	len += strlen(argv[i]) + 1;
503    len = max (1, len);
504    tmp = malloc (len);
505    if (tmp == NULL)
506	errx (1, "malloc %u failed", len);
507
508    *tmp = '\0';
509    for (i = 0; i < argc - 1; ++i) {
510	strcat (tmp, argv[i]);
511	strcat (tmp, " ");
512    }
513    if (argc > 0)
514	strcat (tmp, argv[argc-1]);
515    *res = tmp;
516    return len;
517}
518
519static char *
520print_addr (const struct sockaddr_in *sin)
521{
522    char addr_str[256];
523    char *res;
524
525    inet_ntop (AF_INET, &sin->sin_addr, addr_str, sizeof(addr_str));
526    res = strdup(addr_str);
527    if (res == NULL)
528	errx (1, "malloc: out of memory");
529    return res;
530}
531
532static int
533doit_broken (int argc,
534	     char **argv,
535	     int optind,
536	     const char *host,
537	     const char *remote_user,
538	     const char *local_user,
539	     int port,
540	     int priv_socket1,
541	     int priv_socket2,
542	     const char *cmd,
543	     size_t cmd_len)
544{
545    struct addrinfo *ai, *a;
546    struct addrinfo hints;
547    int error;
548    char portstr[NI_MAXSERV];
549
550    if (priv_socket1 < 0) {
551	warnx ("unable to bind reserved port: is rsh setuid root?");
552	return 1;
553    }
554
555    memset (&hints, 0, sizeof(hints));
556    hints.ai_socktype = SOCK_STREAM;
557    hints.ai_protocol = IPPROTO_TCP;
558    hints.ai_family   = AF_INET;
559
560    snprintf (portstr, sizeof(portstr), "%u", ntohs(port));
561
562    error = getaddrinfo (host, portstr, &hints, &ai);
563    if (error) {
564	warnx ("%s: %s", host, gai_strerror(error));
565	return 1;
566    }
567
568    if (connect (priv_socket1, ai->ai_addr, ai->ai_addrlen) < 0) {
569	if (ai->ai_next == NULL) {
570	    freeaddrinfo (ai);
571	    return 1;
572	}
573
574	close(priv_socket1);
575	close(priv_socket2);
576
577	for (a = ai->ai_next; a != NULL; a = a->ai_next) {
578	    pid_t pid;
579
580	    pid = fork();
581	    if (pid < 0)
582		err (1, "fork");
583	    else if(pid == 0) {
584		char **new_argv;
585		int i = 0;
586		struct sockaddr_in *sin = (struct sockaddr_in *)a->ai_addr;
587
588		new_argv = malloc((argc + 2) * sizeof(*new_argv));
589		if (new_argv == NULL)
590		    errx (1, "malloc: out of memory");
591		new_argv[i] = argv[i];
592		++i;
593		if (optind == i)
594		    new_argv[i++] = print_addr (sin);
595		new_argv[i++] = "-K";
596		for(; i <= argc; ++i)
597		    new_argv[i] = argv[i - 1];
598		if (optind > 1)
599		    new_argv[optind + 1] = print_addr(sin);
600		new_argv[argc + 1] = NULL;
601		execv(PATH_RSH, new_argv);
602		err(1, "execv(%s)", PATH_RSH);
603	    } else {
604		int status;
605
606		freeaddrinfo (ai);
607
608		while(waitpid(pid, &status, 0) < 0)
609		    ;
610		if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
611		    return 0;
612	    }
613	}
614	return 1;
615    } else {
616	int ret;
617
618	freeaddrinfo (ai);
619
620	ret = proto (priv_socket1, priv_socket2,
621		     argv[optind],
622		     local_user, remote_user,
623		     cmd, cmd_len,
624		     send_broken_auth);
625	return ret;
626    }
627}
628
629static int
630doit (const char *hostname,
631      const char *remote_user,
632      const char *local_user,
633      int port,
634      const char *cmd,
635      size_t cmd_len,
636      int do_errsock,
637      int (*auth_func)(int s,
638		       struct sockaddr *this, struct sockaddr *that,
639		       const char *hostname, const char *remote_user,
640		       const char *local_user, size_t cmd_len,
641		       const char *cmd))
642{
643    struct addrinfo *ai, *a;
644    struct addrinfo hints;
645    int error;
646    char portstr[NI_MAXSERV];
647    int ret;
648
649    memset (&hints, 0, sizeof(hints));
650    hints.ai_socktype = SOCK_STREAM;
651    hints.ai_protocol = IPPROTO_TCP;
652
653    snprintf (portstr, sizeof(portstr), "%u", ntohs(port));
654
655    error = getaddrinfo (hostname, portstr, &hints, &ai);
656    if (error) {
657	errx (1, "%s: %s", hostname, gai_strerror(error));
658	return -1;
659    }
660
661    for (a = ai; a != NULL; a = a->ai_next) {
662	int s;
663	int errsock;
664
665	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
666	if (s < 0)
667	    continue;
668	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
669	    warn ("connect(%s)", hostname);
670	    close (s);
671	    continue;
672	}
673	if (do_errsock) {
674	    struct addrinfo *ea;
675	    struct addrinfo hints;
676
677	    memset (&hints, 0, sizeof(hints));
678	    hints.ai_socktype = a->ai_socktype;
679	    hints.ai_protocol = a->ai_protocol;
680	    hints.ai_family   = a->ai_family;
681	    hints.ai_flags    = AI_PASSIVE;
682
683	    error = getaddrinfo (NULL, "0", &hints, &ea);
684	    if (error)
685		errx (1, "getaddrinfo: %s", gai_strerror(error));
686	    errsock = socket (ea->ai_family, ea->ai_socktype, ea->ai_protocol);
687	    if (errsock < 0)
688		err (1, "socket");
689	    if (bind (errsock, ea->ai_addr, ea->ai_addrlen) < 0)
690		err (1, "bind");
691	    freeaddrinfo (ea);
692	} else
693	    errsock = -1;
694
695	freeaddrinfo (ai);
696	ret = proto (s, errsock,
697		     hostname,
698		     local_user, remote_user,
699		     cmd, cmd_len, auth_func);
700	close (s);
701	return ret;
702    }
703    warnx ("failed to contact %s", hostname);
704    freeaddrinfo (ai);
705    return -1;
706}
707
708#ifdef KRB4
709static int use_v4 = -1;
710#endif
711static int use_v5 = -1;
712static int use_only_broken = 0;
713static int use_broken = 1;
714static char *port_str;
715static const char *user;
716static int do_version;
717static int do_help;
718static int do_errsock = 1;
719
720struct getargs args[] = {
721#ifdef KRB4
722    { "krb4",	'4', arg_flag,		&use_v4,	"Use Kerberos V4",
723      NULL },
724#endif
725    { "krb5",	'5', arg_flag,		&use_v5,	"Use Kerberos V5",
726      NULL },
727    { "broken", 'K', arg_flag,		&use_only_broken, "Use priv port",
728      NULL },
729    { "input",	'n', arg_negative_flag,	&input,		"Close stdin",
730      NULL },
731    { "encrypt", 'x', arg_flag,		&do_encrypt,	"Encrypt connection",
732      NULL },
733    { "encrypt", 'z', arg_negative_flag,      &do_encrypt,
734      "Don't encrypt connection", NULL },
735    { "forward", 'f', arg_flag,		&do_forward,	"Forward credentials",
736      NULL },
737    { "forward", 'G', arg_negative_flag,&do_forward,	"Forward credentials",
738      NULL },
739    { "forwardable", 'F', arg_flag,	&do_forwardable,
740      "Forward forwardable credentials", NULL },
741    { "unique", 'u', arg_flag,	&do_unique_tkfile,
742      "Use unique remote tkfile", NULL },
743    { "tkfile", 'U', arg_string,  &unique_tkfile,
744      "Use that remote tkfile", NULL },
745    { "port",	'p', arg_string,	&port_str,	"Use this port",
746      "number-or-service" },
747    { "user",	'l', arg_string,	&user,		"Run as this user",
748      NULL },
749    { "stderr", 'e', arg_negative_flag, &do_errsock,	"don't open stderr"},
750    { "version", 0,  arg_flag,		&do_version,	"Print version",
751      NULL },
752    { "help",	 0,  arg_flag,		&do_help,	NULL,
753      NULL }
754};
755
756static void
757usage (int ret)
758{
759    arg_printusage (args,
760		    sizeof(args) / sizeof(args[0]),
761		    NULL,
762		    "host [command]");
763    exit (ret);
764}
765
766/*
767 *
768 */
769
770int
771main(int argc, char **argv)
772{
773    int priv_port1, priv_port2;
774    int priv_socket1, priv_socket2;
775    int port = 0;
776    int optind = 0;
777    int ret = 1;
778    char *cmd;
779    size_t cmd_len;
780    const char *local_user;
781    char *host = NULL;
782    int host_index = -1;
783    int status;
784
785    priv_port1 = priv_port2 = IPPORT_RESERVED-1;
786    priv_socket1 = rresvport(&priv_port1);
787    priv_socket2 = rresvport(&priv_port2);
788    setuid(getuid());
789
790    set_progname (argv[0]);
791
792    if (argc >= 2 && argv[1][0] != '-') {
793	host = argv[host_index = 1];
794	optind = 1;
795    }
796
797    status = krb5_init_context (&context);
798    if (status)
799        errx(1, "krb5_init_context failed: %u", status);
800
801    do_forwardable = krb5_config_get_bool (context, NULL,
802					   "libdefaults",
803					   "forwardable",
804					   NULL);
805
806    do_forward = krb5_config_get_bool (context, NULL,
807				       "libdefaults",
808				       "forward",
809				       NULL);
810
811    do_encrypt = krb5_config_get_bool (context, NULL,
812				       "libdefaults",
813				       "encrypt",
814				       NULL);
815
816    if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
817		&optind))
818	usage (1);
819
820    if (do_forwardable)
821	do_forward = 1;
822
823#if defined(KRB4) && defined(KRB5)
824    if(use_v4 == -1 && use_v5 == 1)
825	use_v4 = 0;
826    if(use_v5 == -1 && use_v4 == 1)
827	use_v5 = 0;
828#endif
829
830    if (use_only_broken) {
831#ifdef KRB4
832	use_v4 = 0;
833#endif
834	use_v5 = 0;
835    }
836
837    if (do_help)
838	usage (0);
839
840    if (do_version) {
841	print_version (NULL);
842	return 0;
843    }
844
845    if (do_unique_tkfile && unique_tkfile != NULL)
846	errx (1, "Only one of -u and -U allowed.");
847
848    if (do_unique_tkfile)
849	strcpy(tkfile,"-u ");
850    else if (unique_tkfile != NULL) {
851	if (strchr(unique_tkfile,' ') != NULL) {
852	    warnx("Space is not allowed in tkfilename");
853	    usage(1);
854	}
855	do_unique_tkfile = 1;
856	snprintf (tkfile, sizeof(tkfile), "-U %s ", unique_tkfile);
857    }
858
859    if (host == NULL) {
860	if (argc - optind < 1)
861	    usage (1);
862	else
863	    host = argv[host_index = optind++];
864    }
865
866    if (optind == argc) {
867	close (priv_socket1);
868	close (priv_socket2);
869	argv[0] = "rlogin";
870	execvp ("rlogin", argv);
871	err (1, "execvp rlogin");
872    }
873
874    if (port_str) {
875	struct servent *s = roken_getservbyname (port_str, "tcp");
876
877	if (s)
878	    port = s->s_port;
879	else {
880	    char *ptr;
881
882	    port = strtol (port_str, &ptr, 10);
883	    if (port == 0 && ptr == port_str)
884		errx (1, "Bad port `%s'", port_str);
885	    port = htons(port);
886	}
887    }
888
889    local_user = get_default_username ();
890    if (local_user == NULL)
891	errx (1, "who are you?");
892
893    if (user == NULL)
894	user = local_user;
895
896    cmd_len = construct_command(&cmd, argc - optind, argv + optind);
897
898    /*
899     * Try all different authentication methods
900     */
901
902    if (ret && use_v5) {
903	int tmp_port;
904
905	if (port)
906	    tmp_port = port;
907	else
908	    tmp_port = krb5_getportbyname (context, "kshell", "tcp", 544);
909
910	auth_method = AUTH_KRB5;
911	ret = doit (host, user, local_user, tmp_port, cmd, cmd_len,
912		    do_errsock,
913		    send_krb5_auth);
914    }
915#ifdef KRB4
916    if (ret && use_v4) {
917	int tmp_port;
918
919	if (port)
920	    tmp_port = port;
921	else if (do_encrypt)
922	    tmp_port = krb5_getportbyname (context, "ekshell", "tcp", 545);
923	else
924	    tmp_port = krb5_getportbyname (context, "kshell", "tcp", 544);
925
926	auth_method = AUTH_KRB4;
927	ret = doit (host, user, local_user, tmp_port, cmd, cmd_len,
928		    do_errsock,
929		    send_krb4_auth);
930    }
931#endif
932    if (ret && use_broken) {
933	int tmp_port;
934
935	if(port)
936	    tmp_port = port;
937	else
938	    tmp_port = krb5_getportbyname(context, "shell", "tcp", 514);
939	auth_method = AUTH_BROKEN;
940	ret = doit_broken (argc, argv, host_index, host,
941			   user, local_user,
942			   tmp_port,
943			   priv_socket1,
944			   do_errsock ? priv_socket2 : -1,
945			   cmd, cmd_len);
946    }
947    return ret;
948}
949