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