2 * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
35 RCSID("$Id: rsh.c,v 1.68 2002/09/04 21:40:04 joda Exp $");
37 enum auth_method auth_method;
38 #if defined(KRB4) || defined(KRB5)
42 int do_unique_tkfile = 0;
43 char *unique_tkfile = NULL;
44 char tkfile[MAXPATHLEN];
46 int do_forwardable = -1;
48 krb5_keyblock *keyblock;
52 des_key_schedule schedule;
58 static int use_v4 = -1;
61 static int use_v5 = -1;
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;
69 static int do_errsock = 1;
70 static char *protocol_version_str;
71 static int protocol_version = 2;
77 static int input = 1; /* Read from stdin */
80 loop (int s, int errsock)
86 if(auth_method == AUTH_KRB5 && protocol_version == 2)
90 if (s >= FD_SETSIZE || errsock >= FD_SETSIZE)
91 errx (1, "fd too large");
93 FD_ZERO(&real_readset);
94 FD_SET(s, &real_readset);
96 FD_SET(errsock, &real_readset);
100 FD_SET(STDIN_FILENO, &real_readset);
105 char buf[RSH_BUFSIZ];
107 readset = real_readset;
108 ret = select (max(s, errsock) + 1, &readset, NULL, NULL, NULL);
115 if (FD_ISSET(s, &readset)) {
116 ret = do_read (s, buf, sizeof(buf), ivec_in[0]);
121 FD_CLR(s, &real_readset);
125 net_write (STDOUT_FILENO, buf, ret);
127 if (errsock != -1 && FD_ISSET(errsock, &readset)) {
128 ret = do_read (errsock, buf, sizeof(buf), ivec_in[1]);
133 FD_CLR(errsock, &real_readset);
137 net_write (STDERR_FILENO, buf, ret);
139 if (FD_ISSET(STDIN_FILENO, &readset)) {
140 ret = read (STDIN_FILENO, buf, sizeof(buf));
144 close (STDIN_FILENO);
145 FD_CLR(STDIN_FILENO, &real_readset);
146 shutdown (s, SHUT_WR);
148 do_write (s, buf, ret, ivec_out[0]);
155 send_krb4_auth(int s,
156 struct sockaddr *thisaddr,
157 struct sockaddr *thataddr,
158 const char *hostname,
159 const char *remote_user,
160 const char *local_user,
170 status = krb_sendauth (do_encrypt ? KOPT_DO_MUTUAL : 0,
172 (char *)hostname, krb_realmofhost (hostname),
173 getpid(), &msg, &cred, schedule,
174 (struct sockaddr_in *)thisaddr,
175 (struct sockaddr_in *)thataddr,
177 if (status != KSUCCESS) {
178 warnx("%s: %s", hostname, krb_get_err_text(status));
181 memcpy (iv, cred.session, sizeof(iv));
183 len = strlen(remote_user) + 1;
184 if (net_write (s, remote_user, len) != len) {
188 if (net_write (s, cmd, cmd_len) != cmd_len) {
198 * Send forward information on `s' for host `hostname', them being
199 * forwardable themselves if `forwardable'
203 krb5_forward_cred (krb5_auth_context auth_context,
205 const char *hostname,
211 krb5_kdc_flags flags;
213 krb5_principal principal;
215 memset (&creds, 0, sizeof(creds));
217 ret = krb5_cc_default (context, &ccache);
219 warnx ("could not forward creds: krb5_cc_default: %s",
220 krb5_get_err_text (context, ret));
224 ret = krb5_cc_get_principal (context, ccache, &principal);
226 warnx ("could not forward creds: krb5_cc_get_principal: %s",
227 krb5_get_err_text (context, ret));
231 creds.client = principal;
233 ret = krb5_build_principal (context,
235 strlen(principal->realm),
242 warnx ("could not forward creds: krb5_build_principal: %s",
243 krb5_get_err_text (context, ret));
247 creds.times.endtime = 0;
250 flags.b.forwarded = 1;
251 flags.b.forwardable = forwardable;
253 ret = krb5_get_forwarded_creds (context,
261 warnx ("could not forward creds: krb5_get_forwarded_creds: %s",
262 krb5_get_err_text (context, ret));
266 ret = krb5_write_message (context,
269 krb5_data_free (&out_data);
272 warnx ("could not forward creds: krb5_write_message: %s",
273 krb5_get_err_text (context, ret));
277 static int sendauth_version_error;
280 send_krb5_auth(int s,
281 struct sockaddr *thisaddr,
282 struct sockaddr *thataddr,
283 const char *hostname,
284 const char *remote_user,
285 const char *local_user,
289 krb5_principal server;
290 krb5_data cksum_data;
293 krb5_auth_context auth_context = NULL;
294 const char *protocol_string = NULL;
297 status = krb5_sname_to_principal(context,
303 warnx ("%s: %s", hostname, krb5_get_err_text(context, status));
307 cksum_data.length = asprintf ((char **)&cksum_data.data,
309 ntohs(socket_get_port(thataddr)),
310 do_encrypt ? "-x " : "",
317 ap_opts |= AP_OPTS_MUTUAL_REQUIRED;
319 switch(protocol_version) {
321 ap_opts |= AP_OPTS_USE_SUBKEY;
322 protocol_string = KCMD_NEW_VERSION;
325 protocol_string = KCMD_OLD_VERSION;
326 key_usage = KRB5_KU_OTHER_ENCRYPTED;
332 status = krb5_sendauth (context,
346 krb5_free_principal(context, server);
347 krb5_data_free(&cksum_data);
350 if(status == KRB5_SENDAUTH_REJECTED &&
351 protocol_version == 2 && protocol_version_str == NULL)
352 sendauth_version_error = 1;
354 krb5_warn(context, status, "%s", hostname);
358 status = krb5_auth_con_getlocalsubkey (context, auth_context, &keyblock);
360 status = krb5_auth_con_getkey (context, auth_context, &keyblock);
362 warnx ("krb5_auth_con_getkey: %s", krb5_get_err_text(context, status));
366 status = krb5_auth_con_setaddrs_from_fd (context,
370 warnx("krb5_auth_con_setaddrs_from_fd: %s",
371 krb5_get_err_text(context, status));
375 status = krb5_crypto_init(context, keyblock, 0, &crypto);
377 warnx ("krb5_crypto_init: %s", krb5_get_err_text(context, status));
381 len = strlen(remote_user) + 1;
382 if (net_write (s, remote_user, len) != len) {
386 if (do_encrypt && net_write (s, "-x ", 3) != 3) {
390 if (net_write (s, cmd, cmd_len) != cmd_len) {
395 if (do_unique_tkfile) {
396 if (net_write (s, tkfile, strlen(tkfile)) != strlen(tkfile)) {
401 len = strlen(local_user) + 1;
402 if (net_write (s, local_user, len) != len) {
408 || krb5_forward_cred (auth_context, s, hostname, do_forwardable)) {
409 /* Empty forwarding info */
411 u_char zero[4] = {0, 0, 0, 0};
414 krb5_auth_con_free (context, auth_context);
421 send_broken_auth(int s,
422 struct sockaddr *thisaddr,
423 struct sockaddr *thataddr,
424 const char *hostname,
425 const char *remote_user,
426 const char *local_user,
432 len = strlen(local_user) + 1;
433 if (net_write (s, local_user, len) != len) {
437 len = strlen(remote_user) + 1;
438 if (net_write (s, remote_user, len) != len) {
442 if (net_write (s, cmd, cmd_len) != cmd_len) {
450 proto (int s, int errsock,
451 const char *hostname, const char *local_user, const char *remote_user,
452 const char *cmd, size_t cmd_len,
453 int (*auth_func)(int s,
454 struct sockaddr *this, struct sockaddr *that,
455 const char *hostname, const char *remote_user,
456 const char *local_user, size_t cmd_len,
464 struct sockaddr_storage thisaddr_ss;
465 struct sockaddr *thisaddr = (struct sockaddr *)&thisaddr_ss;
466 struct sockaddr_storage thataddr_ss;
467 struct sockaddr *thataddr = (struct sockaddr *)&thataddr_ss;
468 struct sockaddr_storage erraddr_ss;
469 struct sockaddr *erraddr = (struct sockaddr *)&erraddr_ss;
473 addrlen = sizeof(thisaddr_ss);
474 if (getsockname (s, thisaddr, &addrlen) < 0) {
475 warn ("getsockname(%s)", hostname);
478 addrlen = sizeof(thataddr_ss);
479 if (getpeername (s, thataddr, &addrlen) < 0) {
480 warn ("getpeername(%s)", hostname);
486 addrlen = sizeof(erraddr_ss);
487 if (getsockname (errsock, erraddr, &addrlen) < 0) {
488 warn ("getsockname");
492 if (listen (errsock, 1) < 0) {
498 snprintf (p, sizeof(buf), "%u",
499 ntohs(socket_get_port(erraddr)));
500 len = strlen(buf) + 1;
501 if(net_write (s, buf, len) != len) {
511 if (errsock >= FD_SETSIZE || s >= FD_SETSIZE)
512 errx (1, "fd too large");
515 FD_SET(errsock, &fdset);
518 ret = select (max(errsock, s) + 1, &fdset, NULL, NULL, NULL);
526 if (FD_ISSET(errsock, &fdset)) {
527 errsock2 = accept (errsock, NULL, NULL);
537 * there should not arrive any data on this fd so if it's
538 * readable it probably indicates that the other side when
542 if (FD_ISSET(s, &fdset)) {
543 warnx ("socket closed");
550 if (net_write (s, "0", 2) != 2) {
557 if ((*auth_func)(s, thisaddr, thataddr, hostname,
558 remote_user, local_user,
564 ret = net_read (s, &reply, 1);
569 } else if (ret == 0) {
570 warnx ("unexpected EOF from %s", hostname);
576 warnx ("Error from rshd at %s:", hostname);
578 while ((ret = read (s, buf, sizeof(buf))) > 0)
579 write (STDOUT_FILENO, buf, ret);
580 write (STDOUT_FILENO,"\n",1);
587 if (setsockopt(s, SOL_SOCKET, SO_DEBUG, (void *)&one, sizeof(one)) < 0)
588 warn("setsockopt remote");
589 if (errsock2 != -1 &&
590 setsockopt(errsock2, SOL_SOCKET, SO_DEBUG,
591 (void *)&one, sizeof(one)) < 0)
592 warn("setsockopt stderr");
595 return loop (s, errsock2);
599 * Return in `res' a copy of the concatenation of `argc, argv' into
603 construct_command (char **res, int argc, char **argv)
609 for (i = 0; i < argc; ++i)
610 len += strlen(argv[i]) + 1;
614 errx (1, "malloc %u failed", len);
617 for (i = 0; i < argc - 1; ++i) {
618 strcat (tmp, argv[i]);
622 strcat (tmp, argv[argc-1]);
628 print_addr (const struct sockaddr_in *sin)
633 inet_ntop (AF_INET, &sin->sin_addr, addr_str, sizeof(addr_str));
634 res = strdup(addr_str);
636 errx (1, "malloc: out of memory");
641 doit_broken (int argc,
645 const char *remote_user,
646 const char *local_user,
654 if (connect (priv_socket1, ai->ai_addr, ai->ai_addrlen) < 0) {
655 if (ai->ai_next == NULL)
661 for (a = ai->ai_next; a != NULL; a = a->ai_next) {
670 struct sockaddr_in *sin = (struct sockaddr_in *)a->ai_addr;
672 new_argv = malloc((argc + 2) * sizeof(*new_argv));
673 if (new_argv == NULL)
674 errx (1, "malloc: out of memory");
675 new_argv[i] = argv[i];
678 new_argv[i++] = print_addr (sin);
679 new_argv[i++] = "-K";
680 for(; i <= argc; ++i)
681 new_argv[i] = argv[i - 1];
683 new_argv[optind + 1] = print_addr(sin);
684 new_argv[argc + 1] = NULL;
685 execv(PATH_RSH, new_argv);
686 err(1, "execv(%s)", PATH_RSH);
690 while(waitpid(pid, &status, 0) < 0)
692 if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
700 ret = proto (priv_socket1, priv_socket2,
702 local_user, remote_user,
709 #if defined(KRB4) || defined(KRB5)
711 doit (const char *hostname,
713 const char *remote_user,
714 const char *local_user,
718 int (*auth_func)(int s,
719 struct sockaddr *this, struct sockaddr *that,
720 const char *hostname, const char *remote_user,
721 const char *local_user, size_t cmd_len,
726 int socketfailed = 1;
729 for (a = ai; a != NULL; a = a->ai_next) {
733 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
737 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
739 if(getnameinfo(a->ai_addr, a->ai_addrlen,
740 addr, sizeof(addr), NULL, 0, NI_NUMERICHOST) == 0)
741 warn ("connect(%s [%s])", hostname, addr);
743 warn ("connect(%s)", hostname);
748 struct addrinfo *ea, *eai;
749 struct addrinfo hints;
751 memset (&hints, 0, sizeof(hints));
752 hints.ai_socktype = a->ai_socktype;
753 hints.ai_protocol = a->ai_protocol;
754 hints.ai_family = a->ai_family;
755 hints.ai_flags = AI_PASSIVE;
759 error = getaddrinfo (NULL, "0", &hints, &eai);
761 errx (1, "getaddrinfo: %s", gai_strerror(error));
762 for (ea = eai; ea != NULL; ea = ea->ai_next) {
763 errsock = socket (ea->ai_family, ea->ai_socktype,
767 if (bind (errsock, ea->ai_addr, ea->ai_addrlen) < 0)
777 ret = proto (s, errsock,
779 local_user, remote_user,
780 cmd, cmd_len, auth_func);
785 warnx ("failed to contact %s", hostname);
788 #endif /* KRB4 || KRB5 */
790 struct getargs args[] = {
792 { "krb4", '4', arg_flag, &use_v4, "Use Kerberos V4" },
795 { "krb5", '5', arg_flag, &use_v5, "Use Kerberos V5" },
796 { "forward", 'f', arg_flag, &do_forward, "Forward credentials (krb5)"},
797 { NULL, 'G', arg_negative_flag,&do_forward, "Don't forward credentials" },
798 { "forwardable", 'F', arg_flag, &do_forwardable,
799 "Forward forwardable credentials" },
801 #if defined(KRB4) || defined(KRB5)
802 { "broken", 'K', arg_flag, &use_only_broken, "Use only priv port" },
803 { "encrypt", 'x', arg_flag, &do_encrypt, "Encrypt connection" },
804 { NULL, 'z', arg_negative_flag, &do_encrypt,
805 "Don't encrypt connection", NULL },
808 { "unique", 'u', arg_flag, &do_unique_tkfile,
809 "Use unique remote tkfile (krb5)" },
810 { "tkfile", 'U', arg_string, &unique_tkfile,
811 "Use that remote tkfile (krb5)" },
813 { NULL, 'd', arg_flag, &sock_debug, "Enable socket debugging" },
814 { "input", 'n', arg_negative_flag, &input, "Close stdin" },
815 { "port", 'p', arg_string, &port_str, "Use this port",
817 { "user", 'l', arg_string, &user, "Run as this user", "login" },
818 { "stderr", 'e', arg_negative_flag, &do_errsock, "Don't open stderr"},
819 { "protocol", 'P', arg_string, &protocol_version_str,
820 "Protocol version", "protocol" },
821 { "version", 0, arg_flag, &do_version, NULL },
822 { "help", 0, arg_flag, &do_help, NULL }
828 arg_printusage (args,
829 sizeof(args) / sizeof(args[0]),
831 "[login@]host [command]");
840 main(int argc, char **argv)
842 int priv_port1, priv_port2;
843 int priv_socket1, priv_socket2;
846 struct addrinfo hints, *ai;
851 const char *local_user;
859 priv_port1 = priv_port2 = IPPORT_RESERVED-1;
860 priv_socket1 = rresvport(&priv_port1);
861 priv_socket2 = rresvport(&priv_port2);
863 if (setuid (uid) || (uid != 0 && setuid(0) == 0))
866 setprogname (argv[0]);
868 if (argc >= 2 && argv[1][0] != '-') {
869 host = argv[host_index = 1];
873 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
881 print_version (NULL);
885 if(protocol_version_str != NULL) {
886 if(strcasecmp(protocol_version_str, "N") == 0)
887 protocol_version = 2;
888 else if(strcasecmp(protocol_version_str, "O") == 0)
889 protocol_version = 1;
893 v = strtol(protocol_version_str, &end, 0);
894 if(*end != '\0' || (v != 1 && v != 2)) {
895 errx(1, "unknown protocol version \"%s\"",
896 protocol_version_str);
898 protocol_version = v;
903 status = krb5_init_context (&context);
906 errx(1, "krb5_init_context failed: %d", status);
911 if (do_forwardable == -1)
912 do_forwardable = krb5_config_get_bool (context, NULL,
917 if (do_forward == -1)
918 do_forward = krb5_config_get_bool (context, NULL,
922 else if (do_forward == 0)
928 #if defined(KRB4) || defined(KRB5)
929 if (do_encrypt == -1) {
930 /* we want to tell the -x flag from the default encryption
933 /* the normal default for krb4 should be to disable encryption */
934 if(!krb5_config_get_bool (context, NULL,
943 #if defined(KRB4) && defined(KRB5)
944 if(use_v4 == -1 && use_v5 == 1)
946 if(use_v5 == -1 && use_v4 == 1)
950 if (use_only_broken) {
959 if(priv_socket1 < 0) {
961 errx (1, "unable to bind reserved port: is rsh setuid root?");
965 #if defined(KRB4) || defined(KRB5)
966 if (do_encrypt == 1 && use_only_broken)
967 errx (1, "encryption not supported with old style authentication");
973 if (do_unique_tkfile && unique_tkfile != NULL)
974 errx (1, "Only one of -u and -U allowed.");
976 if (do_unique_tkfile)
977 strcpy(tkfile,"-u ");
978 else if (unique_tkfile != NULL) {
979 if (strchr(unique_tkfile,' ') != NULL) {
980 warnx("Space is not allowed in tkfilename");
983 do_unique_tkfile = 1;
984 snprintf (tkfile, sizeof(tkfile), "-U %s ", unique_tkfile);
989 if (argc - optind < 1)
992 host = argv[host_index = optind++];
995 if((tmp = strchr(host, '@')) != NULL) {
1001 if (optind == argc) {
1002 close (priv_socket1);
1003 close (priv_socket2);
1005 execvp ("rlogin", argv);
1006 err (1, "execvp rlogin");
1009 local_user = get_default_username ();
1010 if (local_user == NULL)
1011 errx (1, "who are you?");
1016 cmd_len = construct_command(&cmd, argc - optind, argv + optind);
1019 * Try all different authentication methods
1023 if (ret && use_v5) {
1024 memset (&hints, 0, sizeof(hints));
1025 hints.ai_socktype = SOCK_STREAM;
1026 hints.ai_protocol = IPPROTO_TCP;
1028 if(port_str == NULL) {
1029 error = getaddrinfo(host, "kshell", &hints, &ai);
1030 if(error == EAI_NONAME)
1031 error = getaddrinfo(host, "544", &hints, &ai);
1033 error = getaddrinfo(host, port_str, &hints, &ai);
1036 errx (1, "getaddrinfo: %s", gai_strerror(error));
1038 auth_method = AUTH_KRB5;
1040 ret = doit (host, ai, user, local_user, cmd, cmd_len,
1043 if(ret != 0 && sendauth_version_error &&
1044 protocol_version == 2) {
1045 protocol_version = 1;
1052 if (ret && use_v4) {
1053 memset (&hints, 0, sizeof(hints));
1054 hints.ai_socktype = SOCK_STREAM;
1055 hints.ai_protocol = IPPROTO_TCP;
1057 if(port_str == NULL) {
1059 error = getaddrinfo(host, "ekshell", &hints, &ai);
1060 if(error == EAI_NONAME)
1061 error = getaddrinfo(host, "545", &hints, &ai);
1063 error = getaddrinfo(host, "kshell", &hints, &ai);
1064 if(error == EAI_NONAME)
1065 error = getaddrinfo(host, "544", &hints, &ai);
1068 error = getaddrinfo(host, port_str, &hints, &ai);
1071 errx (1, "getaddrinfo: %s", gai_strerror(error));
1072 auth_method = AUTH_KRB4;
1073 ret = doit (host, ai, user, local_user, cmd, cmd_len,
1079 if (ret && use_broken) {
1080 memset (&hints, 0, sizeof(hints));
1081 hints.ai_socktype = SOCK_STREAM;
1082 hints.ai_protocol = IPPROTO_TCP;
1084 if(port_str == NULL) {
1085 error = getaddrinfo(host, "shell", &hints, &ai);
1086 if(error == EAI_NONAME)
1087 error = getaddrinfo(host, "514", &hints, &ai);
1089 error = getaddrinfo(host, port_str, &hints, &ai);
1092 errx (1, "getaddrinfo: %s", gai_strerror(error));
1094 auth_method = AUTH_BROKEN;
1095 ret = doit_broken (argc, argv, host_index, ai,
1098 do_errsock ? priv_socket2 : -1,