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