4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1983-1989 AT&T */
28 /* All Rights Reserved */
31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 * under license from the Regents of the University of California.
35 #define _FILE_OFFSET_BITS 64
38 * remote shell server:
44 #include <sys/types.h>
45 #include <sys/ioctl.h>
46 #include <sys/telioctl.h>
47 #include <sys/param.h>
48 #include <sys/socket.h>
52 #include <sys/select.h>
54 #include <netinet/in.h>
56 #include <arpa/inet.h>
72 #include <sys/resource.h>
73 #include <sys/filio.h>
77 #include <security/pam_appl.h>
81 #include <krb5_repository.h>
85 #include <addr_match.h>
86 #include <store_forw_creds.h>
92 static void error(char *, ...);
93 static void doit(int, struct sockaddr_storage
*, char **);
94 static void getstr(int, char *, int, char *);
96 static int legalenvvar(char *);
97 static void add_to_envinit(char *);
98 static int locale_envmatch(char *, char *);
100 /* Function decls. for functions not in any header file. (Grrrr.) */
101 extern int audit_rshd_setup(void);
102 extern int audit_rshd_success(char *, char *, char *, char *);
103 extern int audit_rshd_fail(char *, char *, char *, char *, char *);
104 extern int audit_settid(int);
106 static int do_encrypt
= 0;
107 static pam_handle_t
*pamh
;
110 * This is the shell/kshell daemon. The very basic protocol for checking
111 * authentication and authorization is:
112 * 1) Check authentication.
113 * 2) Check authorization via the access-control files:
114 * ~/.k5login (using krb5_kuserok) and/or
115 * Execute command if configured authoriztion checks pass, else deny
118 * The configuration is done either by command-line arguments passed by inetd,
119 * or by the name of the daemon. If command-line arguments are present, they
120 * take priority. The options are:
121 * -k allow kerberos authentication (krb5 only; krb4 support is not provided)
122 * -5 same as `-k', mainly for compatability with MIT
123 * -e allow encrypted session
124 * -c demand authenticator checksum
125 * -i ignore authenticator checksum
126 * -U Refuse connections that cannot be mapped to a name via `gethostbyname'
127 * -s <tos> Set the IP TOS option
128 * -S <keytab> Set the keytab file to use
129 * -M <realm> Set the Kerberos realm to use
132 #define ARGSTR "ek5ciUD:M:S:L:?:"
133 #define RSHD_BUFSIZ (50 * 1024)
135 static krb5_context bsd_context
;
136 static krb5_keytab keytab
= NULL
;
137 static krb5_ccache ccache
= NULL
;
138 static krb5_keyblock
*sessionkey
= NULL
;
140 static int require_encrypt
= 0;
141 static int resolve_hostname
= 0;
142 static int krb5auth_flag
= 0; /* Flag set, when KERBEROS is enabled */
143 static enum kcmd_proto kcmd_protocol
;
146 static int debug_port
= 0;
150 * There are two authentication related masks:
151 * auth_ok and auth_sent.
152 * The auth_ok mask is the or'ing of authentication
153 * systems any one of which can be used.
154 * The auth_sent mask is the or'ing of one or more authentication/authorization
155 * systems that succeeded. If the and'ing
156 * of these two masks is true, then authorization is successful.
159 #define AUTH_KRB5 (0x2)
160 static int auth_ok
= 0;
161 static int auth_sent
= 0;
162 static int checksum_required
= 0;
163 static int checksum_ignored
= 0;
166 * Leave room for 4 environment variables to be passed.
167 * The "-L env_var" option has been added primarily to
168 * maintain compatability with MIT.
171 static char *save_env
[MAXENV
];
172 static int num_env
= 0;
174 static void usage(void);
175 static krb5_error_code
recvauth(int, int *);
179 main(int argc
, char **argv
, char **renvp
)
181 struct linger linger
;
183 struct sockaddr_storage from
;
186 extern int opterr
, optind
;
190 krb5_error_code status
;
192 openlog("rsh", LOG_PID
| LOG_ODELAY
, LOG_DAEMON
);
193 (void) audit_rshd_setup(); /* BSM */
194 fromlen
= sizeof (from
);
196 (void) setlocale(LC_ALL
, "");
199 * Analyze parameters.
202 while ((ch
= getopt(argc
, argv
, ARGSTR
)) != EOF
)
206 auth_ok
|= AUTH_KRB5
;
211 checksum_required
= 1;
215 checksum_ignored
= 1;
225 debug_port
= atoi(optarg
);
229 resolve_hostname
= 1;
233 (void) krb5_set_default_realm(bsd_context
, optarg
);
238 if ((status
= krb5_kt_resolve(bsd_context
, optarg
,
240 com_err("rsh", status
,
241 gettext("while resolving "
242 "srvtab file %s"), optarg
);
249 if (optarg
== NULL
|| ((tos
= atoi(optarg
)) < 0) ||
251 syslog(LOG_ERR
, "rshd: illegal tos value: "
257 if (num_env
< MAXENV
) {
258 save_env
[num_env
] = strdup(optarg
);
259 if (!save_env
[num_env
++]) {
260 com_err("rsh", ENOMEM
,
261 gettext("in saving env"));
265 (void) fprintf(stderr
, gettext("rshd: Only %d"
266 " -L arguments allowed\n"),
286 if (krb5auth_flag
> 0) {
287 status
= krb5_init_context(&bsd_context
);
289 syslog(LOG_ERR
, "Error initializing krb5: %s",
290 error_message(status
));
295 if (!checksum_required
&& !checksum_ignored
)
296 checksum_ignored
= 1;
298 if (checksum_required
&& checksum_ignored
) {
299 syslog(LOG_CRIT
, gettext("Checksums are required and ignored."
300 "These options are mutually exclusive"
301 "--check the documentation."));
302 error("Configuration error: mutually exclusive "
303 "options specified.\n");
310 struct sockaddr_in sin
;
312 if ((s
= socket(AF_INET
, SOCK_STREAM
, PF_UNSPEC
)) < 0) {
313 fprintf(stderr
, gettext("Error in socket: %s\n"),
317 (void) memset((char *)&sin
, 0, sizeof (sin
));
318 sin
.sin_family
= AF_INET
;
319 sin
.sin_port
= htons(debug_port
);
320 sin
.sin_addr
.s_addr
= INADDR_ANY
;
322 (void) setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
,
323 (char *)&on
, sizeof (on
));
325 if ((bind(s
, (struct sockaddr
*)&sin
, sizeof (sin
))) < 0) {
326 (void) fprintf(stderr
, gettext("Error in bind: %s\n"),
330 if ((listen(s
, 5)) < 0) {
331 (void) fprintf(stderr
, gettext("Error in listen: %s\n"),
335 if ((fd
= accept(s
, (struct sockaddr
*)&from
,
337 (void) fprintf(stderr
, gettext("Error in accept: %s\n"),
346 if (getpeername(STDIN_FILENO
, (struct sockaddr
*)&from
,
347 (socklen_t
*)&fromlen
) < 0) {
348 (void) fprintf(stderr
, "rshd: ");
349 perror("getpeername");
355 if (audit_settid(fd
) != 0) {
360 if (setsockopt(fd
, SOL_SOCKET
, SO_KEEPALIVE
, (char *)&on
,
362 syslog(LOG_WARNING
, "setsockopt (SO_KEEPALIVE): %m");
364 linger
.l_linger
= 60; /* XXX */
365 if (setsockopt(fd
, SOL_SOCKET
, SO_LINGER
, (char *)&linger
,
366 sizeof (linger
)) < 0)
367 syslog(LOG_WARNING
, "setsockopt (SO_LINGER): %m");
369 if ((tos
!= -1) && (setsockopt(fd
, IPPROTO_IP
, IP_TOS
, (char *)&tos
,
370 sizeof (tos
)) < 0) &&
371 (errno
!= ENOPROTOOPT
)) {
372 syslog(LOG_ERR
, "setsockopt (IP_TOS %d): %m");
375 doit(dup(fd
), &from
, renvp
);
380 * locale environments to be passed to shells.
382 static char *localeenv
[] = {
384 "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE",
385 "LC_MONETARY", "LC_MESSAGES", "LC_ALL", NULL
};
388 * The following is for the environment variable list
389 * used in the call to execle(). envinit is declared here,
390 * but populated after the call to getpwnam().
392 static char *homedir
; /* "HOME=" */
393 static char *shell
; /* "SHELL=" */
394 static char *username
; /* "USER=" */
395 static char *tz
; /* "TZ=" */
397 static char homestr
[] = "HOME=";
398 static char shellstr
[] = "SHELL=";
399 static char userstr
[] = "USER=";
400 static char tzstr
[] = "TZ=";
402 static char **envinit
;
403 #define PAM_ENV_ELIM 16 /* allow 16 PAM environment variables */
404 #define USERNAME_LEN 16 /* maximum number of characters in user name */
407 * See PSARC opinion 1992/025
409 static char userpath
[] = "PATH=/usr/bin:";
410 static char rootpath
[] = "PATH=/usr/sbin:/usr/bin";
412 static char cmdbuf
[NCARGS
+1];
413 static char hostname
[MAXHOSTNAMELEN
+ 1];
414 static char locuser
[USERNAME_LEN
+ 1];
415 static char remuser
[USERNAME_LEN
+ 1];
417 #define KRB5_RECVAUTH_V5 5
418 #define SIZEOF_INADDR sizeof (struct in_addr)
420 #define MAX_REPOSITORY_LEN 255
421 static char repository
[MAX_REPOSITORY_LEN
];
423 static char *kremuser
;
424 static krb5_principal client
= NULL
;
426 static char remote_addr
[64];
427 static char local_addr
[64];
429 #define _PATH_DEFAULT_LOGIN "/etc/default/login"
432 doit(int f
, struct sockaddr_storage
*fromp
, char **renvp
)
443 krb5_error_code status
;
447 struct sockaddr_in localaddr
;
452 int pv
[2], pw
[2], px
[2], cc
;
453 char buf
[RSHD_BUFSIZ
];
460 char abuf
[INET6_ADDRSTRLEN
];
461 struct sockaddr_in
*sin
;
462 struct sockaddr_in6
*sin6
;
464 int homedir_len
, shell_len
, username_len
, tz_len
;
469 (void) signal(SIGINT
, SIG_DFL
);
470 (void) signal(SIGQUIT
, SIG_DFL
);
471 (void) signal(SIGTERM
, SIG_DFL
);
472 (void) signal(SIGXCPU
, SIG_DFL
);
473 (void) signal(SIGXFSZ
, SIG_DFL
);
474 (void) sigset(SIGCHLD
, SIG_IGN
);
475 (void) signal(SIGPIPE
, SIG_DFL
);
476 (void) signal(SIGHUP
, SIG_DFL
);
480 int t
= open("/dev/tty", 2);
488 if (fromp
->ss_family
== AF_INET
) {
489 sin
= (struct sockaddr_in
*)fromp
;
490 port
= ntohs((ushort_t
)sin
->sin_port
);
491 fromplen
= sizeof (struct sockaddr_in
);
492 } else if (fromp
->ss_family
== AF_INET6
) {
493 sin6
= (struct sockaddr_in6
*)fromp
;
494 port
= ntohs((ushort_t
)sin6
->sin6_port
);
495 fromplen
= sizeof (struct sockaddr_in6
);
497 syslog(LOG_ERR
, "wrong address family\n");
501 if (fromp
->ss_family
== AF_INET6
) {
502 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) {
503 struct in_addr ipv4_addr
;
505 IN6_V4MAPPED_TO_INADDR(&sin6
->sin6_addr
, &ipv4_addr
);
506 (void) inet_ntop(AF_INET
, &ipv4_addr
, abuf
,
509 (void) inet_ntop(AF_INET6
, &sin6
->sin6_addr
, abuf
,
512 } else if (fromp
->ss_family
== AF_INET
) {
513 (void) inet_ntop(AF_INET
, &sin
->sin_addr
, abuf
, sizeof (abuf
));
516 sin_len
= sizeof (struct sockaddr_in
);
517 if (getsockname(f
, (struct sockaddr
*)&localaddr
, &sin_len
) < 0) {
518 perror("getsockname");
524 bad_port
= (port
>= IPPORT_RESERVED
||
525 port
< (uint_t
)(IPPORT_RESERVED
/2));
527 /* Get the name of the client side host to use later */
528 no_name
= (getnameinfo((const struct sockaddr
*) fromp
, fromplen
,
529 hostname
, sizeof (hostname
), NULL
, 0, 0) != 0);
531 if (bad_port
|| no_name
!= 0) {
533 * If there is no host name available then use the
534 * IP address to identify the host in the PAM call
535 * below. Do the same if a bad port was used, to
536 * prevent untrustworthy authentication.
538 (void) strlcpy(hostname
, abuf
, sizeof (hostname
));
543 * If the '-U' option was given on the cmd line,
544 * we must be able to lookup the hostname
546 if (resolve_hostname
) {
547 syslog(LOG_ERR
, "rshd: Couldn't resolve your "
548 "address into a host name.\r\n Please "
549 "contact your net administrator");
554 * Even if getnameinfo() succeeded, we still have to check
557 check_address("rshd", fromp
, sin
, sin6
, abuf
, hostname
,
561 if (!krb5auth_flag
&& bad_port
) {
563 syslog(LOG_NOTICE
, "connection from %s - "
566 syslog(LOG_NOTICE
, "connection from %s (%s) - "
567 "bad port\n", hostname
, abuf
);
575 if ((cc
= read(f
, &c
, 1)) != 1) {
577 syslog(LOG_NOTICE
, "read: %m");
578 (void) shutdown(f
, 1+1);
583 port
= port
* 10 + c
- '0';
588 struct sockaddr_storage ctl_addr
;
591 (void) memset(&ctl_addr
, 0, sizeof (ctl_addr
));
592 addrlen
= sizeof (ctl_addr
);
593 if (getsockname(f
, (struct sockaddr
*)&ctl_addr
,
595 syslog(LOG_ERR
, "getsockname: %m");
600 * 0 means that rresvport_addr() will bind to a port in
601 * the anonymous priviledged port range.
605 * Kerberos does not support IPv6 yet.
607 lport
= IPPORT_RESERVED
- 1;
609 s
= rresvport_addr(&lport
, &ctl_addr
);
612 syslog(LOG_ERR
, "can't get stderr port: %m");
615 if (!krb5auth_flag
&& (port
>= IPPORT_RESERVED
)) {
616 syslog(LOG_ERR
, "2nd port not reserved\n");
619 if (fromp
->ss_family
== AF_INET
) {
620 sin
->sin_port
= htons((ushort_t
)port
);
621 } else if (fromp
->ss_family
== AF_INET6
) {
622 sin6
->sin6_port
= htons((ushort_t
)port
);
624 if (connect(s
, (struct sockaddr
*)fromp
, fromplen
) < 0) {
625 if (errno
== EADDRINUSE
) {
629 syslog(LOG_INFO
, "connect second port: %m");
638 syslog(LOG_NOTICE
, "rshd: Client hostname = %s", hostname
);
640 syslog(LOG_NOTICE
, "rshd: Debug port is %d", debug_port
);
641 if (krb5auth_flag
> 0)
642 syslog(LOG_NOTICE
, "rshd: Kerberos mode is ON");
644 syslog(LOG_NOTICE
, "rshd: Kerberos mode is OFF");
647 if (krb5auth_flag
> 0) {
648 if ((status
= recvauth(f
, &valid_checksum
))) {
649 syslog(LOG_ERR
, gettext("Kerberos Authentication "
651 error("Authentication failed: %s\n",
652 error_message(status
));
653 (void) audit_rshd_fail("Kerberos Authentication "
654 "failed", hostname
, remuser
, locuser
, cmdbuf
);
658 if (checksum_required
&& !valid_checksum
&&
659 kcmd_protocol
== KCMD_OLD_PROTOCOL
) {
660 syslog(LOG_WARNING
, "Client did not supply required"
661 " checksum--connection rejected.");
662 error("Client did not supply required"
663 "checksum--connection rejected.\n");
664 (void) audit_rshd_fail("Client did not supply required"
665 " checksum--connection rejected.", hostname
,
666 remuser
, locuser
, cmdbuf
); /* BSM */
671 * Authentication has succeeded, we now need
672 * to check authorization.
674 * krb5_kuserok returns 1 if OK.
676 if (client
&& krb5_kuserok(bsd_context
, client
, locuser
)) {
677 auth_sent
|= AUTH_KRB5
;
679 syslog(LOG_ERR
, "Principal %s (%s@%s) for local user "
680 "%s failed krb5_kuserok.\n",
681 kremuser
, remuser
, hostname
, locuser
);
684 getstr(netf
, remuser
, sizeof (remuser
), "remuser");
685 getstr(netf
, locuser
, sizeof (locuser
), "locuser");
686 getstr(netf
, cmdbuf
, sizeof (cmdbuf
), "command");
690 syslog(LOG_NOTICE
, "rshd: locuser = %s, remuser = %s, cmdbuf = %s",
691 locuser
, remuser
, cmdbuf
);
695 * Note that there is no rsh conv functions at present.
697 if (krb5auth_flag
> 0) {
698 if ((err
= pam_start("krsh", locuser
, NULL
, &pamh
))
700 syslog(LOG_ERR
, "pam_start() failed: %s\n",
701 pam_strerror(0, err
));
707 if ((err
= pam_start("rsh", locuser
, NULL
, &pamh
))
709 syslog(LOG_ERR
, "pam_start() failed: %s\n",
710 pam_strerror(0, err
));
714 if ((err
= pam_set_item(pamh
, PAM_RHOST
, hostname
)) != PAM_SUCCESS
) {
715 syslog(LOG_ERR
, "pam_set_item() failed: %s\n",
716 pam_strerror(pamh
, err
));
719 if ((err
= pam_set_item(pamh
, PAM_RUSER
, remuser
)) != PAM_SUCCESS
) {
720 syslog(LOG_ERR
, "pam_set_item() failed: %s\n",
721 pam_strerror(pamh
, err
));
725 pwd
= getpwnam(locuser
);
726 shpwd
= getspnam(locuser
);
727 if ((pwd
== NULL
) || (shpwd
== NULL
)) {
728 if (krb5auth_flag
> 0)
729 syslog(LOG_ERR
, "Principal %s (%s@%s) for local user "
730 "%s has no account.\n", kremuser
, remuser
,
732 error("permission denied.\n");
733 (void) audit_rshd_fail("Login incorrect", hostname
,
734 remuser
, locuser
, cmdbuf
); /* BSM */
738 if (krb5auth_flag
> 0) {
739 (void) snprintf(repository
, sizeof (repository
),
740 KRB5_REPOSITORY_NAME
);
742 * We currently only support special handling of the
743 * KRB5 PAM repository
745 if (strlen(locuser
) != 0) {
746 krb5_repository_data_t krb5_data
;
747 pam_repository_t pam_rep_data
;
749 krb5_data
.principal
= locuser
;
750 krb5_data
.flags
= SUNW_PAM_KRB5_ALREADY_AUTHENTICATED
;
752 pam_rep_data
.type
= repository
;
753 pam_rep_data
.scope
= (void *)&krb5_data
;
754 pam_rep_data
.scope_len
= sizeof (krb5_data
);
756 (void) pam_set_item(pamh
, PAM_REPOSITORY
,
757 (void *)&pam_rep_data
);
761 if (shpwd
->sp_pwdp
!= 0) {
762 if (*shpwd
->sp_pwdp
!= '\0') {
763 if ((v
= pam_authenticate(pamh
, 0)) != PAM_SUCCESS
) {
764 error("permission denied\n");
765 (void) audit_rshd_fail("Permission denied",
766 hostname
, remuser
, locuser
, cmdbuf
);
767 (void) pam_end(pamh
, v
);
774 * maintain 2.1 and 4.* and BSD semantics with
775 * anonymous rshd unless PASSREQ is set to YES in
776 * /etc/default/login: then we deny logins with empty
779 if (defopen(_PATH_DEFAULT_LOGIN
) == 0) {
780 flags
= defcntl(DC_GETFLAGS
, 0);
781 TURNOFF(flags
, DC_CASE
);
782 (void) defcntl(DC_SETFLAGS
, flags
);
784 if ((p
= defread("PASSREQ=")) != NULL
&&
785 strcasecmp(p
, "YES") == 0) {
786 error("permission denied\n");
787 (void) audit_rshd_fail(
788 "Permission denied", hostname
,
789 remuser
, locuser
, cmdbuf
);
790 (void) pam_end(pamh
, PAM_ABORT
);
791 (void) defopen(NULL
);
792 syslog(LOG_AUTH
|LOG_NOTICE
,
793 "empty password not allowed for "
794 "%s from %s.", locuser
, hostname
);
797 (void) defopen(NULL
);
800 * /etc/default/login not found or PASSREQ not set
801 * to YES. Allow logins without passwords.
806 if (krb5auth_flag
> 0) {
807 if (require_encrypt
&& (!do_encrypt
)) {
808 error("You must use encryption.\n");
809 (void) audit_rshd_fail("You must use encryption.",
810 hostname
, remuser
, locuser
, cmdbuf
); /* BSM */
814 if (!(auth_ok
& auth_sent
)) {
816 error("Another authentication mechanism "
817 "must be used to access this host.\n");
818 (void) audit_rshd_fail("Another authentication"
819 " mechanism must be used to access"
820 " this host.\n", hostname
, remuser
,
821 locuser
, cmdbuf
); /* BSM */
824 error("Permission denied.\n");
825 (void) audit_rshd_fail("Permission denied.",
826 hostname
, remuser
, locuser
, cmdbuf
);
833 if (pwd
->pw_uid
&& !access("/etc/nologin", F_OK
)) {
834 error("Logins currently disabled.\n");
835 (void) audit_rshd_fail("Logins currently disabled.",
836 hostname
, remuser
, locuser
, cmdbuf
);
840 /* Log access to account */
841 if (pwd
&& (pwd
->pw_uid
== 0)) {
842 syslog(LOG_NOTICE
, "Executing %s for user %s (%s@%s)"
844 kremuser
, remuser
, hostname
);
848 if ((v
= pam_acct_mgmt(pamh
, 0)) != PAM_SUCCESS
) {
850 case PAM_NEW_AUTHTOK_REQD
:
851 error("password expired\n");
852 (void) audit_rshd_fail("Password expired", hostname
,
853 remuser
, locuser
, cmdbuf
); /* BSM */
855 case PAM_PERM_DENIED
:
856 error("account expired\n");
857 (void) audit_rshd_fail("Account expired", hostname
,
858 remuser
, locuser
, cmdbuf
); /* BSM */
860 case PAM_AUTHTOK_EXPIRED
:
861 error("password expired\n");
862 (void) audit_rshd_fail("Password expired", hostname
,
863 remuser
, locuser
, cmdbuf
); /* BSM */
866 error("login incorrect\n");
867 (void) audit_rshd_fail("Permission denied", hostname
,
868 remuser
, locuser
, cmdbuf
); /* BSM */
871 (void) pam_end(pamh
, PAM_ABORT
);
875 if (chdir(pwd
->pw_dir
) < 0) {
878 error("No remote directory.\n");
885 * XXX There is no session management currently being done
888 (void) write(STDERR_FILENO
, "\0", 1);
889 if (port
|| do_encrypt
) {
890 if ((pipe(pv
) < 0)) {
891 error("Can't make pipe.\n");
892 (void) pam_end(pamh
, PAM_ABORT
);
897 error("Can't make pipe 2.\n");
898 (void) pam_end(pamh
, PAM_ABORT
);
902 error("Can't make pipe 3.\n");
903 (void) pam_end(pamh
, PAM_ABORT
);
908 if (pid
== (pid_t
)-1) {
909 error("Fork (to start shell) failed on server. "
910 "Please try again later.\n");
911 (void) pam_end(pamh
, PAM_ABORT
);
918 (void) close(STDIN_FILENO
);
919 (void) close(STDOUT_FILENO
);
920 (void) close(STDERR_FILENO
);
929 (void) FD_ZERO(&readfrom
);
931 FD_SET(pv
[0], &readfrom
);
933 FD_SET(pw
[0], &readfrom
);
934 FD_SET(f
, &readfrom
);
937 FD_SET(s
, &readfrom
);
939 /* read f (net), write to px[1] (child stdin) */
940 /* read pw[0] (child stdout), write to f (net) */
941 /* read s (alt. channel), signal child */
942 /* read pv[0] (child stderr), write to s */
943 if (ioctl(pv
[0], FIONBIO
, (char *)&one
) == -1)
944 syslog(LOG_INFO
, "ioctl FIONBIO: %m");
946 ioctl(pw
[0], FIONBIO
, (char *)&one
) == -1)
947 syslog(LOG_INFO
, "ioctl FIONBIO: %m");
950 if (select(FD_SETSIZE
, &ready
, NULL
,
952 if (errno
== EINTR
) {
959 * Read from child stderr, write to net
961 if (port
&& FD_ISSET(pv
[0], &ready
)) {
963 cc
= read(pv
[0], buf
, sizeof (buf
));
965 (void) shutdown(s
, 2);
966 FD_CLR(pv
[0], &readfrom
);
968 (void) deswrite(s
, buf
, cc
, 1);
972 * Read from alternate channel, signal child
974 if (port
&& FD_ISSET(s
, &ready
)) {
975 if ((int)desread(s
, &sig
, 1, 1) <= 0)
976 FD_CLR(s
, &readfrom
);
978 (void) killpg(pid
, sig
);
981 * Read from child stdout, write to net
983 if (do_encrypt
&& FD_ISSET(pw
[0], &ready
)) {
985 cc
= read(pw
[0], buf
, sizeof (buf
));
987 (void) shutdown(f
, 2);
988 FD_CLR(pw
[0], &readfrom
);
990 (void) deswrite(f
, buf
, cc
, 0);
994 * Read from the net, write to child stdin
996 if (do_encrypt
&& FD_ISSET(f
, &ready
)) {
998 cc
= desread(f
, buf
, sizeof (buf
), 0);
1000 (void) close(px
[1]);
1001 FD_CLR(f
, &readfrom
);
1004 wcc
= write(px
[1], buf
, cc
);
1014 (void) close(px
[1]);
1015 FD_CLR(f
, &readfrom
);
1016 } else if (wcc
!= cc
) {
1018 syslog(LOG_INFO
, gettext("only wrote %d/%d to child"),
1023 } while ((port
&& FD_ISSET(s
, &readfrom
)) ||
1024 (port
&& FD_ISSET(pv
[0], &readfrom
)) ||
1025 (do_encrypt
&& FD_ISSET(f
, &readfrom
)) ||
1026 (do_encrypt
&& FD_ISSET(pw
[0], &readfrom
)));
1028 syslog(LOG_INFO
, "Shell process completed.");
1031 (void) pam_close_session(pamh
, 0);
1032 (void) pam_end(pamh
, PAM_SUCCESS
);
1035 } /* End of Parent block */
1037 (void) setsid(); /* Should be the same as above. */
1038 (void) close(pv
[0]);
1039 (void) dup2(pv
[1], 2);
1040 (void) close(pv
[1]);
1045 (void) close(pw
[0]);
1046 (void) close(px
[1]);
1048 (void) dup2(px
[0], 0);
1049 (void) dup2(pw
[1], 1);
1051 (void) close(px
[0]);
1052 (void) close(pw
[1]);
1056 if (*pwd
->pw_shell
== '\0')
1057 pwd
->pw_shell
= "/bin/sh";
1061 * write audit record before making uid switch
1063 (void) audit_rshd_success(hostname
, remuser
, locuser
, cmdbuf
); /* BSM */
1065 /* set the real (and effective) GID */
1066 if (setgid(pwd
->pw_gid
) == -1) {
1067 error("Invalid gid.\n");
1068 (void) pam_end(pamh
, PAM_ABORT
);
1073 * Initialize the supplementary group access list.
1075 if (strlen(locuser
) == 0) {
1076 error("No local user.\n");
1077 (void) pam_end(pamh
, PAM_ABORT
);
1080 if (initgroups(locuser
, pwd
->pw_gid
) == -1) {
1081 error("Initgroup failed.\n");
1082 (void) pam_end(pamh
, PAM_ABORT
);
1086 if ((v
= pam_setcred(pamh
, PAM_ESTABLISH_CRED
)) != PAM_SUCCESS
) {
1087 error("Insufficient credentials.\n");
1088 (void) pam_end(pamh
, v
);
1092 /* set the real (and effective) UID */
1093 if (setuid(pwd
->pw_uid
) == -1) {
1094 error("Invalid uid.\n");
1095 (void) pam_end(pamh
, PAM_ABORT
);
1099 /* Change directory only after becoming the appropriate user. */
1100 if (chdir(pwd
->pw_dir
) < 0) {
1102 if (krb5auth_flag
> 0) {
1103 syslog(LOG_ERR
, "Principal %s (%s@%s) for local user"
1104 " %s has no home directory.",
1105 kremuser
, remuser
, hostname
, locuser
);
1106 error("No remote directory.\n");
1110 error("No remote directory.\n");
1115 path
= (pwd
->pw_uid
== 0) ? rootpath
: userpath
;
1118 * Space for the following environment variables are dynamically
1119 * allocated because their lengths are not known before calling
1122 homedir_len
= strlen(pwd
->pw_dir
) + strlen(homestr
) + 1;
1123 shell_len
= strlen(pwd
->pw_shell
) + strlen(shellstr
) + 1;
1124 username_len
= strlen(pwd
->pw_name
) + strlen(userstr
) + 1;
1125 homedir
= (char *)malloc(homedir_len
);
1126 shell
= (char *)malloc(shell_len
);
1127 username
= (char *)malloc(username_len
);
1128 if (homedir
== NULL
|| shell
== NULL
|| username
== NULL
) {
1132 (void) snprintf(homedir
, homedir_len
, "%s%s", homestr
, pwd
->pw_dir
);
1133 (void) snprintf(shell
, shell_len
, "%s%s", shellstr
, pwd
->pw_shell
);
1134 (void) snprintf(username
, username_len
, "%s%s", userstr
, pwd
->pw_name
);
1136 /* Pass timezone to executed command. */
1137 if (tzenv
= getenv("TZ")) {
1138 tz_len
= strlen(tzenv
) + strlen(tzstr
) + 1;
1139 tz
= malloc(tz_len
);
1141 (void) snprintf(tz
, tz_len
, "%s%s", tzstr
, tzenv
);
1144 add_to_envinit(homedir
);
1145 add_to_envinit(shell
);
1146 add_to_envinit(path
);
1147 add_to_envinit(username
);
1150 if (krb5auth_flag
> 0) {
1155 * If we have KRB5CCNAME set, then copy into the child's
1156 * environment. This can't really have a fixed position
1157 * because `tz' may or may not be set.
1159 if (getenv("KRB5CCNAME")) {
1160 length
= (int)strlen(getenv("KRB5CCNAME")) +
1161 (int)strlen("KRB5CCNAME=") + 1;
1162 buffer
= (char *)malloc(length
);
1165 (void) snprintf(buffer
, length
, "KRB5CCNAME=%s",
1166 getenv("KRB5CCNAME"));
1167 add_to_envinit(buffer
);
1170 /* These two are covered by ADDRPAD */
1171 length
= strlen(inet_ntoa(localaddr
.sin_addr
)) + 1 +
1172 strlen("KRB5LOCALADDR=");
1173 (void) snprintf(local_addr
, length
, "KRB5LOCALADDR=%s",
1174 inet_ntoa(localaddr
.sin_addr
));
1175 add_to_envinit(local_addr
);
1177 length
= strlen(inet_ntoa(sin
->sin_addr
)) + 1 +
1178 strlen("KRB5REMOTEADDR=");
1179 (void) snprintf(remote_addr
, length
,
1180 "KRB5REMOTEADDR=%s", inet_ntoa(sin
->sin_addr
));
1181 add_to_envinit(remote_addr
);
1185 * If we do anything else, make sure there is
1186 * space in the array.
1188 for (cnt
= 0; cnt
< num_env
; cnt
++) {
1191 if (getenv(save_env
[cnt
])) {
1192 length
= (int)strlen(getenv(save_env
[cnt
])) +
1193 (int)strlen(save_env
[cnt
]) + 2;
1195 buf
= (char *)malloc(length
);
1197 (void) snprintf(buf
, length
, "%s=%s",
1199 getenv(save_env
[cnt
]));
1200 add_to_envinit(buf
);
1208 * add PAM environment variables set by modules
1209 * -- only allowed 16 (PAM_ENV_ELIM)
1210 * -- check to see if the environment variable is legal
1212 if ((pam_env
= pam_getenvlist(pamh
)) != 0) {
1213 while (pam_env
[idx
] != 0) {
1214 if (idx
< PAM_ENV_ELIM
&&
1215 legalenvvar(pam_env
[idx
])) {
1216 add_to_envinit(pam_env
[idx
]);
1222 (void) pam_end(pamh
, PAM_SUCCESS
);
1225 * Pick up locale environment variables, if any.
1228 while (*lenvp
!= NULL
) {
1231 for (index
= 0; localeenv
[index
] != NULL
; index
++)
1233 * locale_envmatch() returns 1 if
1234 * *lenvp is localenev[index] and valid.
1236 if (locale_envmatch(localeenv
[index
], *lenvp
)) {
1237 add_to_envinit(*lenvp
);
1244 cp
= strrchr(pwd
->pw_shell
, '/');
1250 * rdist has been moved to /usr/bin, so /usr/ucb/rdist might not
1251 * be present on a system. So if it doesn't exist we fall back
1252 * and try for it in /usr/bin. We take care to match the space
1253 * after the name because the only purpose of this is to protect
1254 * the internal call from old rdist's, not humans who type
1255 * "rsh foo /usr/ucb/rdist".
1257 #define RDIST_PROG_NAME "/usr/ucb/rdist -Server"
1258 if (strncmp(cmdbuf
, RDIST_PROG_NAME
, strlen(RDIST_PROG_NAME
)) == 0) {
1259 if (stat("/usr/ucb/rdist", &statb
) != 0) {
1260 (void) strncpy(cmdbuf
+ 5, "bin", 3);
1265 syslog(LOG_NOTICE
, "rshd: cmdbuf = %s", cmdbuf
);
1267 syslog(LOG_NOTICE
, "rshd: cmd to be exec'ed = %s",
1268 ((char *)cmdbuf
+ 3));
1271 if (do_encrypt
&& (strncmp(cmdbuf
, "-x ", 3) == 0)) {
1272 (void) execle(pwd
->pw_shell
, cp
, "-c", (char *)cmdbuf
+ 3,
1275 (void) execle(pwd
->pw_shell
, cp
, "-c", cmdbuf
, NULL
,
1279 perror(pwd
->pw_shell
);
1284 (void) pam_close_session(pamh
, 0);
1286 (void) pam_end(pamh
, PAM_ABORT
);
1291 getstr(fd
, buf
, cnt
, err
)
1300 if (read(fd
, &c
, 1) != 1)
1303 error("%s too long\n", err
);
1312 error(char *fmt
, ...)
1315 char buf
[RSHD_BUFSIZ
];
1319 (void) vsnprintf(&buf
[1], sizeof (buf
) - 1, fmt
, ap
);
1321 (void) write(STDERR_FILENO
, buf
, strlen(buf
));
1324 static char *illegal
[] = {
1340 * legalenvvar - can PAM modules insert this environmental variable?
1344 legalenvvar(char *s
)
1348 for (p
= illegal
; *p
; p
++)
1349 if (strncmp(s
, *p
, strlen(*p
)) == 0)
1352 if (s
[0] == 'L' && s
[1] == 'D' && s
[2] == '_')
1359 * Add a string to the environment of the new process.
1363 add_to_envinit(char *string
)
1366 * Reserve space for 2 * 8 = 16 environment entries initially which
1367 * should be enough to avoid reallocation of "envinit" in most cases.
1369 static int size
= 8;
1370 static int index
= 0;
1375 if ((envinit
== NULL
) || (index
== size
)) {
1377 envinit
= realloc(envinit
, (size
+ 1) * sizeof (char *));
1378 if (envinit
== NULL
) {
1384 envinit
[index
++] = string
;
1385 envinit
[index
] = NULL
;
1389 * Check if lenv and penv matches or not.
1392 locale_envmatch(char *lenv
, char *penv
)
1394 while ((*lenv
== *penv
) && (*lenv
!= '\0') && (*penv
!= '=')) {
1400 * '/' is eliminated for security reason.
1402 return ((*lenv
== '\0' && *penv
== '=' && *(penv
+ 1) != '/'));
1405 #ifndef KRB_SENDAUTH_VLEN
1406 #define KRB_SENDAUTH_VLEN 8 /* length for version strings */
1409 /* MUST be KRB_SENDAUTH_VLEN chars */
1410 #define KRB_SENDAUTH_VERS "AUTHV0.1"
1411 #define SIZEOF_INADDR sizeof (struct in_addr)
1413 static krb5_error_code
1414 recvauth(int netf
, int *valid_checksum
)
1416 krb5_auth_context auth_context
= NULL
;
1417 krb5_error_code status
;
1418 struct sockaddr_in laddr
;
1421 krb5_authenticator
*authenticator
;
1422 krb5_ticket
*ticket
;
1425 krb5_encrypt_block eblock
; /* eblock for encrypt/decrypt */
1427 krb5_data desoutbuf
;
1428 char des_inbuf
[2 * RSHD_BUFSIZ
];
1429 /* needs to be > largest read size */
1430 char des_outbuf
[2 * RSHD_BUFSIZ
+ 4];
1431 /* needs to be > largest write size */
1433 *valid_checksum
= 0;
1434 len
= sizeof (laddr
);
1436 if (getsockname(netf
, (struct sockaddr
*)&laddr
, &len
)) {
1440 if (status
= krb5_auth_con_init(bsd_context
, &auth_context
))
1443 if (status
= krb5_auth_con_genaddrs(bsd_context
, auth_context
, netf
,
1444 KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR
))
1447 status
= krb5_auth_con_getrcache(bsd_context
, auth_context
, &rcache
);
1452 krb5_principal server
;
1454 status
= krb5_sname_to_principal(bsd_context
, 0, 0,
1455 KRB5_NT_SRV_HST
, &server
);
1459 status
= krb5_get_server_rcache(bsd_context
,
1460 krb5_princ_component(bsd_context
, server
, 0),
1462 krb5_free_principal(bsd_context
, server
);
1466 status
= krb5_auth_con_setrcache(bsd_context
, auth_context
,
1472 status
= krb5_recvauth_version(bsd_context
, &auth_context
, &netf
,
1473 NULL
, /* Specify daemon principal */
1475 keytab
, /* normally NULL to use v5srvtab */
1476 &ticket
, /* return ticket */
1477 &version
); /* application version string */
1481 getstr(netf
, locuser
, sizeof (locuser
), "locuser");
1482 getstr(netf
, cmdbuf
, sizeof (cmdbuf
), "command");
1483 getstr(netf
, remuser
, sizeof (locuser
), "remuser");
1486 getstr(netf
, locuser
, sizeof (locuser
), "locuser");
1487 getstr(netf
, cmdbuf
, sizeof (cmdbuf
), "command");
1491 kcmd_protocol
= KCMD_UNKNOWN_PROTOCOL
;
1492 if (version
.length
!= 9 || version
.data
== NULL
) {
1493 syslog(LOG_ERR
, "bad application version length");
1494 error(gettext("bad application version length\n"));
1497 if (strncmp(version
.data
, "KCMDV0.1", 9) == 0) {
1498 kcmd_protocol
= KCMD_OLD_PROTOCOL
;
1499 } else if (strncmp(version
.data
, "KCMDV0.2", 9) == 0) {
1500 kcmd_protocol
= KCMD_NEW_PROTOCOL
;
1502 syslog(LOG_ERR
, "Unrecognized KCMD protocol (%s)",
1503 (char *)version
.data
);
1504 error(gettext("Unrecognized KCMD protocol (%s)"),
1505 (char *)version
.data
);
1508 getstr(netf
, remuser
, sizeof (locuser
), "remuser");
1510 if ((status
= krb5_unparse_name(bsd_context
, ticket
->enc_part2
->client
,
1514 if ((status
= krb5_copy_principal(bsd_context
,
1515 ticket
->enc_part2
->client
, &client
)))
1519 if (checksum_required
&& (kcmd_protocol
== KCMD_OLD_PROTOCOL
)) {
1520 if ((status
= krb5_auth_con_getauthenticator(bsd_context
,
1521 auth_context
, &authenticator
)))
1524 if (authenticator
->checksum
&& checksum_required
) {
1525 struct sockaddr_in adr
;
1526 int adr_length
= sizeof (adr
);
1527 int chksumsize
= strlen(cmdbuf
) + strlen(locuser
) + 32;
1531 char *chksumbuf
= (char *)malloc(chksumsize
);
1535 if (getsockname(netf
, (struct sockaddr
*)&adr
,
1539 (void) snprintf(chksumbuf
, chksumsize
, "%u:",
1540 ntohs(adr
.sin_port
));
1541 if (strlcat(chksumbuf
, cmdbuf
,
1542 chksumsize
) >= chksumsize
) {
1543 syslog(LOG_ERR
, "cmd buffer too long.");
1547 if (strlcat(chksumbuf
, locuser
,
1548 chksumsize
) >= chksumsize
) {
1549 syslog(LOG_ERR
, "locuser too long.");
1554 input
.data
= chksumbuf
;
1555 input
.length
= strlen(chksumbuf
);
1556 key
.magic
= ticket
->enc_part2
->session
->magic
;
1557 key
.enctype
= ticket
->enc_part2
->session
->enctype
;
1558 key
.contents
= ticket
->enc_part2
->session
->contents
;
1559 key
.length
= ticket
->enc_part2
->session
->length
;
1561 status
= krb5_c_verify_checksum(bsd_context
,
1562 &key
, 0, &input
, authenticator
->checksum
,
1563 (unsigned int *)valid_checksum
);
1565 if (status
== 0 && *valid_checksum
== 0)
1566 status
= KRB5KRB_AP_ERR_BAD_INTEGRITY
;
1569 krb5_xfree(chksumbuf
);
1571 krb5_free_authenticator(bsd_context
,
1576 krb5_free_authenticator(bsd_context
, authenticator
);
1580 if ((strncmp(cmdbuf
, "-x ", 3) == 0)) {
1581 if (krb5_privacy_allowed()) {
1584 syslog(LOG_ERR
, "rshd: Encryption not supported");
1585 error("rshd: Encryption not supported. \n");
1589 status
= krb5_auth_con_getremotesubkey(bsd_context
,
1593 syslog(LOG_ERR
, "Error getting KRB5 session subkey");
1594 error(gettext("Error getting KRB5 session subkey"));
1598 * The "new" protocol requires that a subkey be sent.
1600 if (sessionkey
== NULL
&& kcmd_protocol
== KCMD_NEW_PROTOCOL
) {
1601 syslog(LOG_ERR
, "No KRB5 session subkey sent");
1602 error(gettext("No KRB5 session subkey sent"));
1606 * The "old" protocol does not permit an authenticator subkey.
1607 * The key is taken from the ticket instead (see below).
1609 if (sessionkey
!= NULL
&& kcmd_protocol
== KCMD_OLD_PROTOCOL
) {
1610 syslog(LOG_ERR
, "KRB5 session subkey not permitted "
1611 "with old KCMD protocol");
1612 error(gettext("KRB5 session subkey not permitted "
1613 "with old KCMD protocol"));
1617 * If no key at this point, use the session key from
1620 if (sessionkey
== NULL
) {
1622 * Save the session key so we can configure the crypto
1625 status
= krb5_copy_keyblock(bsd_context
,
1626 ticket
->enc_part2
->session
,
1629 syslog(LOG_ERR
, "krb5_copy_keyblock failed");
1630 error(gettext("krb5_copy_keyblock failed"));
1635 * If session key still cannot be found, we must
1636 * exit because encryption is required here
1637 * when encr_flag (-x) is set.
1639 if (sessionkey
== NULL
) {
1640 syslog(LOG_ERR
, "Could not find an encryption key");
1641 error(gettext("Could not find an encryption key"));
1646 * Initialize parameters/buffers for desread & deswrite here.
1648 desinbuf
.data
= des_inbuf
;
1649 desoutbuf
.data
= des_outbuf
;
1650 desinbuf
.length
= sizeof (des_inbuf
);
1651 desoutbuf
.length
= sizeof (des_outbuf
);
1653 eblock
.crypto_entry
= sessionkey
->enctype
;
1654 eblock
.key
= (krb5_keyblock
*)sessionkey
;
1656 init_encrypt(do_encrypt
, bsd_context
, kcmd_protocol
,
1657 &desinbuf
, &desoutbuf
, SERVER
, &eblock
);
1660 ticket
->enc_part2
->session
= 0;
1662 if ((status
= krb5_read_message(bsd_context
, (krb5_pointer
) & netf
,
1664 error(gettext("Error reading message: %s\n"),
1665 error_message(status
));
1670 krb5_creds
**creds
= NULL
;
1672 /* Forwarding being done, read creds */
1673 if ((status
= krb5_rd_cred(bsd_context
,
1674 auth_context
, &inbuf
, &creds
,
1676 error("Can't get forwarded credentials: %s\n",
1677 error_message(status
));
1681 /* Store the forwarded creds in the ccache */
1682 if ((status
= store_forw_creds(bsd_context
,
1683 creds
, ticket
, locuser
,
1685 error("Can't store forwarded credentials: %s\n",
1686 error_message(status
));
1689 krb5_free_creds(bsd_context
, *creds
);
1692 krb5_free_ticket(bsd_context
, ticket
);
1699 (void) fprintf(stderr
, gettext("%s: rshd [-k5eciU] "
1700 "[-P path] [-M realm] [-s tos] "
1704 "[-S keytab]"), gettext("usage"));
1706 syslog(LOG_ERR
, "%s: rshd [-k5eciU] [-P path] [-M realm] [-s tos] "
1710 "[-S keytab]", gettext("usage"));