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