2 * Copyright (c) 1997 - 2004 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
37 enum auth_method auth_method;
42 int do_unique_tkfile = 0;
43 char *unique_tkfile = NULL;
44 char tkfile[MAXPATHLEN];
46 int do_forwardable = -1;
48 krb5_keyblock *keyblock;
54 static int use_v5 = -1;
57 static int use_only_broken = 0;
59 static int use_only_broken = 1;
61 static int use_broken = 1;
62 static char *port_str;
63 static const char *user;
64 static int do_version;
66 static int do_errsock = 1;
68 static char *protocol_version_str;
69 static int protocol_version = 2;
76 static int input = 1; /* Read from stdin */
79 rsh_loop (int s, int errsock)
85 if(auth_method == AUTH_KRB5 && protocol_version == 2)
86 init_ivecs(1, errsock != -1);
89 if (s >= FD_SETSIZE || (errsock != -1 && errsock >= FD_SETSIZE))
90 errx (1, "fd too large");
92 FD_ZERO(&real_readset);
93 FD_SET(s, &real_readset);
95 FD_SET(errsock, &real_readset);
99 FD_SET(STDIN_FILENO, &real_readset);
104 char buf[RSH_BUFSIZ];
106 readset = real_readset;
107 ret = select (max(s, errsock) + 1, &readset, NULL, NULL, NULL);
114 if (FD_ISSET(s, &readset)) {
115 ret = do_read (s, buf, sizeof(buf), ivec_in[0]);
120 FD_CLR(s, &real_readset);
124 net_write (STDOUT_FILENO, buf, ret);
126 if (errsock != -1 && FD_ISSET(errsock, &readset)) {
127 ret = do_read (errsock, buf, sizeof(buf), ivec_in[1]);
132 FD_CLR(errsock, &real_readset);
136 net_write (STDERR_FILENO, buf, ret);
138 if (FD_ISSET(STDIN_FILENO, &readset)) {
139 ret = read (STDIN_FILENO, buf, sizeof(buf));
143 close (STDIN_FILENO);
144 FD_CLR(STDIN_FILENO, &real_readset);
145 shutdown (s, SHUT_WR);
147 do_write (s, buf, ret, ivec_out[0]);
154 * Send forward information on `s' for host `hostname', them being
155 * forwardable themselves if `forwardable'
159 krb5_forward_cred (krb5_auth_context auth_context,
161 const char *hostname,
167 krb5_kdc_flags flags;
169 krb5_principal principal;
171 memset (&creds, 0, sizeof(creds));
173 ret = krb5_cc_default (context, &ccache);
175 warnx ("could not forward creds: krb5_cc_default: %s",
176 krb5_get_err_text (context, ret));
180 ret = krb5_cc_get_principal (context, ccache, &principal);
182 warnx ("could not forward creds: krb5_cc_get_principal: %s",
183 krb5_get_err_text (context, ret));
187 creds.client = principal;
189 ret = krb5_make_principal(context,
197 warnx ("could not forward creds: krb5_make_principal: %s",
198 krb5_get_err_text (context, ret));
202 creds.times.endtime = 0;
205 flags.b.forwarded = 1;
206 flags.b.forwardable = forwardable;
208 ret = krb5_get_forwarded_creds (context,
216 warnx ("could not forward creds: krb5_get_forwarded_creds: %s",
217 krb5_get_err_text (context, ret));
221 ret = krb5_write_message (context,
224 krb5_data_free (&out_data);
227 warnx ("could not forward creds: krb5_write_message: %s",
228 krb5_get_err_text (context, ret));
232 static int sendauth_version_error;
235 send_krb5_auth(int s,
236 struct sockaddr *thisaddr,
237 struct sockaddr *thataddr,
238 const char *hostname,
239 const char *remote_user,
240 const char *local_user,
244 krb5_principal server;
245 krb5_data cksum_data;
248 krb5_auth_context auth_context = NULL;
249 const char *protocol_string = NULL;
253 status = krb5_sname_to_principal(context,
259 warnx ("%s: %s", hostname, krb5_get_err_text(context, status));
263 if(do_encrypt == -1) {
264 krb5_appdefault_boolean(context, NULL,
265 krb5_principal_get_realm(context, server),
271 cksum_data.length = asprintf (&str,
273 ntohs(socket_get_port(thataddr)),
274 do_encrypt ? "-x " : "",
278 warnx ("%s: failed to allocate command", hostname);
281 cksum_data.data = str;
286 ap_opts |= AP_OPTS_MUTUAL_REQUIRED;
288 switch(protocol_version) {
290 ap_opts |= AP_OPTS_USE_SUBKEY;
291 protocol_string = KCMD_NEW_VERSION;
294 protocol_string = KCMD_OLD_VERSION;
295 key_usage = KRB5_KU_OTHER_ENCRYPTED;
301 status = krb5_sendauth (context,
315 /* do this while we have a principal */
316 if(do_forward == -1 || do_forwardable == -1) {
317 krb5_const_realm realm = krb5_principal_get_realm(context, server);
318 if (do_forwardable == -1)
319 krb5_appdefault_boolean(context, NULL, realm,
320 "forwardable", FALSE,
322 if (do_forward == -1)
323 krb5_appdefault_boolean(context, NULL, realm,
328 krb5_free_principal(context, server);
329 krb5_data_free(&cksum_data);
332 if(status == KRB5_SENDAUTH_REJECTED &&
333 protocol_version == 2 && protocol_version_str == NULL)
334 sendauth_version_error = 1;
336 krb5_warn(context, status, "%s", hostname);
340 status = krb5_auth_con_getlocalsubkey (context, auth_context, &keyblock);
342 status = krb5_auth_con_getkey (context, auth_context, &keyblock);
344 warnx ("krb5_auth_con_getkey: %s", krb5_get_err_text(context, status));
348 status = krb5_auth_con_setaddrs_from_fd (context,
352 warnx("krb5_auth_con_setaddrs_from_fd: %s",
353 krb5_get_err_text(context, status));
357 status = krb5_crypto_init(context, keyblock, 0, &crypto);
359 warnx ("krb5_crypto_init: %s", krb5_get_err_text(context, status));
363 len = strlen(remote_user) + 1;
364 if (net_write (s, remote_user, len) != len) {
368 if (do_encrypt && net_write (s, "-x ", 3) != 3) {
372 if (net_write (s, cmd, cmd_len) != cmd_len) {
377 if (do_unique_tkfile) {
378 if (net_write (s, tkfile, strlen(tkfile)) != strlen(tkfile)) {
383 len = strlen(local_user) + 1;
384 if (net_write (s, local_user, len) != len) {
390 || krb5_forward_cred (auth_context, s, hostname, do_forwardable)) {
391 /* Empty forwarding info */
393 u_char zero[4] = {0, 0, 0, 0};
396 krb5_auth_con_free (context, auth_context);
403 send_broken_auth(int s,
404 struct sockaddr *thisaddr,
405 struct sockaddr *thataddr,
406 const char *hostname,
407 const char *remote_user,
408 const char *local_user,
414 len = strlen(local_user) + 1;
415 if (net_write (s, local_user, len) != len) {
419 len = strlen(remote_user) + 1;
420 if (net_write (s, remote_user, len) != len) {
424 if (net_write (s, cmd, cmd_len) != cmd_len) {
432 proto (int s, int errsock,
433 const char *hostname, const char *local_user, const char *remote_user,
434 const char *cmd, size_t cmd_len,
435 int (*auth_func)(int s,
436 struct sockaddr *this, struct sockaddr *that,
437 const char *hostname, const char *remote_user,
438 const char *local_user, size_t cmd_len,
446 struct sockaddr_storage thisaddr_ss;
447 struct sockaddr *thisaddr = (struct sockaddr *)&thisaddr_ss;
448 struct sockaddr_storage thataddr_ss;
449 struct sockaddr *thataddr = (struct sockaddr *)&thataddr_ss;
450 struct sockaddr_storage erraddr_ss;
451 struct sockaddr *erraddr = (struct sockaddr *)&erraddr_ss;
455 addrlen = sizeof(thisaddr_ss);
456 if (getsockname (s, thisaddr, &addrlen) < 0) {
457 warn ("getsockname(%s)", hostname);
460 addrlen = sizeof(thataddr_ss);
461 if (getpeername (s, thataddr, &addrlen) < 0) {
462 warn ("getpeername(%s)", hostname);
468 addrlen = sizeof(erraddr_ss);
469 if (getsockname (errsock, erraddr, &addrlen) < 0) {
470 warn ("getsockname");
474 if (listen (errsock, 1) < 0) {
480 snprintf (p, sizeof(buf), "%u",
481 ntohs(socket_get_port(erraddr)));
482 len = strlen(buf) + 1;
483 if(net_write (s, buf, len) != len) {
493 if (errsock >= FD_SETSIZE || s >= FD_SETSIZE)
494 errx (1, "fd too large");
497 FD_SET(errsock, &fdset);
500 ret = select (max(errsock, s) + 1, &fdset, NULL, NULL, NULL);
508 if (FD_ISSET(errsock, &fdset)) {
509 errsock2 = accept (errsock, NULL, NULL);
519 * there should not arrive any data on this fd so if it's
520 * readable it probably indicates that the other side when
524 if (FD_ISSET(s, &fdset)) {
525 warnx ("socket closed");
532 if (net_write (s, "0", 2) != 2) {
539 if ((*auth_func)(s, thisaddr, thataddr, hostname,
540 remote_user, local_user,
546 ret = net_read (s, &reply, 1);
551 } else if (ret == 0) {
552 warnx ("unexpected EOF from %s", hostname);
558 warnx ("Error from rshd at %s:", hostname);
560 while ((ret = read (s, buf, sizeof(buf))) > 0)
561 write (STDOUT_FILENO, buf, ret);
562 write (STDOUT_FILENO,"\n",1);
569 if (setsockopt(s, SOL_SOCKET, SO_DEBUG, (void *)&one, sizeof(one)) < 0)
570 warn("setsockopt remote");
571 if (errsock2 != -1 &&
572 setsockopt(errsock2, SOL_SOCKET, SO_DEBUG,
573 (void *)&one, sizeof(one)) < 0)
574 warn("setsockopt stderr");
577 return rsh_loop (s, errsock2);
581 * Return in `res' a copy of the concatenation of `argc, argv' into
585 construct_command (char **res, int argc, char **argv)
591 for (i = 0; i < argc; ++i)
592 len += strlen(argv[i]) + 1;
596 errx (1, "malloc %lu failed", (unsigned long)len);
599 for (i = 0; i < argc - 1; ++i) {
600 strlcat (tmp, argv[i], len);
601 strlcat (tmp, " ", len);
604 strlcat (tmp, argv[argc-1], len);
610 print_addr (const struct sockaddr *sa)
614 const char *as = NULL;
616 if(sa->sa_family == AF_INET)
617 as = inet_ntop (sa->sa_family, &((struct sockaddr_in*)sa)->sin_addr,
618 addr_str, sizeof(addr_str));
620 else if(sa->sa_family == AF_INET6)
621 as = inet_ntop (sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr,
622 addr_str, sizeof(addr_str));
628 errx (1, "malloc: out of memory");
633 doit_broken (int argc,
637 const char *remote_user,
638 const char *local_user,
646 if (connect (priv_socket1, ai->ai_addr, ai->ai_addrlen) < 0) {
647 int save_errno = errno;
652 for (a = ai->ai_next; a != NULL; a = a->ai_next) {
654 char *adr = print_addr(a->ai_addr);
665 new_argv = malloc((argc + 2) * sizeof(*new_argv));
666 if (new_argv == NULL)
667 errx (1, "malloc: out of memory");
668 new_argv[i] = argv[i];
672 new_argv[i++] = "-K";
673 for(; i <= argc; ++i)
674 new_argv[i] = argv[i - 1];
676 new_argv[hostindex + 1] = adr;
677 new_argv[argc + 1] = NULL;
678 execv(PATH_RSH, new_argv);
679 err(1, "execv(%s)", PATH_RSH);
684 while(waitpid(pid, &status, 0) < 0)
686 if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
691 warn("%s", argv[hostindex]);
696 ret = proto (priv_socket1, priv_socket2,
698 local_user, remote_user,
707 doit (const char *hostname,
709 const char *remote_user,
710 const char *local_user,
713 int (*auth_func)(int s,
714 struct sockaddr *this, struct sockaddr *that,
715 const char *hostname, const char *remote_user,
716 const char *local_user, size_t cmd_len,
721 int socketfailed = 1;
724 for (a = ai; a != NULL; a = a->ai_next) {
728 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
732 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
734 if(getnameinfo(a->ai_addr, a->ai_addrlen,
735 addr, sizeof(addr), NULL, 0, NI_NUMERICHOST) == 0)
736 warn ("connect(%s [%s])", hostname, addr);
738 warn ("connect(%s)", hostname);
743 struct addrinfo *ea, *eai;
744 struct addrinfo hints;
746 memset (&hints, 0, sizeof(hints));
747 hints.ai_socktype = a->ai_socktype;
748 hints.ai_protocol = a->ai_protocol;
749 hints.ai_family = a->ai_family;
750 hints.ai_flags = AI_PASSIVE;
754 error = getaddrinfo (NULL, "0", &hints, &eai);
756 errx (1, "getaddrinfo: %s", gai_strerror(error));
757 for (ea = eai; ea != NULL; ea = ea->ai_next) {
758 errsock = socket (ea->ai_family, ea->ai_socktype,
762 if (bind (errsock, ea->ai_addr, ea->ai_addrlen) < 0)
772 ret = proto (s, errsock,
774 local_user, remote_user,
775 cmd, cmd_len, auth_func);
780 warnx ("failed to contact %s", hostname);
785 struct getargs args[] = {
787 { "krb5", '5', arg_flag, &use_v5, "Use Kerberos V5" },
788 { "forward", 'f', arg_flag, &do_forward, "Forward credentials [krb5]"},
789 { "forwardable", 'F', arg_flag, &do_forwardable,
790 "Forward forwardable credentials [krb5]" },
791 { NULL, 'G', arg_negative_flag,&do_forward, "Don't forward credentials" },
792 { "unique", 'u', arg_flag, &do_unique_tkfile,
793 "Use unique remote credentials cache [krb5]" },
794 { "tkfile", 'U', arg_string, &unique_tkfile,
795 "Specifies remote credentials cache [krb5]" },
796 { "protocol", 'P', arg_string, &protocol_version_str,
797 "Protocol version [krb5]", "protocol" },
799 { "broken", 'K', arg_flag, &use_only_broken, "Use only priv port" },
801 { "encrypt", 'x', arg_flag, &do_encrypt, "Encrypt connection" },
802 { NULL, 'z', arg_negative_flag, &do_encrypt,
803 "Don't encrypt connection", NULL },
805 { NULL, 'd', arg_flag, &sock_debug, "Enable socket debugging" },
806 { "input", 'n', arg_negative_flag, &input, "Close stdin" },
807 { "port", 'p', arg_string, &port_str, "Use this port",
809 { "user", 'l', arg_string, &user, "Run as this user", "login" },
810 { "stderr", 'e', arg_negative_flag, &do_errsock, "Don't open stderr"},
813 { "version", 0, arg_flag, &do_version, NULL },
814 { "help", 0, arg_flag, &do_help, NULL }
820 arg_printusage (args,
821 sizeof(args) / sizeof(args[0]),
823 "[login@]host [command]");
832 main(int argc, char **argv)
834 int priv_port1, priv_port2;
835 int priv_socket1, priv_socket2;
838 struct addrinfo hints, *ai;
843 const char *local_user;
851 priv_port1 = priv_port2 = IPPORT_RESERVED-1;
852 priv_socket1 = rresvport(&priv_port1);
853 priv_socket2 = rresvport(&priv_port2);
855 if (setuid (uid) || (uid != 0 && setuid(0) == 0))
858 setprogname (argv[0]);
860 if (argc >= 2 && argv[1][0] != '-') {
861 host = argv[host_index = 1];
865 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
873 print_version (NULL);
878 if(protocol_version_str != NULL) {
879 if(strcasecmp(protocol_version_str, "N") == 0)
880 protocol_version = 2;
881 else if(strcasecmp(protocol_version_str, "O") == 0)
882 protocol_version = 1;
886 v = strtol(protocol_version_str, &end, 0);
887 if(*end != '\0' || (v != 1 && v != 2)) {
888 errx(1, "unknown protocol version \"%s\"",
889 protocol_version_str);
891 protocol_version = v;
895 status = krb5_init_context (&context);
898 errx(1, "krb5_init_context failed: %d", status);
903 /* request for forwardable on the command line means we should
905 if (do_forwardable == 1)
910 if (use_only_broken) {
916 if(priv_socket1 < 0) {
918 errx (1, "unable to bind reserved port: is rsh setuid root?");
923 if (do_encrypt == 1 && use_only_broken)
924 errx (1, "encryption not supported with old style authentication");
930 if (do_unique_tkfile && unique_tkfile != NULL)
931 errx (1, "Only one of -u and -U allowed.");
933 if (do_unique_tkfile)
934 strlcpy(tkfile,"-u ", sizeof(tkfile));
935 else if (unique_tkfile != NULL) {
936 if (strchr(unique_tkfile,' ') != NULL) {
937 warnx("Space is not allowed in tkfilename");
940 do_unique_tkfile = 1;
941 snprintf (tkfile, sizeof(tkfile), "-U %s ", unique_tkfile);
946 if (argc - argindex < 1)
949 host = argv[host_index = argindex++];
952 if((tmp = strchr(host, '@')) != NULL) {
958 if (argindex == argc) {
959 close (priv_socket1);
960 close (priv_socket2);
962 execvp ("rlogin", argv);
963 err (1, "execvp rlogin");
966 local_user = get_default_username ();
967 if (local_user == NULL)
968 errx (1, "who are you?");
973 cmd_len = construct_command(&cmd, argc - argindex, argv + argindex);
976 * Try all different authentication methods
981 memset (&hints, 0, sizeof(hints));
982 hints.ai_socktype = SOCK_STREAM;
983 hints.ai_protocol = IPPROTO_TCP;
985 if(port_str == NULL) {
986 error = getaddrinfo(host, "kshell", &hints, &ai);
987 if(error == EAI_NONAME)
988 error = getaddrinfo(host, "544", &hints, &ai);
990 error = getaddrinfo(host, port_str, &hints, &ai);
993 errx (1, "getaddrinfo: %s", gai_strerror(error));
995 auth_method = AUTH_KRB5;
997 ret = doit (host, ai, user, local_user, cmd, cmd_len,
999 if(ret != 0 && sendauth_version_error &&
1000 protocol_version == 2) {
1001 protocol_version = 1;
1007 if (ret && use_broken) {
1008 memset (&hints, 0, sizeof(hints));
1009 hints.ai_socktype = SOCK_STREAM;
1010 hints.ai_protocol = IPPROTO_TCP;
1012 if(port_str == NULL) {
1013 error = getaddrinfo(host, "shell", &hints, &ai);
1014 if(error == EAI_NONAME)
1015 error = getaddrinfo(host, "514", &hints, &ai);
1017 error = getaddrinfo(host, port_str, &hints, &ai);
1020 errx (1, "getaddrinfo: %s", gai_strerror(error));
1022 auth_method = AUTH_BROKEN;
1023 ret = doit_broken (argc, argv, host_index, ai,
1026 do_errsock ? priv_socket2 : -1,