1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1983-1989 AT&T */
28 /* All Rights Reserved */
29
30 /*
31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 * under license from the Regents of the University of California.
33 */
34
35 #define _FILE_OFFSET_BITS 64
36
37 /*
38 * remote shell server:
39 * remuser\0
40 * locuser\0
41 * command\0
42 * data
43 */
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>
49 #include <sys/time.h>
50 #include <sys/stat.h>
51 #include <sys/file.h>
52 #include <sys/select.h>
53
54 #include <netinet/in.h>
55
56 #include <arpa/inet.h>
57
58 #include <unistd.h>
59 #include <string.h>
60 #include <stdio.h>
61 #include <stdarg.h>
62 #include <errno.h>
63 #include <pwd.h>
64 #include <grp.h>
65 #include <signal.h>
66 #include <netdb.h>
67 #include <syslog.h>
68 #include <fcntl.h>
69 #include <ctype.h>
70 #include <locale.h>
71
72 #include <sys/resource.h>
73 #include <sys/filio.h>
74 #include <shadow.h>
75 #include <stdlib.h>
76
77 #include <security/pam_appl.h>
78 #include <deflt.h>
79
80 #include <k5-int.h>
81 #include <krb5_repository.h>
82 #include <com_err.h>
83 #include <kcmd.h>
84
85 #include <addr_match.h>
86 #include <store_forw_creds.h>
87
88 #ifndef NCARGS
89 #define NCARGS 5120
90 #endif /* !NCARGS */
91
92 static void error(char *, ...);
93 static void doit(int, struct sockaddr_storage *, char **);
94 static void getstr(int, char *, int, char *);
95
96 static int legalenvvar(char *);
97 static void add_to_envinit(char *);
98 static int locale_envmatch(char *, char *);
99
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);
105
106 static int do_encrypt = 0;
107 static pam_handle_t *pamh;
108
109 /*
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
116 * permission.
117 *
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
130 */
131
132 #define ARGSTR "ek5ciUD:M:S:L:?:"
133 #define RSHD_BUFSIZ (50 * 1024)
134
135 static krb5_context bsd_context;
136 static krb5_keytab keytab = NULL;
137 static krb5_ccache ccache = NULL;
138 static krb5_keyblock *sessionkey = NULL;
139
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;
144
145 #ifdef DEBUG
146 static int debug_port = 0;
147 #endif /* DEBUG */
148
149 /*
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.
157 */
158
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;
164
165 /*
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.
169 */
170 #define MAXENV 4
171 static char *save_env[MAXENV];
172 static int num_env = 0;
173
174 static void usage(void);
175 static krb5_error_code recvauth(int, int *);
176
177 /*ARGSUSED*/
178 int
main(int argc,char ** argv,char ** renvp)179 main(int argc, char **argv, char **renvp)
180 {
181 struct linger linger;
182 int on = 1, fromlen;
183 struct sockaddr_storage from;
184 int fd = 0;
185
186 extern int opterr, optind;
187 extern char *optarg;
188 int ch;
189 int tos = -1;
190 krb5_error_code status;
191
192 openlog("rsh", LOG_PID | LOG_ODELAY, LOG_DAEMON);
193 (void) audit_rshd_setup(); /* BSM */
194 fromlen = sizeof (from);
195
196 (void) setlocale(LC_ALL, "");
197
198 /*
199 * Analyze parameters.
200 */
201 opterr = 0;
202 while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
203 switch (ch) {
204 case '5':
205 case 'k':
206 auth_ok |= AUTH_KRB5;
207 krb5auth_flag++;
208 break;
209
210 case 'c':
211 checksum_required = 1;
212 krb5auth_flag++;
213 break;
214 case 'i':
215 checksum_ignored = 1;
216 krb5auth_flag++;
217 break;
218
219 case 'e':
220 require_encrypt = 1;
221 krb5auth_flag++;
222 break;
223 #ifdef DEBUG
224 case 'D':
225 debug_port = atoi(optarg);
226 break;
227 #endif /* DEBUG */
228 case 'U':
229 resolve_hostname = 1;
230 break;
231
232 case 'M':
233 (void) krb5_set_default_realm(bsd_context, optarg);
234 krb5auth_flag++;
235 break;
236
237 case 'S':
238 if ((status = krb5_kt_resolve(bsd_context, optarg,
239 &keytab))) {
240 com_err("rsh", status,
241 gettext("while resolving "
242 "srvtab file %s"), optarg);
243 exit(2);
244 }
245 krb5auth_flag++;
246 break;
247
248 case 's':
249 if (optarg == NULL || ((tos = atoi(optarg)) < 0) ||
250 (tos > 255)) {
251 syslog(LOG_ERR, "rshd: illegal tos value: "
252 "%s\n", optarg);
253 }
254 break;
255
256 case 'L':
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"));
262 exit(2);
263 }
264 } else {
265 (void) fprintf(stderr, gettext("rshd: Only %d"
266 " -L arguments allowed\n"),
267 MAXENV);
268 exit(2);
269 }
270 break;
271
272 case '?':
273 default:
274 usage();
275 exit(1);
276 break;
277 }
278
279 if (optind == 0) {
280 usage();
281 exit(1);
282 }
283 argc -= optind;
284 argv += optind;
285
286 if (krb5auth_flag > 0) {
287 status = krb5_init_context(&bsd_context);
288 if (status) {
289 syslog(LOG_ERR, "Error initializing krb5: %s",
290 error_message(status));
291 exit(1);
292 }
293 }
294
295 if (!checksum_required && !checksum_ignored)
296 checksum_ignored = 1;
297
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");
304 exit(1);
305 }
306
307 #ifdef DEBUG
308 if (debug_port) {
309 int s;
310 struct sockaddr_in sin;
311
312 if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
313 fprintf(stderr, gettext("Error in socket: %s\n"),
314 strerror(errno));
315 exit(2);
316 }
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;
321
322 (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
323 (char *)&on, sizeof (on));
324
325 if ((bind(s, (struct sockaddr *)&sin, sizeof (sin))) < 0) {
326 (void) fprintf(stderr, gettext("Error in bind: %s\n"),
327 strerror(errno));
328 exit(2);
329 }
330 if ((listen(s, 5)) < 0) {
331 (void) fprintf(stderr, gettext("Error in listen: %s\n"),
332 strerror(errno));
333 exit(2);
334 }
335 if ((fd = accept(s, (struct sockaddr *)&from,
336 &fromlen)) < 0) {
337 (void) fprintf(stderr, gettext("Error in accept: %s\n"),
338 strerror(errno));
339 exit(2);
340 }
341 (void) close(s);
342 }
343 else
344 #endif /* DEBUG */
345 {
346 if (getpeername(STDIN_FILENO, (struct sockaddr *)&from,
347 (socklen_t *)&fromlen) < 0) {
348 (void) fprintf(stderr, "rshd: ");
349 perror("getpeername");
350 _exit(1);
351 }
352 fd = STDIN_FILENO;
353 }
354
355 if (audit_settid(fd) != 0) {
356 perror("settid");
357 exit(1);
358 }
359
360 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
361 sizeof (on)) < 0)
362 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
363 linger.l_onoff = 1;
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");
368
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");
373 }
374
375 doit(dup(fd), &from, renvp);
376 return (0);
377 }
378
379 /*
380 * locale environments to be passed to shells.
381 */
382 static char *localeenv[] = {
383 "LANG",
384 "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE",
385 "LC_MONETARY", "LC_MESSAGES", "LC_ALL", NULL};
386
387 /*
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().
391 */
392 static char *homedir; /* "HOME=" */
393 static char *shell; /* "SHELL=" */
394 static char *username; /* "USER=" */
395 static char *tz; /* "TZ=" */
396
397 static char homestr[] = "HOME=";
398 static char shellstr[] = "SHELL=";
399 static char userstr[] = "USER=";
400 static char tzstr[] = "TZ=";
401
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 */
405
406 /*
407 * See PSARC opinion 1992/025
408 */
409 static char userpath[] = "PATH=/usr/bin:";
410 static char rootpath[] = "PATH=/usr/sbin:/usr/bin";
411
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];
416
417 #define KRB5_RECVAUTH_V5 5
418 #define SIZEOF_INADDR sizeof (struct in_addr)
419
420 #define MAX_REPOSITORY_LEN 255
421 static char repository[MAX_REPOSITORY_LEN];
422
423 static char *kremuser;
424 static krb5_principal client = NULL;
425
426 static char remote_addr[64];
427 static char local_addr[64];
428
429 #define _PATH_DEFAULT_LOGIN "/etc/default/login"
430
431 static void
doit(int f,struct sockaddr_storage * fromp,char ** renvp)432 doit(int f, struct sockaddr_storage *fromp, char **renvp)
433 {
434 char *cp;
435
436 struct passwd *pwd;
437 char *path;
438 char *tzenv;
439 struct spwd *shpwd;
440 struct stat statb;
441 char **lenvp;
442
443 krb5_error_code status;
444 int valid_checksum;
445 int cnt;
446 int sin_len;
447 struct sockaddr_in localaddr;
448
449 int s;
450 in_port_t port;
451 pid_t pid;
452 int pv[2], pw[2], px[2], cc;
453 char buf[RSHD_BUFSIZ];
454 char sig;
455 int one = 1;
456 int v = 0;
457 int err = 0;
458 int idx = 0;
459 char **pam_env;
460 char abuf[INET6_ADDRSTRLEN];
461 struct sockaddr_in *sin;
462 struct sockaddr_in6 *sin6;
463 int fromplen;
464 int homedir_len, shell_len, username_len, tz_len;
465 int no_name;
466 boolean_t bad_port;
467 int netf = 0;
468
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);
477
478 #ifdef DEBUG
479 {
480 int t = open("/dev/tty", 2);
481
482 if (t >= 0) {
483 (void) setsid();
484 (void) close(t);
485 }
486 }
487 #endif
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);
496 } else {
497 syslog(LOG_ERR, "wrong address family\n");
498 exit(1);
499 }
500
501 if (fromp->ss_family == AF_INET6) {
502 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
503 struct in_addr ipv4_addr;
504
505 IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &ipv4_addr);
506 (void) inet_ntop(AF_INET, &ipv4_addr, abuf,
507 sizeof (abuf));
508 } else {
509 (void) inet_ntop(AF_INET6, &sin6->sin6_addr, abuf,
510 sizeof (abuf));
511 }
512 } else if (fromp->ss_family == AF_INET) {
513 (void) inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof (abuf));
514 }
515
516 sin_len = sizeof (struct sockaddr_in);
517 if (getsockname(f, (struct sockaddr *)&localaddr, &sin_len) < 0) {
518 perror("getsockname");
519 exit(1);
520 }
521
522 netf = f;
523
524 bad_port = (port >= IPPORT_RESERVED ||
525 port < (uint_t)(IPPORT_RESERVED/2));
526
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);
530
531 if (bad_port || no_name != 0) {
532 /*
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.
537 */
538 (void) strlcpy(hostname, abuf, sizeof (hostname));
539 }
540
541 if (no_name != 0) {
542 /*
543 * If the '-U' option was given on the cmd line,
544 * we must be able to lookup the hostname
545 */
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");
550 exit(1);
551 }
552 } else {
553 /*
554 * Even if getnameinfo() succeeded, we still have to check
555 * for spoofing.
556 */
557 check_address("rshd", fromp, sin, sin6, abuf, hostname,
558 sizeof (hostname));
559 }
560
561 if (!krb5auth_flag && bad_port) {
562 if (no_name)
563 syslog(LOG_NOTICE, "connection from %s - "
564 "bad port\n", abuf);
565 else
566 syslog(LOG_NOTICE, "connection from %s (%s) - "
567 "bad port\n", hostname, abuf);
568 exit(1);
569 }
570
571 (void) alarm(60);
572 port = 0;
573 for (;;) {
574 char c;
575 if ((cc = read(f, &c, 1)) != 1) {
576 if (cc < 0)
577 syslog(LOG_NOTICE, "read: %m");
578 (void) shutdown(f, 1+1);
579 exit(1);
580 }
581 if (c == 0)
582 break;
583 port = port * 10 + c - '0';
584 }
585 (void) alarm(0);
586 if (port != 0) {
587 int lport = 0;
588 struct sockaddr_storage ctl_addr;
589 int addrlen;
590
591 (void) memset(&ctl_addr, 0, sizeof (ctl_addr));
592 addrlen = sizeof (ctl_addr);
593 if (getsockname(f, (struct sockaddr *)&ctl_addr,
594 &addrlen) < 0) {
595 syslog(LOG_ERR, "getsockname: %m");
596 exit(1);
597 }
598 get_port:
599 /*
600 * 0 means that rresvport_addr() will bind to a port in
601 * the anonymous priviledged port range.
602 */
603 if (krb5auth_flag) {
604 /*
605 * Kerberos does not support IPv6 yet.
606 */
607 lport = IPPORT_RESERVED - 1;
608 }
609 s = rresvport_addr(&lport, &ctl_addr);
610
611 if (s < 0) {
612 syslog(LOG_ERR, "can't get stderr port: %m");
613 exit(1);
614 }
615 if (!krb5auth_flag && (port >= IPPORT_RESERVED)) {
616 syslog(LOG_ERR, "2nd port not reserved\n");
617 exit(1);
618 }
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);
623 }
624 if (connect(s, (struct sockaddr *)fromp, fromplen) < 0) {
625 if (errno == EADDRINUSE) {
626 (void) close(s);
627 goto get_port;
628 }
629 syslog(LOG_INFO, "connect second port: %m");
630 exit(1);
631 }
632 }
633 (void) dup2(f, 0);
634 (void) dup2(f, 1);
635 (void) dup2(f, 2);
636
637 #ifdef DEBUG
638 syslog(LOG_NOTICE, "rshd: Client hostname = %s", hostname);
639 if (debug_port)
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");
643 else
644 syslog(LOG_NOTICE, "rshd: Kerberos mode is OFF");
645 #endif /* DEBUG */
646
647 if (krb5auth_flag > 0) {
648 if ((status = recvauth(f, &valid_checksum))) {
649 syslog(LOG_ERR, gettext("Kerberos Authentication "
650 "failed \n"));
651 error("Authentication failed: %s\n",
652 error_message(status));
653 (void) audit_rshd_fail("Kerberos Authentication "
654 "failed", hostname, remuser, locuser, cmdbuf);
655 exit(1);
656 }
657
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 */
667 goto signout;
668 }
669
670 /*
671 * Authentication has succeeded, we now need
672 * to check authorization.
673 *
674 * krb5_kuserok returns 1 if OK.
675 */
676 if (client && krb5_kuserok(bsd_context, client, locuser)) {
677 auth_sent |= AUTH_KRB5;
678 } else {
679 syslog(LOG_ERR, "Principal %s (%s@%s) for local user "
680 "%s failed krb5_kuserok.\n",
681 kremuser, remuser, hostname, locuser);
682 }
683 } else {
684 getstr(netf, remuser, sizeof (remuser), "remuser");
685 getstr(netf, locuser, sizeof (locuser), "locuser");
686 getstr(netf, cmdbuf, sizeof (cmdbuf), "command");
687 }
688
689 #ifdef DEBUG
690 syslog(LOG_NOTICE, "rshd: locuser = %s, remuser = %s, cmdbuf = %s",
691 locuser, remuser, cmdbuf);
692 #endif /* DEBUG */
693
694 /*
695 * Note that there is no rsh conv functions at present.
696 */
697 if (krb5auth_flag > 0) {
698 if ((err = pam_start("krsh", locuser, NULL, &pamh))
699 != PAM_SUCCESS) {
700 syslog(LOG_ERR, "pam_start() failed: %s\n",
701 pam_strerror(0, err));
702 exit(1);
703 }
704 }
705 else
706 {
707 if ((err = pam_start("rsh", locuser, NULL, &pamh))
708 != PAM_SUCCESS) {
709 syslog(LOG_ERR, "pam_start() failed: %s\n",
710 pam_strerror(0, err));
711 exit(1);
712 }
713 }
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));
717 exit(1);
718 }
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));
722 exit(1);
723 }
724
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,
731 hostname, locuser);
732 error("permission denied.\n");
733 (void) audit_rshd_fail("Login incorrect", hostname,
734 remuser, locuser, cmdbuf); /* BSM */
735 exit(1);
736 }
737
738 if (krb5auth_flag > 0) {
739 (void) snprintf(repository, sizeof (repository),
740 KRB5_REPOSITORY_NAME);
741 /*
742 * We currently only support special handling of the
743 * KRB5 PAM repository
744 */
745 if (strlen(locuser) != 0) {
746 krb5_repository_data_t krb5_data;
747 pam_repository_t pam_rep_data;
748
749 krb5_data.principal = locuser;
750 krb5_data.flags = SUNW_PAM_KRB5_ALREADY_AUTHENTICATED;
751
752 pam_rep_data.type = repository;
753 pam_rep_data.scope = (void *)&krb5_data;
754 pam_rep_data.scope_len = sizeof (krb5_data);
755
756 (void) pam_set_item(pamh, PAM_REPOSITORY,
757 (void *)&pam_rep_data);
758 }
759 }
760
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);
768 exit(1);
769 }
770 } else {
771 int flags;
772 char *p;
773 /*
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
777 * passwords.
778 */
779 if (defopen(_PATH_DEFAULT_LOGIN) == 0) {
780 flags = defcntl(DC_GETFLAGS, 0);
781 TURNOFF(flags, DC_CASE);
782 (void) defcntl(DC_SETFLAGS, flags);
783
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);
795 exit(1);
796 }
797 (void) defopen(NULL);
798 }
799 /*
800 * /etc/default/login not found or PASSREQ not set
801 * to YES. Allow logins without passwords.
802 */
803 }
804 }
805
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 */
811 goto signout;
812 }
813
814 if (!(auth_ok & auth_sent)) {
815 if (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 */
822 goto signout;
823 } else {
824 error("Permission denied.\n");
825 (void) audit_rshd_fail("Permission denied.",
826 hostname, remuser, locuser, cmdbuf);
827 /* BSM */
828 goto signout;
829 }
830 }
831
832
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);
837 goto signout;
838 }
839
840 /* Log access to account */
841 if (pwd && (pwd->pw_uid == 0)) {
842 syslog(LOG_NOTICE, "Executing %s for user %s (%s@%s)"
843 " as ROOT", cmdbuf,
844 kremuser, remuser, hostname);
845 }
846 }
847
848 if ((v = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
849 switch (v) {
850 case PAM_NEW_AUTHTOK_REQD:
851 error("password expired\n");
852 (void) audit_rshd_fail("Password expired", hostname,
853 remuser, locuser, cmdbuf); /* BSM */
854 break;
855 case PAM_PERM_DENIED:
856 error("account expired\n");
857 (void) audit_rshd_fail("Account expired", hostname,
858 remuser, locuser, cmdbuf); /* BSM */
859 break;
860 case PAM_AUTHTOK_EXPIRED:
861 error("password expired\n");
862 (void) audit_rshd_fail("Password expired", hostname,
863 remuser, locuser, cmdbuf); /* BSM */
864 break;
865 default:
866 error("login incorrect\n");
867 (void) audit_rshd_fail("Permission denied", hostname,
868 remuser, locuser, cmdbuf); /* BSM */
869 break;
870 }
871 (void) pam_end(pamh, PAM_ABORT);
872 exit(1);
873 }
874
875 if (chdir(pwd->pw_dir) < 0) {
876 (void) chdir("/");
877 #ifdef notdef
878 error("No remote directory.\n");
879
880 exit(1);
881 #endif
882 }
883
884 /*
885 * XXX There is no session management currently being done
886 */
887
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);
893 exit(1);
894 }
895 if (do_encrypt) {
896 if (pipe(pw) < 0) {
897 error("Can't make pipe 2.\n");
898 (void) pam_end(pamh, PAM_ABORT);
899 exit(1);
900 }
901 if (pipe(px) < 0) {
902 error("Can't make pipe 3.\n");
903 (void) pam_end(pamh, PAM_ABORT);
904 exit(1);
905 }
906 }
907 pid = fork();
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);
912 exit(1);
913 }
914 if (pid) {
915 fd_set ready;
916 fd_set readfrom;
917
918 (void) close(STDIN_FILENO);
919 (void) close(STDOUT_FILENO);
920 (void) close(STDERR_FILENO);
921 (void) close(pv[1]);
922 if (do_encrypt) {
923 (void) close(pw[1]);
924 (void) close(px[0]);
925 } else {
926 (void) close(f);
927 }
928
929 (void) FD_ZERO(&readfrom);
930
931 FD_SET(pv[0], &readfrom);
932 if (do_encrypt) {
933 FD_SET(pw[0], &readfrom);
934 FD_SET(f, &readfrom);
935 }
936 if (port)
937 FD_SET(s, &readfrom);
938
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");
945 if (do_encrypt &&
946 ioctl(pw[0], FIONBIO, (char *)&one) == -1)
947 syslog(LOG_INFO, "ioctl FIONBIO: %m");
948 do {
949 ready = readfrom;
950 if (select(FD_SETSIZE, &ready, NULL,
951 NULL, NULL) < 0) {
952 if (errno == EINTR) {
953 continue;
954 } else {
955 break;
956 }
957 }
958 /*
959 * Read from child stderr, write to net
960 */
961 if (port && FD_ISSET(pv[0], &ready)) {
962 errno = 0;
963 cc = read(pv[0], buf, sizeof (buf));
964 if (cc <= 0) {
965 (void) shutdown(s, 2);
966 FD_CLR(pv[0], &readfrom);
967 } else {
968 (void) deswrite(s, buf, cc, 1);
969 }
970 }
971 /*
972 * Read from alternate channel, signal child
973 */
974 if (port && FD_ISSET(s, &ready)) {
975 if ((int)desread(s, &sig, 1, 1) <= 0)
976 FD_CLR(s, &readfrom);
977 else
978 (void) killpg(pid, sig);
979 }
980 /*
981 * Read from child stdout, write to net
982 */
983 if (do_encrypt && FD_ISSET(pw[0], &ready)) {
984 errno = 0;
985 cc = read(pw[0], buf, sizeof (buf));
986 if (cc <= 0) {
987 (void) shutdown(f, 2);
988 FD_CLR(pw[0], &readfrom);
989 } else {
990 (void) deswrite(f, buf, cc, 0);
991 }
992 }
993 /*
994 * Read from the net, write to child stdin
995 */
996 if (do_encrypt && FD_ISSET(f, &ready)) {
997 errno = 0;
998 cc = desread(f, buf, sizeof (buf), 0);
999 if (cc <= 0) {
1000 (void) close(px[1]);
1001 FD_CLR(f, &readfrom);
1002 } else {
1003 int wcc;
1004 wcc = write(px[1], buf, cc);
1005 if (wcc == -1) {
1006 /*
1007 * pipe closed,
1008 * don't read any
1009 * more
1010 *
1011 * might check for
1012 * EPIPE
1013 */
1014 (void) close(px[1]);
1015 FD_CLR(f, &readfrom);
1016 } else if (wcc != cc) {
1017 /* CSTYLED */
1018 syslog(LOG_INFO, gettext("only wrote %d/%d to child"),
1019 wcc, cc);
1020 }
1021 }
1022 }
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)));
1027 #ifdef DEBUG
1028 syslog(LOG_INFO, "Shell process completed.");
1029 #endif /* DEBUG */
1030 if (ccache)
1031 (void) pam_close_session(pamh, 0);
1032 (void) pam_end(pamh, PAM_SUCCESS);
1033
1034 exit(0);
1035 } /* End of Parent block */
1036
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]);
1041 if (port)
1042 (void) close(s);
1043 if (do_encrypt) {
1044 (void) close(f);
1045 (void) close(pw[0]);
1046 (void) close(px[1]);
1047
1048 (void) dup2(px[0], 0);
1049 (void) dup2(pw[1], 1);
1050
1051 (void) close(px[0]);
1052 (void) close(pw[1]);
1053 }
1054 }
1055
1056 if (*pwd->pw_shell == '\0')
1057 pwd->pw_shell = "/bin/sh";
1058 if (!do_encrypt)
1059 (void) close(f);
1060 /*
1061 * write audit record before making uid switch
1062 */
1063 (void) audit_rshd_success(hostname, remuser, locuser, cmdbuf); /* BSM */
1064
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);
1069 exit(1);
1070 }
1071
1072 /*
1073 * Initialize the supplementary group access list.
1074 */
1075 if (strlen(locuser) == 0) {
1076 error("No local user.\n");
1077 (void) pam_end(pamh, PAM_ABORT);
1078 exit(1);
1079 }
1080 if (initgroups(locuser, pwd->pw_gid) == -1) {
1081 error("Initgroup failed.\n");
1082 (void) pam_end(pamh, PAM_ABORT);
1083 exit(1);
1084 }
1085
1086 if ((v = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
1087 error("Insufficient credentials.\n");
1088 (void) pam_end(pamh, v);
1089 exit(1);
1090 }
1091
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);
1096 exit(1);
1097 }
1098
1099 /* Change directory only after becoming the appropriate user. */
1100 if (chdir(pwd->pw_dir) < 0) {
1101 (void) chdir("/");
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");
1107 goto signout;
1108 }
1109 #ifdef notdef
1110 error("No remote directory.\n");
1111 exit(1);
1112 #endif
1113 }
1114
1115 path = (pwd->pw_uid == 0) ? rootpath : userpath;
1116
1117 /*
1118 * Space for the following environment variables are dynamically
1119 * allocated because their lengths are not known before calling
1120 * getpwnam().
1121 */
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) {
1129 perror("malloc");
1130 exit(1);
1131 }
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);
1135
1136 /* Pass timezone to executed command. */
1137 if (tzenv = getenv("TZ")) {
1138 tz_len = strlen(tzenv) + strlen(tzstr) + 1;
1139 tz = malloc(tz_len);
1140 if (tz != NULL)
1141 (void) snprintf(tz, tz_len, "%s%s", tzstr, tzenv);
1142 }
1143
1144 add_to_envinit(homedir);
1145 add_to_envinit(shell);
1146 add_to_envinit(path);
1147 add_to_envinit(username);
1148 add_to_envinit(tz);
1149
1150 if (krb5auth_flag > 0) {
1151 int length;
1152 char *buffer;
1153
1154 /*
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.
1158 */
1159 if (getenv("KRB5CCNAME")) {
1160 length = (int)strlen(getenv("KRB5CCNAME")) +
1161 (int)strlen("KRB5CCNAME=") + 1;
1162 buffer = (char *)malloc(length);
1163
1164 if (buffer) {
1165 (void) snprintf(buffer, length, "KRB5CCNAME=%s",
1166 getenv("KRB5CCNAME"));
1167 add_to_envinit(buffer);
1168 }
1169 } {
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);
1176
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);
1182 }
1183
1184 /*
1185 * If we do anything else, make sure there is
1186 * space in the array.
1187 */
1188 for (cnt = 0; cnt < num_env; cnt++) {
1189 char *buf;
1190
1191 if (getenv(save_env[cnt])) {
1192 length = (int)strlen(getenv(save_env[cnt])) +
1193 (int)strlen(save_env[cnt]) + 2;
1194
1195 buf = (char *)malloc(length);
1196 if (buf) {
1197 (void) snprintf(buf, length, "%s=%s",
1198 save_env[cnt],
1199 getenv(save_env[cnt]));
1200 add_to_envinit(buf);
1201 }
1202 }
1203 }
1204
1205 }
1206
1207 /*
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
1211 */
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]);
1217 }
1218 idx++;
1219 }
1220 }
1221
1222 (void) pam_end(pamh, PAM_SUCCESS);
1223
1224 /*
1225 * Pick up locale environment variables, if any.
1226 */
1227 lenvp = renvp;
1228 while (*lenvp != NULL) {
1229 int index;
1230
1231 for (index = 0; localeenv[index] != NULL; index++)
1232 /*
1233 * locale_envmatch() returns 1 if
1234 * *lenvp is localenev[index] and valid.
1235 */
1236 if (locale_envmatch(localeenv[index], *lenvp)) {
1237 add_to_envinit(*lenvp);
1238 break;
1239 }
1240
1241 lenvp++;
1242 }
1243
1244 cp = strrchr(pwd->pw_shell, '/');
1245 if (cp != NULL)
1246 cp++;
1247 else
1248 cp = pwd->pw_shell;
1249 /*
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".
1256 */
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);
1261 }
1262 }
1263
1264 #ifdef DEBUG
1265 syslog(LOG_NOTICE, "rshd: cmdbuf = %s", cmdbuf);
1266 if (do_encrypt)
1267 syslog(LOG_NOTICE, "rshd: cmd to be exec'ed = %s",
1268 ((char *)cmdbuf + 3));
1269 #endif /* DEBUG */
1270
1271 if (do_encrypt && (strncmp(cmdbuf, "-x ", 3) == 0)) {
1272 (void) execle(pwd->pw_shell, cp, "-c", (char *)cmdbuf + 3,
1273 NULL, envinit);
1274 } else {
1275 (void) execle(pwd->pw_shell, cp, "-c", cmdbuf, NULL,
1276 envinit);
1277 }
1278
1279 perror(pwd->pw_shell);
1280 exit(1);
1281
1282 signout:
1283 if (ccache)
1284 (void) pam_close_session(pamh, 0);
1285 ccache = NULL;
1286 (void) pam_end(pamh, PAM_ABORT);
1287 exit(1);
1288 }
1289
1290 static void
getstr(fd,buf,cnt,err)1291 getstr(fd, buf, cnt, err)
1292 int fd;
1293 char *buf;
1294 int cnt;
1295 char *err;
1296 {
1297 char c;
1298
1299 do {
1300 if (read(fd, &c, 1) != 1)
1301 exit(1);
1302 if (cnt-- == 0) {
1303 error("%s too long\n", err);
1304 exit(1);
1305 }
1306 *buf++ = c;
1307 } while (c != 0);
1308 }
1309
1310 /*PRINTFLIKE1*/
1311 static void
error(char * fmt,...)1312 error(char *fmt, ...)
1313 {
1314 va_list ap;
1315 char buf[RSHD_BUFSIZ];
1316
1317 buf[0] = 1;
1318 va_start(ap, fmt);
1319 (void) vsnprintf(&buf[1], sizeof (buf) - 1, fmt, ap);
1320 va_end(ap);
1321 (void) write(STDERR_FILENO, buf, strlen(buf));
1322 }
1323
1324 static char *illegal[] = {
1325 "SHELL=",
1326 "HOME=",
1327 "LOGNAME=",
1328 #ifndef NO_MAIL
1329 "MAIL=",
1330 #endif
1331 "CDPATH=",
1332 "IFS=",
1333 "PATH=",
1334 "USER=",
1335 "TZ=",
1336 0
1337 };
1338
1339 /*
1340 * legalenvvar - can PAM modules insert this environmental variable?
1341 */
1342
1343 static int
legalenvvar(char * s)1344 legalenvvar(char *s)
1345 {
1346 register char **p;
1347
1348 for (p = illegal; *p; p++)
1349 if (strncmp(s, *p, strlen(*p)) == 0)
1350 return (0);
1351
1352 if (s[0] == 'L' && s[1] == 'D' && s[2] == '_')
1353 return (0);
1354
1355 return (1);
1356 }
1357
1358 /*
1359 * Add a string to the environment of the new process.
1360 */
1361
1362 static void
add_to_envinit(char * string)1363 add_to_envinit(char *string)
1364 {
1365 /*
1366 * Reserve space for 2 * 8 = 16 environment entries initially which
1367 * should be enough to avoid reallocation of "envinit" in most cases.
1368 */
1369 static int size = 8;
1370 static int index = 0;
1371
1372 if (string == NULL)
1373 return;
1374
1375 if ((envinit == NULL) || (index == size)) {
1376 size *= 2;
1377 envinit = realloc(envinit, (size + 1) * sizeof (char *));
1378 if (envinit == NULL) {
1379 perror("malloc");
1380 exit(1);
1381 }
1382 }
1383
1384 envinit[index++] = string;
1385 envinit[index] = NULL;
1386 }
1387
1388 /*
1389 * Check if lenv and penv matches or not.
1390 */
1391 static int
locale_envmatch(char * lenv,char * penv)1392 locale_envmatch(char *lenv, char *penv)
1393 {
1394 while ((*lenv == *penv) && (*lenv != '\0') && (*penv != '=')) {
1395 lenv++;
1396 penv++;
1397 }
1398
1399 /*
1400 * '/' is eliminated for security reason.
1401 */
1402 return ((*lenv == '\0' && *penv == '=' && *(penv + 1) != '/'));
1403 }
1404
1405 #ifndef KRB_SENDAUTH_VLEN
1406 #define KRB_SENDAUTH_VLEN 8 /* length for version strings */
1407 #endif
1408
1409 /* MUST be KRB_SENDAUTH_VLEN chars */
1410 #define KRB_SENDAUTH_VERS "AUTHV0.1"
1411 #define SIZEOF_INADDR sizeof (struct in_addr)
1412
1413 static krb5_error_code
recvauth(int netf,int * valid_checksum)1414 recvauth(int netf, int *valid_checksum)
1415 {
1416 krb5_auth_context auth_context = NULL;
1417 krb5_error_code status;
1418 struct sockaddr_in laddr;
1419 int len;
1420 krb5_data inbuf;
1421 krb5_authenticator *authenticator;
1422 krb5_ticket *ticket;
1423 krb5_rcache rcache;
1424 krb5_data version;
1425 krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
1426 krb5_data desinbuf;
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 */
1432
1433 *valid_checksum = 0;
1434 len = sizeof (laddr);
1435
1436 if (getsockname(netf, (struct sockaddr *)&laddr, &len)) {
1437 exit(1);
1438 }
1439
1440 if (status = krb5_auth_con_init(bsd_context, &auth_context))
1441 return (status);
1442
1443 if (status = krb5_auth_con_genaddrs(bsd_context, auth_context, netf,
1444 KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR))
1445 return (status);
1446
1447 status = krb5_auth_con_getrcache(bsd_context, auth_context, &rcache);
1448 if (status)
1449 return (status);
1450
1451 if (!rcache) {
1452 krb5_principal server;
1453
1454 status = krb5_sname_to_principal(bsd_context, 0, 0,
1455 KRB5_NT_SRV_HST, &server);
1456 if (status)
1457 return (status);
1458
1459 status = krb5_get_server_rcache(bsd_context,
1460 krb5_princ_component(bsd_context, server, 0),
1461 &rcache);
1462 krb5_free_principal(bsd_context, server);
1463 if (status)
1464 return (status);
1465
1466 status = krb5_auth_con_setrcache(bsd_context, auth_context,
1467 rcache);
1468 if (status)
1469 return (status);
1470 }
1471
1472 status = krb5_recvauth_version(bsd_context, &auth_context, &netf,
1473 NULL, /* Specify daemon principal */
1474 0, /* no flags */
1475 keytab, /* normally NULL to use v5srvtab */
1476 &ticket, /* return ticket */
1477 &version); /* application version string */
1478
1479
1480 if (status) {
1481 getstr(netf, locuser, sizeof (locuser), "locuser");
1482 getstr(netf, cmdbuf, sizeof (cmdbuf), "command");
1483 getstr(netf, remuser, sizeof (locuser), "remuser");
1484 return (status);
1485 }
1486 getstr(netf, locuser, sizeof (locuser), "locuser");
1487 getstr(netf, cmdbuf, sizeof (cmdbuf), "command");
1488
1489 /* Must be V5 */
1490
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"));
1495 exit(1);
1496 }
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;
1501 } else {
1502 syslog(LOG_ERR, "Unrecognized KCMD protocol (%s)",
1503 (char *)version.data);
1504 error(gettext("Unrecognized KCMD protocol (%s)"),
1505 (char *)version.data);
1506 exit(1);
1507 }
1508 getstr(netf, remuser, sizeof (locuser), "remuser");
1509
1510 if ((status = krb5_unparse_name(bsd_context, ticket->enc_part2->client,
1511 &kremuser)))
1512 return (status);
1513
1514 if ((status = krb5_copy_principal(bsd_context,
1515 ticket->enc_part2->client, &client)))
1516 return (status);
1517
1518
1519 if (checksum_required && (kcmd_protocol == KCMD_OLD_PROTOCOL)) {
1520 if ((status = krb5_auth_con_getauthenticator(bsd_context,
1521 auth_context, &authenticator)))
1522 return (status);
1523
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;
1528 krb5_data input;
1529 krb5_keyblock key;
1530
1531 char *chksumbuf = (char *)malloc(chksumsize);
1532
1533 if (chksumbuf == 0)
1534 goto error_cleanup;
1535 if (getsockname(netf, (struct sockaddr *)&adr,
1536 &adr_length) != 0)
1537 goto error_cleanup;
1538
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.");
1544 free(chksumbuf);
1545 return (-1);
1546 }
1547 if (strlcat(chksumbuf, locuser,
1548 chksumsize) >= chksumsize) {
1549 syslog(LOG_ERR, "locuser too long.");
1550 free(chksumbuf);
1551 return (-1);
1552 }
1553
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;
1560
1561 status = krb5_c_verify_checksum(bsd_context,
1562 &key, 0, &input, authenticator->checksum,
1563 (unsigned int *)valid_checksum);
1564
1565 if (status == 0 && *valid_checksum == 0)
1566 status = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1567 error_cleanup:
1568 if (chksumbuf)
1569 krb5_xfree(chksumbuf);
1570 if (status) {
1571 krb5_free_authenticator(bsd_context,
1572 authenticator);
1573 return (status);
1574 }
1575 }
1576 krb5_free_authenticator(bsd_context, authenticator);
1577 }
1578
1579
1580 if ((strncmp(cmdbuf, "-x ", 3) == 0)) {
1581 if (krb5_privacy_allowed()) {
1582 do_encrypt = 1;
1583 } else {
1584 syslog(LOG_ERR, "rshd: Encryption not supported");
1585 error("rshd: Encryption not supported. \n");
1586 exit(2);
1587 }
1588
1589 status = krb5_auth_con_getremotesubkey(bsd_context,
1590 auth_context,
1591 &sessionkey);
1592 if (status) {
1593 syslog(LOG_ERR, "Error getting KRB5 session subkey");
1594 error(gettext("Error getting KRB5 session subkey"));
1595 exit(1);
1596 }
1597 /*
1598 * The "new" protocol requires that a subkey be sent.
1599 */
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"));
1603 exit(1);
1604 }
1605 /*
1606 * The "old" protocol does not permit an authenticator subkey.
1607 * The key is taken from the ticket instead (see below).
1608 */
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"));
1614 exit(1);
1615 }
1616 /*
1617 * If no key at this point, use the session key from
1618 * the ticket.
1619 */
1620 if (sessionkey == NULL) {
1621 /*
1622 * Save the session key so we can configure the crypto
1623 * module later.
1624 */
1625 status = krb5_copy_keyblock(bsd_context,
1626 ticket->enc_part2->session,
1627 &sessionkey);
1628 if (status) {
1629 syslog(LOG_ERR, "krb5_copy_keyblock failed");
1630 error(gettext("krb5_copy_keyblock failed"));
1631 exit(1);
1632 }
1633 }
1634 /*
1635 * If session key still cannot be found, we must
1636 * exit because encryption is required here
1637 * when encr_flag (-x) is set.
1638 */
1639 if (sessionkey == NULL) {
1640 syslog(LOG_ERR, "Could not find an encryption key");
1641 error(gettext("Could not find an encryption key"));
1642 exit(1);
1643 }
1644
1645 /*
1646 * Initialize parameters/buffers for desread & deswrite here.
1647 */
1648 desinbuf.data = des_inbuf;
1649 desoutbuf.data = des_outbuf;
1650 desinbuf.length = sizeof (des_inbuf);
1651 desoutbuf.length = sizeof (des_outbuf);
1652
1653 eblock.crypto_entry = sessionkey->enctype;
1654 eblock.key = (krb5_keyblock *)sessionkey;
1655
1656 init_encrypt(do_encrypt, bsd_context, kcmd_protocol,
1657 &desinbuf, &desoutbuf, SERVER, &eblock);
1658 }
1659
1660 ticket->enc_part2->session = 0;
1661
1662 if ((status = krb5_read_message(bsd_context, (krb5_pointer) & netf,
1663 &inbuf))) {
1664 error(gettext("Error reading message: %s\n"),
1665 error_message(status));
1666 exit(1);
1667 }
1668
1669 if (inbuf.length) {
1670 krb5_creds **creds = NULL;
1671
1672 /* Forwarding being done, read creds */
1673 if ((status = krb5_rd_cred(bsd_context,
1674 auth_context, &inbuf, &creds,
1675 NULL))) {
1676 error("Can't get forwarded credentials: %s\n",
1677 error_message(status));
1678 exit(1);
1679 }
1680
1681 /* Store the forwarded creds in the ccache */
1682 if ((status = store_forw_creds(bsd_context,
1683 creds, ticket, locuser,
1684 &ccache))) {
1685 error("Can't store forwarded credentials: %s\n",
1686 error_message(status));
1687 exit(1);
1688 }
1689 krb5_free_creds(bsd_context, *creds);
1690 }
1691
1692 krb5_free_ticket(bsd_context, ticket);
1693 return (0);
1694 }
1695
1696 static void
usage(void)1697 usage(void)
1698 {
1699 (void) fprintf(stderr, gettext("%s: rshd [-k5eciU] "
1700 "[-P path] [-M realm] [-s tos] "
1701 #ifdef DEBUG
1702 "[-D port] "
1703 #endif /* DEBUG */
1704 "[-S keytab]"), gettext("usage"));
1705
1706 syslog(LOG_ERR, "%s: rshd [-k5eciU] [-P path] [-M realm] [-s tos] "
1707 #ifdef DEBUG
1708 "[-D port] "
1709 #endif /* DEBUG */
1710 "[-S keytab]", gettext("usage"));
1711 }
1712