More documentation.
[heimdal.git] / appl / rsh / rsh.c
blobd4a15abd35dcb8c1f2523b846e9d932cfddfc0b1
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("$Id$");
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;
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
83 static int input = 1; /* Read from stdin */
85 static int
86 rsh_loop (int s, int errsock)
88 fd_set real_readset;
89 int count = 1;
91 #ifdef KRB5
92 if(auth_method == AUTH_KRB5 && protocol_version == 2)
93 init_ivecs(1, errsock != -1);
94 #endif
96 if (s >= FD_SETSIZE || (errsock != -1 && errsock >= FD_SETSIZE))
97 errx (1, "fd too large");
99 FD_ZERO(&real_readset);
100 FD_SET(s, &real_readset);
101 if (errsock != -1) {
102 FD_SET(errsock, &real_readset);
103 ++count;
105 if(input)
106 FD_SET(STDIN_FILENO, &real_readset);
108 for (;;) {
109 int ret;
110 fd_set readset;
111 char buf[RSH_BUFSIZ];
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");
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);
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);
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]);
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)
170 KTEXT_ST text;
171 CREDENTIALS cred;
172 MSG_DAT msg;
173 int status;
174 size_t len;
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;
188 memcpy (iv, cred.session, sizeof(iv));
190 len = strlen(remote_user) + 1;
191 if (net_write (s, remote_user, len) != len) {
192 warn("write");
193 return 1;
195 if (net_write (s, cmd, cmd_len) != cmd_len) {
196 warn("write");
197 return 1;
199 return 0;
201 #endif /* KRB4 */
203 #ifdef KRB5
205 * Send forward information on `s' for host `hostname', them being
206 * forwardable themselves if `forwardable'
209 static int
210 krb5_forward_cred (krb5_auth_context auth_context,
211 int s,
212 const char *hostname,
213 int forwardable)
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;
222 memset (&creds, 0, sizeof(creds));
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;
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;
238 creds.client = principal;
240 ret = krb5_build_principal (context,
241 &creds.server,
242 strlen(principal->realm),
243 principal->realm,
244 "krbtgt",
245 principal->realm,
246 NULL);
248 if (ret) {
249 warnx ("could not forward creds: krb5_build_principal: %s",
250 krb5_get_err_text (context, ret));
251 return 1;
254 creds.times.endtime = 0;
256 flags.i = 0;
257 flags.b.forwarded = 1;
258 flags.b.forwardable = forwardable;
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;
273 ret = krb5_write_message (context,
274 (void *)&s,
275 &out_data);
276 krb5_data_free (&out_data);
278 if (ret)
279 warnx ("could not forward creds: krb5_write_message: %s",
280 krb5_get_err_text (context, ret));
281 return 0;
284 static int sendauth_version_error;
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)
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;
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;
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);
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;
333 cksum_data.data = str;
335 ap_opts = 0;
337 if(do_encrypt)
338 ap_opts |= AP_OPTS_MUTUAL_REQUIRED;
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();
353 status = krb5_sendauth (context,
354 &auth_context,
356 protocol_string,
357 NULL,
358 server,
359 ap_opts,
360 &cksum_data,
361 NULL,
362 NULL,
363 NULL,
364 NULL,
365 NULL);
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);
380 krb5_free_principal(context, server);
381 krb5_data_free(&cksum_data);
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;
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;
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);
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;
415 len = strlen(remote_user) + 1;
416 if (net_write (s, remote_user, len) != len) {
417 warn ("write");
418 return 1;
420 if (do_encrypt && net_write (s, "-x ", 3) != 3) {
421 warn ("write");
422 return 1;
424 if (net_write (s, cmd, cmd_len) != cmd_len) {
425 warn ("write");
426 return 1;
429 if (do_unique_tkfile) {
430 if (net_write (s, tkfile, strlen(tkfile)) != strlen(tkfile)) {
431 warn ("write");
432 return 1;
435 len = strlen(local_user) + 1;
436 if (net_write (s, local_user, len) != len) {
437 warn ("write");
438 return 1;
441 if (!do_forward
442 || krb5_forward_cred (auth_context, s, hostname, do_forwardable)) {
443 /* Empty forwarding info */
445 u_char zero[4] = {0, 0, 0, 0};
446 write (s, &zero, 4);
448 krb5_auth_con_free (context, auth_context);
449 return 0;
452 #endif /* KRB5 */
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)
464 size_t len;
466 len = strlen(local_user) + 1;
467 if (net_write (s, local_user, len) != len) {
468 warn ("write");
469 return 1;
471 len = strlen(remote_user) + 1;
472 if (net_write (s, remote_user, len) != len) {
473 warn ("write");
474 return 1;
476 if (net_write (s, cmd, cmd_len) != cmd_len) {
477 warn ("write");
478 return 1;
480 return 0;
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))
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;
507 addrlen = sizeof(thisaddr_ss);
508 if (getsockname (s, thisaddr, &addrlen) < 0) {
509 warn ("getsockname(%s)", hostname);
510 return 1;
512 addrlen = sizeof(thataddr_ss);
513 if (getpeername (s, thataddr, &addrlen) < 0) {
514 warn ("getpeername(%s)", hostname);
515 return 1;
518 if (errsock != -1) {
520 addrlen = sizeof(erraddr_ss);
521 if (getsockname (errsock, erraddr, &addrlen) < 0) {
522 warn ("getsockname");
523 return 1;
526 if (listen (errsock, 1) < 0) {
527 warn ("listen");
528 return 1;
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;
542 for (;;) {
543 fd_set fdset;
545 if (errsock >= FD_SETSIZE || s >= FD_SETSIZE)
546 errx (1, "fd too large");
548 FD_ZERO(&fdset);
549 FD_SET(errsock, &fdset);
550 FD_SET(s, &fdset);
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;
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;
567 break;
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.
576 if (FD_ISSET(s, &fdset)) {
577 warnx ("socket closed");
578 close (errsock);
579 errsock2 = -1;
580 break;
583 } else {
584 if (net_write (s, "0", 2) != 2) {
585 warn ("write");
586 return 1;
588 errsock2 = -1;
591 if ((*auth_func)(s, thisaddr, thataddr, hostname,
592 remote_user, local_user,
593 cmd_len, cmd)) {
594 close (errsock2);
595 return 1;
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;
608 if (reply != 0) {
610 warnx ("Error from rshd at %s:", hostname);
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;
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");
629 return rsh_loop (s, errsock2);
633 * Return in `res' a copy of the concatenation of `argc, argv' into
634 * malloced space. */
636 static size_t
637 construct_command (char **res, int argc, char **argv)
639 int i;
640 size_t len = 0;
641 char *tmp;
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);
650 *tmp = '\0';
651 for (i = 0; i < argc - 1; ++i) {
652 strlcat (tmp, argv[i], len);
653 strlcat (tmp, " ", len);
655 if (argc > 0)
656 strlcat (tmp, argv[argc-1], len);
657 *res = tmp;
658 return len;
661 static char *
662 print_addr (const struct sockaddr *sa)
664 char addr_str[256];
665 char *res;
666 const char *as = NULL;
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;
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)
696 struct addrinfo *a;
698 if (connect (priv_socket1, ai->ai_addr, ai->ai_addrlen) < 0) {
699 int save_errno = errno;
701 close(priv_socket1);
702 close(priv_socket2);
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;
710 pid = fork();
711 if (pid < 0)
712 err (1, "fork");
713 else if(pid == 0) {
714 char **new_argv;
715 int i = 0;
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);
736 while(waitpid(pid, &status, 0) < 0)
738 if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
739 return 0;
742 errno = save_errno;
743 warn("%s", argv[hostindex]);
744 return 1;
745 } else {
746 int ret;
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;
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))
771 int error;
772 struct addrinfo *a;
773 int socketfailed = 1;
774 int ret;
776 for (a = ai; a != NULL; a = a->ai_next) {
777 int s;
778 int errsock;
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;
794 if (do_errsock) {
795 struct addrinfo *ea, *eai;
796 struct addrinfo hints;
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;
804 errsock = -1;
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;
818 if (errsock < 0)
819 err (1, "socket");
820 freeaddrinfo (eai);
821 } else
822 errsock = -1;
824 ret = proto (s, errsock,
825 hostname,
826 local_user, remote_user,
827 cmd, cmd_len, auth_func);
828 close (s);
829 return ret;
831 if(socketfailed)
832 warnx ("failed to contact %s", hostname);
833 return -1;
835 #endif /* KRB4 || KRB5 */
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 }
872 static void
873 usage (int ret)
875 arg_printusage (args,
876 sizeof(args) / sizeof(args[0]),
877 NULL,
878 "[login@]host [command]");
879 exit (ret);
887 main(int argc, char **argv)
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;
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");
913 setprogname (argv[0]);
915 if (argc >= 2 && argv[1][0] != '-') {
916 host = argv[host_index = 1];
917 argindex = 1;
920 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
921 &argindex))
922 usage (1);
924 if (do_help)
925 usage (0);
927 if (do_version) {
928 print_version (NULL);
929 return 0;
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);
946 protocol_version = v;
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;
958 /* request for forwardable on the command line means we should
959 also forward */
960 if (do_forwardable == 1)
961 do_forward = 1;
963 #endif
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
972 if (use_only_broken) {
973 #ifdef KRB4
974 use_v4 = 0;
975 #endif
976 #ifdef KRB5
977 use_v5 = 0;
978 #endif
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;
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
994 #ifdef KRB5
995 if (do_unique_tkfile && unique_tkfile != NULL)
996 errx (1, "Only one of -u and -U allowed.");
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);
1005 do_unique_tkfile = 1;
1006 snprintf (tkfile, sizeof(tkfile), "-U %s ", unique_tkfile);
1008 #endif
1010 if (host == NULL) {
1011 if (argc - argindex < 1)
1012 usage (1);
1013 else
1014 host = argv[host_index = argindex++];
1017 if((tmp = strchr(host, '@')) != NULL) {
1018 *tmp++ = '\0';
1019 user = host;
1020 host = tmp;
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");
1031 local_user = get_default_username ();
1032 if (local_user == NULL)
1033 errx (1, "who are you?");
1035 if (user == NULL)
1036 user = local_user;
1038 cmd_len = construct_command(&cmd, argc - argindex, argv + argindex);
1041 * Try all different authentication methods
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;
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);
1057 if(error)
1058 errx (1, "getaddrinfo: %s", gai_strerror(error));
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;
1069 freeaddrinfo(ai);
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;
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);
1088 } else
1089 error = getaddrinfo(host, port_str, &hints, &ai);
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);
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;
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);
1111 if(error)
1112 errx (1, "getaddrinfo: %s", gai_strerror(error));
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);
1122 free(cmd);
1123 return ret;