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