2 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
7 * Copyright (c) 1983 Regents of the University of California.
8 * All rights reserved. The Berkeley software License Agreement
9 * specifies the terms and conditions for redistribution.
13 #define _FILE_OFFSET_BITS 64
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <sys/ioctl.h>
19 /* just for FIONBIO ... */
20 #include <sys/filio.h>
22 #include <sys/select.h>
24 #include <netinet/in.h>
37 #include <priv_utils.h>
40 #include <profile/prof_int.h>
45 /* signal disposition - signal handler or SIG_IGN, SIG_ERR, etc. */
46 typedef void (*sigdisp_t
)(int);
48 extern errcode_t
profile_get_options_boolean(profile_t
, char **,
49 profile_options_boolean
*);
50 extern errcode_t
profile_get_options_string(profile_t
, char **,
51 profile_option_strings
*);
53 #define RSH_BUFSIZ (1024 * 50)
55 static char des_inbuf
[2 * RSH_BUFSIZ
]; /* needs to be > largest read size */
56 static char des_outbuf
[2 * RSH_BUFSIZ
]; /* needs to be > largest write size */
57 static krb5_data desinbuf
, desoutbuf
;
58 static krb5_encrypt_block eblock
; /* eblock for encrypt/decrypt */
59 static krb5_context bsd_context
= NULL
;
60 static krb5_auth_context auth_context
;
61 static krb5_creds
*cred
;
62 static krb5_keyblock
*session_key
;
64 static int encrypt_flag
; /* Flag set, when encryption is used */
65 static int krb5auth_flag
; /* Flag set, when KERBEROS is enabled */
66 static profile_options_boolean autologin_option
[] = {
67 { "autologin", &krb5auth_flag
, 0 },
71 static int no_krb5auth_flag
= 0;
72 static int fflag
; /* Flag set, if creds to be fwd'ed via -f */
73 static int Fflag
; /* Flag set, if fwd'able creds to be fwd'ed via -F */
75 /* Flag set, if -PN / -PO is specified */
76 static boolean_t rcmdoption_done
;
78 /* Flags set, if corres. cmd line options are turned on */
79 static boolean_t encrypt_done
, fwd_done
, fwdable_done
;
81 static profile_options_boolean option
[] = {
82 { "encrypt", &encrypt_flag
, 0 },
83 { "forward", &fflag
, 0 },
84 { "forwardable", &Fflag
, 0 },
88 static char *rcmdproto
;
89 static profile_option_strings rcmdversion
[] = {
90 { "rcmd_protocol", &rcmdproto
, 0 },
94 static char *realmdef
[] = { "realms", NULL
, "rsh", NULL
};
95 static char *appdef
[] = { "appdefaults", "rsh", NULL
};
97 static void sendsig(int);
98 static sigdisp_t
sigdisp(int);
99 static boolean_t
init_service(boolean_t
);
100 static int desrshread(int, char *, int);
101 static int desrshwrite(int, char *, int);
105 static int portnumber
;
107 static const char rlogin_path
[] = "/usr/bin/rlogin";
108 static const char dash_x
[] = "-x "; /* Note the blank after -x */
110 static boolean_t readiv
, writeiv
;
112 #define set2mask(setp) ((setp)->__sigbits[0])
113 #define mask2set(mask, setp) \
114 ((mask) == -1 ? sigfillset(setp) : (set2mask(setp) = (mask)))
117 #define DEBUGOPTSTRING "D:"
119 #define DEBUGOPTSTRING ""
127 (void) sigprocmask(0, NULL
, &nset
);
128 mask2set(mask
, &nset
);
129 (void) sigprocmask(SIG_SETMASK
, &nset
, NULL
);
138 (void) sigprocmask(0, NULL
, &nset
);
139 mask2set(mask
, &nset
);
140 (void) sigprocmask(SIG_BLOCK
, &nset
, &oset
);
141 return (set2mask(&oset
));
145 * Get signal disposition (or signal handler) for a given signal
150 struct sigaction act
;
152 act
.sa_handler
= NULL
;
154 (void) sigemptyset(&act
.sa_mask
);
155 (void) sigaction(sig
, NULL
, &act
);
156 return (act
.sa_handler
);
159 static pid_t child_pid
= -1;
162 * If you do a command like "rsh host output | wc"
163 * and wc terminates, then the parent will receive SIGPIPE
164 * and the child needs to be terminated.
168 sigpipehandler(int signal
)
171 (void) kill(child_pid
, SIGKILL
);
175 #define mask(s) (1 << ((s) - 1))
179 (void) fprintf(stderr
, "%s\n%s\n",
180 gettext("usage: rsh [ -PN / -PO ] [ -l login ] [ -n ] "
181 "[ -k realm ] [ -a ] [ -x ] [ -f / -F ] host command"),
182 gettext(" rsh [ -PN / -PO ] [ -l login ] [ -k realm ] "
183 "[ -a ] [ -x ] [ -f / -F ] host"));
188 die(const char *message
)
190 (void) fputs(message
, stderr
);
197 die(gettext("rsh: Only one of -f and -F allowed.\n"));
205 main(int argc
, char **argv
)
208 char *cmd
, *cp
, **ap
, buf
[RSH_BUFSIZ
], **argv0
, *args
, *args_no_x
;
209 char *host
= NULL
, *user
= NULL
;
211 boolean_t asrsh
= B_FALSE
;
213 boolean_t readfrom_rem
;
214 boolean_t readfrom_rfd2
;
217 boolean_t nflag
= B_FALSE
;
218 char *krb_realm
= NULL
;
220 krb5_error_code status
;
221 enum kcmd_proto kcmd_proto
= KCMD_NEW_PROTOCOL
;
222 uid_t uid
= getuid();
224 c
= (argc
+ 1) * sizeof (char *);
225 if ((argv0
= malloc(c
)) == NULL
) {
227 return (EXIT_FAILURE
);
229 (void) memcpy(argv0
, argv
, c
);
231 (void) setlocale(LC_ALL
, "");
233 (void) textdomain(TEXT_DOMAIN
);
236 * Determine command name used to invoke to rlogin(1). Users can
237 * create links named by a host pointing to the binary and type
238 * "hostname" to log into that host afterwards.
240 cmd
= strrchr(argv
[0], '/');
241 cmd
= (cmd
!= NULL
) ? (cmd
+ 1) : argv
[0];
244 * Add "remsh" as an alias for "rsh" (System III, V networking
245 * add-ons often used this name for the remote shell since rsh
246 * was already taken for the restricted shell). Note that this
247 * usurps the ability to use "remsh" as the name of a host (by
248 * symlinking it to rsh), so we go one step farther: if the
249 * file "/usr/bin/remsh" does not exist, we behave as if "remsh"
250 * is a host name. If it does exist, we accept "remsh" as an
253 if (strcmp(cmd
, "remsh") == 0) {
256 if (stat("/usr/bin/remsh", &sb
) < 0)
258 } else if (strcmp(cmd
, "rsh") != 0) {
262 /* Handle legacy synopsis "rsh hostname options [command]". */
266 if (*argv
[1] != '-') {
275 while ((c
= getopt(argc
, argv
,
276 DEBUGOPTSTRING
"8AFKLP:ade:fk:l:nwx")) != -1) {
280 portnumber
= htons(atoi(optarg
));
289 fwdable_done
= B_TRUE
;
299 if (strcmp(optarg
, "N") == 0)
300 kcmd_proto
= KCMD_NEW_PROTOCOL
;
301 else if (strcmp(optarg
, "O") == 0)
302 kcmd_proto
= KCMD_OLD_PROTOCOL
;
304 die(gettext("rsh: Only -PN or -PO "
307 die(gettext("rsh: Only one of -PN and -PO "
309 rcmdoption_done
= B_TRUE
;
330 if (close(STDIN_FILENO
) < 0) {
332 return (EXIT_FAILURE
);
335 * "STDION_FILENO" defined to 0 by POSIX
336 * and hence the lowest file descriptor.
337 * So the open(2) below is guaranteed to
338 * reopen it because we closed it above.
340 if (open("/dev/null", O_RDONLY
) < 0) {
342 return (EXIT_FAILURE
);
350 encrypt_done
= B_TRUE
;
353 * Ignore the -L, -w, -e and -8 flags to allow aliases with
354 * rlogin to work. Actually rlogin(1) doesn't understand
355 * -w either but because "rsh -w hostname command" used
356 * to work we still accept it.
363 * On the lines of the -L, -w, -e and -8 options above, we
364 * ignore the -A option too, in order to allow aliases with
367 * Mind you !, the -a option to trigger Kerberos authentication
368 * in rsh, has a totally different usage in rlogin, its the
369 * -A option (in rlogin) which needs to be used to talk
391 (void) setreuid(uid
, uid
);
396 (void) execv(rlogin_path
, argv0
);
399 (void) fprintf(stderr
, gettext("No local rlogin "
400 "program found.\n"));
401 return (EXIT_FAILURE
);
404 if (__init_suid_priv(0, PRIV_NET_PRIVADDR
, NULL
) == -1) {
405 (void) fprintf(stderr
,
406 gettext("Insufficient privileges, "
407 "rsh must be set-uid root\n"));
408 return (EXIT_FAILURE
);
413 (void) fprintf(stderr
, gettext("who are you?\n"));
414 return (EXIT_FAILURE
);
420 * if the user disables krb5 on the cmdline (-K), then skip
423 * if the user does not disable krb5 or enable krb5 on the
424 * cmdline, check krb5.conf to see if it should be enabled.
427 if (no_krb5auth_flag
) {
429 Fflag
= fflag
= encrypt_flag
= 0;
430 } else if (!krb5auth_flag
) {
431 /* is autologin set in krb5.conf? */
432 status
= krb5_init_context(&bsd_context
);
433 /* don't sweat failure here */
436 * note that the call to profile_get_options_boolean
437 * with autologin_option can affect value of
440 (void) profile_get_options_boolean(bsd_context
->profile
,
448 status
= krb5_init_context(&bsd_context
);
450 com_err("rsh", status
,
451 "while initializing krb5");
452 return (EXIT_FAILURE
);
458 * Get our local realm to look up local realm options.
460 status
= krb5_get_default_realm(bsd_context
, &realmdef
[1]);
462 com_err("rsh", status
,
463 gettext("while getting default realm"));
464 return (EXIT_FAILURE
);
467 * Check the realms section in krb5.conf for encryption,
468 * forward & forwardable info
470 (void) profile_get_options_boolean(bsd_context
->profile
,
473 * Check the appdefaults section
475 (void) profile_get_options_boolean(bsd_context
->profile
,
477 (void) profile_get_options_string(bsd_context
->profile
,
478 appdef
, rcmdversion
);
480 * Set the *_flag variables, if the corresponding *_done are
481 * set to 1, because we dont want the config file values
482 * overriding the command line options.
489 } else if (fwdable_done
) {
493 if (!rcmdoption_done
&& (rcmdproto
!= NULL
)) {
494 if (strncmp(rcmdproto
, "rcmdv2", 6) == 0) {
495 kcmd_proto
= KCMD_NEW_PROTOCOL
;
496 } else if (strncmp(rcmdproto
, "rcmdv1", 6) == 0) {
497 kcmd_proto
= KCMD_OLD_PROTOCOL
;
499 (void) fprintf(stderr
, gettext("Unrecognized "
500 "KCMD protocol (%s)"), rcmdproto
);
501 return (EXIT_FAILURE
);
506 if (encrypt_flag
&& (!krb5_privacy_allowed())) {
507 (void) fprintf(stderr
, gettext("rsh: Encryption not "
509 return (EXIT_FAILURE
);
514 * Connect with the service (shell/kshell) on the daemon side
516 if (portnumber
== 0) {
517 while (!init_service(krb5auth_flag
)) {
519 * Connecting to the 'kshell' service failed,
520 * fallback to normal rsh; Reset all KRB5 flags
521 * and connect to 'shell' service on the server
524 encrypt_flag
= fflag
= Fflag
= 0;
528 cc
= encrypt_flag
? strlen(dash_x
) : 0;
529 for (ap
= argv
; *ap
!= NULL
; ap
++)
530 cc
+= strlen(*ap
) + 1;
531 cp
= args
= malloc(cc
);
537 length
= strlcpy(args
, dash_x
, cc
);
543 for (ap
= argv
; *ap
!= NULL
; ap
++) {
546 length
= strlcpy(cp
, *ap
, cc
);
557 authopts
= AP_OPTS_MUTUAL_REQUIRED
;
559 * Piggy-back forwarding flags on top of authopts;
560 * they will be reset in kcmd
563 authopts
|= OPTS_FORWARD_CREDS
;
565 authopts
|= OPTS_FORWARDABLE_CREDS
;
567 status
= kcmd(&rem
, &host
, portnumber
,
569 args
, &rfd2
, "host", krb_realm
,
570 bsd_context
, &auth_context
, &cred
,
571 NULL
, /* No need for sequence number */
572 NULL
, /* No need for server seq # */
574 1, /* Always set anyport */
578 * If new protocol requested, we dont fallback to
581 if (kcmd_proto
== KCMD_NEW_PROTOCOL
) {
582 (void) fprintf(stderr
, gettext("rsh: kcmdv2 "
583 "to host %s failed - %s\n"
584 "Fallback to normal rsh denied."),
585 host
, error_message(status
));
586 return (EXIT_FAILURE
);
588 /* check NO_TKT_FILE or equivalent... */
590 (void) fprintf(stderr
,
591 gettext("rsh: kcmd to host %s failed - %s\n"
592 "trying normal rsh...\n\n"),
593 host
, error_message(status
));
595 (void) fprintf(stderr
,
596 gettext("trying normal rsh...\n"));
599 * kcmd() failed, so we now fallback to normal rsh,
600 * after resetting the KRB5 flags and the 'args' array
603 encrypt_flag
= fflag
= Fflag
= 0;
605 (void) init_service(B_FALSE
);
608 * Set up buffers for desread and deswrite.
610 desinbuf
.data
= des_inbuf
;
611 desoutbuf
.data
= des_outbuf
;
612 desinbuf
.length
= sizeof (des_inbuf
);
613 desoutbuf
.length
= sizeof (des_outbuf
);
615 session_key
= &cred
->keyblock
;
617 if (kcmd_proto
== KCMD_NEW_PROTOCOL
) {
618 status
= krb5_auth_con_getlocalsubkey(
623 com_err("rsh", status
,
624 "determining subkey for session");
625 return (EXIT_FAILURE
);
627 if (session_key
== NULL
) {
628 com_err("rsh", 0, "no subkey "
629 "negotiated for connection");
630 return (EXIT_FAILURE
);
634 eblock
.crypto_entry
= session_key
->enctype
;
635 eblock
.key
= (krb5_keyblock
*)session_key
;
637 init_encrypt(encrypt_flag
, bsd_context
, kcmd_proto
,
638 &desinbuf
, &desoutbuf
, CLIENT
, &eblock
);
640 char *s
= gettext("This rsh session is using "
641 "encryption for all data transmissions.");
642 (void) write(STDERR_FILENO
, s
, strlen(s
));
643 (void) write(STDERR_FILENO
, "\r\n", 2);
649 * Don't merge this with the "if" statement above because
650 * "krb5auth_flag" might be set to false inside it.
652 if (!krb5auth_flag
) {
653 rem
= rcmd_af(&host
, portnumber
, pwd
->pw_name
, user
, args
,
656 return (EXIT_FAILURE
);
661 (void) fprintf(stderr
, gettext("rsh: can't establish "
663 return (EXIT_FAILURE
);
665 if (options
& SO_DEBUG
) {
666 if (setsockopt(rem
, SOL_SOCKET
, SO_DEBUG
, (char *)&one
,
668 perror("rsh: setsockopt (stdin)");
669 if (setsockopt(rfd2
, SOL_SOCKET
, SO_DEBUG
, (char *)&one
,
671 perror("rsh: setsockopt (stderr)");
673 omask
= sigblock(mask(SIGINT
)|mask(SIGQUIT
)|mask(SIGTERM
));
675 if (sigdisp(SIGINT
) != SIG_IGN
)
676 (void) sigset(SIGINT
, sendsig
);
677 if (sigdisp(SIGQUIT
) != SIG_IGN
)
678 (void) sigset(SIGQUIT
, sendsig
);
679 if (sigdisp(SIGTERM
) != SIG_IGN
)
680 (void) sigset(SIGTERM
, sendsig
);
683 (void) shutdown(rem
, SHUT_WR
);
688 return (EXIT_FAILURE
);
692 (void) ioctl(rfd2
, FIONBIO
, &one
);
693 (void) ioctl(rem
, FIONBIO
, &one
);
696 if (child_pid
== 0) {
704 cc
= read(0, buf
, sizeof (buf
));
710 FD_SET(rem
, &remset
);
711 if (select(rem
+ 1, NULL
, &remset
, NULL
, NULL
) < 0) {
712 if (errno
!= EINTR
) {
713 perror("rsh: select");
714 return (EXIT_FAILURE
);
718 if (!FD_ISSET(rem
, &remset
))
721 wc
= desrshwrite(rem
, bp
, cc
);
723 if (errno
== EWOULDBLOCK
)
732 (void) shutdown(rem
, SHUT_WR
);
733 return (EXIT_SUCCESS
);
737 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
740 readfrom_rem
= B_TRUE
;
741 readfrom_rfd2
= B_TRUE
;
742 (void) sigset(SIGPIPE
, sigpipehandler
);
748 FD_SET(rem
, &readyset
);
750 FD_SET(rfd2
, &readyset
);
751 if (select(MAX(rem
, rfd2
) + 1, &readyset
, NULL
, NULL
,
753 if (errno
!= EINTR
) {
754 perror("rsh: select");
755 return (EXIT_FAILURE
);
759 if (FD_ISSET(rfd2
, &readyset
)) {
762 cc
= desrshread(rfd2
, buf
, sizeof (buf
));
764 if (errno
!= EWOULDBLOCK
)
765 readfrom_rfd2
= B_FALSE
;
767 (void) write(STDERR_FILENO
, buf
, cc
);
770 if (FD_ISSET(rem
, &readyset
)) {
773 cc
= desrshread(rem
, buf
, sizeof (buf
));
775 if (errno
!= EWOULDBLOCK
)
776 readfrom_rem
= B_FALSE
;
778 (void) write(STDOUT_FILENO
, buf
, cc
);
780 } while (readfrom_rem
|| readfrom_rfd2
);
783 (void) kill(child_pid
, SIGKILL
);
784 return (EXIT_SUCCESS
);
793 buffer
= (char)signum
;
794 (void) desrshwrite(rfd2
, &buffer
, 1);
798 init_service(boolean_t krb5flag
)
803 sp
= getservbyname("kshell", "tcp");
805 (void) fprintf(stderr
,
806 gettext("rsh: kshell/tcp: unknown service.\n"
807 "trying normal shell/tcp service\n"));
811 sp
= getservbyname("shell", "tcp");
813 portnumber
= htons(IPPORT_CMDSERVER
);
818 portnumber
= sp
->s_port
;
823 desrshread(int fd
, char *buf
, int len
)
825 return (desread(fd
, buf
, len
, readiv
? 1 : 0));
829 desrshwrite(int fd
, char *buf
, int len
)
831 return (deswrite(fd
, buf
, len
, writeiv
? 1 : 0));