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 2013 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 /*
27 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
29 */
30
31 /*
32 * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
33 * All rights reserved.
34 *
35 * Copyright (c) 1987, 1988 Microsoft Corporation.
36 * All rights reserved.
37 */
38
39 /*
40 * sulogin - special login program exec'd from init to let user
41 * come up single user, or go to default init state straight away.
42 *
43 * Explain the scoop to the user, prompt for an authorized user
44 * name or ^D and then prompt for password or ^D. If the password
45 * is correct, check if the user is authorized, if so enter
46 * single user. ^D exits sulogin, and init will go to default init state.
47 *
48 * If /etc/passwd is missing, or there's no entry for root,
49 * go single user, no questions asked.
50 */
51
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/param.h>
55 #include <sys/sysmsg_impl.h>
56 #include <sys/mkdev.h>
57 #include <sys/resource.h>
58 #include <sys/uadmin.h>
59 #include <sys/wait.h>
60 #include <sys/stermio.h>
61 #include <fcntl.h>
62 #include <termio.h>
63 #include <pwd.h>
64 #include <shadow.h>
65 #include <stdlib.h>
66 #include <stdio.h>
67 #include <signal.h>
68 #include <siginfo.h>
69 #include <utmpx.h>
70 #include <unistd.h>
71 #include <ucontext.h>
72 #include <string.h>
73 #include <strings.h>
74 #include <deflt.h>
75 #include <limits.h>
76 #include <errno.h>
77 #include <crypt.h>
78 #include <auth_attr.h>
79 #include <auth_list.h>
80 #include <nss_dbdefs.h>
81 #include <user_attr.h>
82 #include <sys/vt.h>
83 #include <sys/kd.h>
84
85 /*
86 * Intervals to sleep after failed login
87 */
88 #ifndef SLEEPTIME
89 #define SLEEPTIME 4 /* sleeptime before login incorrect msg */
90 #endif
91
92 #define SLEEPTIME_MAX 5 /* maximum sleeptime */
93
94 /*
95 * the name of the file containing the login defaults we deliberately
96 * use the same file as login(1)
97 */
98
99 #define DEFAULT_LOGIN "/etc/default/login"
100 #define DEFAULT_SULOGIN "/etc/default/sulogin"
101 #define DEFAULT_CONSOLE "/dev/console"
102
103 static char shell[] = "/sbin/sh";
104 static char su[] = "/sbin/su.static";
105 static int sleeptime = SLEEPTIME;
106 static int nchild = 0;
107 static pid_t pidlist[10];
108 static pid_t masterpid = 0;
109 static pid_t originalpid = 0;
110 static struct sigaction sa;
111 static struct termio ttymodes;
112
113 static char *findttyname(int fd);
114 static char *stripttyname(char *);
115 static char *sulogin_getinput(char *, int);
116 static void noop(int);
117 static void single(const char *, char *);
118 static void main_loop(char *, boolean_t);
119 static void parenthandler();
120 static void termhandler(int);
121 static void setupsigs(void);
122 static int pathcmp(char *, char *);
123 static void doit(char *, char *);
124 static void childcleanup(int);
125
126 #define ECHOON 0
127 #define ECHOOFF 1
128
129 /* ARGSUSED */
130 int
main(int argc,char ** argv)131 main(int argc, char **argv)
132 {
133 struct spwd *shpw;
134 int passreq = B_TRUE;
135 int flags;
136 int fd;
137 char *infop, *ptr, *p;
138 pid_t pid;
139 int bufsize;
140 struct stat st;
141 char cttyname[100];
142 char namedlist[500];
143 char scratchlist[500];
144 dev_t cttyd;
145
146 if (geteuid() != 0) {
147 (void) fprintf(stderr, "%s: must be root\n", argv[0]);
148 return (EXIT_FAILURE);
149 }
150
151 /* Do the magic to determine the children */
152 if ((fd = open(SYSMSG, 0)) < 0)
153 return (EXIT_FAILURE);
154
155 /*
156 * If the console supports the CIOCTTYCONSOLE ioctl, then fetch
157 * its console device list. If not, then we use the default
158 * console name.
159 */
160 if (ioctl(fd, CIOCTTYCONSOLE, &cttyd) == 0) {
161 if ((bufsize = ioctl(fd, CIOCGETCONSOLE, NULL)) < 0)
162 return (EXIT_FAILURE);
163
164 if (bufsize > 0) {
165 if ((infop = calloc(bufsize, sizeof (char))) == NULL)
166 return (EXIT_FAILURE);
167
168 if (ioctl(fd, CIOCGETCONSOLE, infop) < 0)
169 return (EXIT_FAILURE);
170
171 (void) snprintf(namedlist, sizeof (namedlist), "%s %s",
172 DEFAULT_CONSOLE, infop);
173 } else
174 (void) snprintf(namedlist, sizeof (namedlist), "%s",
175 DEFAULT_CONSOLE);
176 } else {
177 (void) snprintf(namedlist, sizeof (namedlist), "%s",
178 DEFAULT_CONSOLE);
179 cttyd = NODEV;
180 }
181
182 /*
183 * The attempt to turn the controlling terminals dev_t into a string
184 * may not be successful, thus leaving the variable cttyname as a
185 * NULL. This occurs if during boot we find
186 * the root partition (or some other partition)
187 * requires manual fsck, thus resulting in sulogin
188 * getting invoked. The ioctl for CIOCTTYCONSOLE
189 * called above returned NODEV for cttyd
190 * in these cases. NODEV gets returned when the vnode pointer
191 * in our session structure is NULL. In these cases it
192 * must be assumed that the default console is used.
193 *
194 * See uts/common/os/session.c:cttydev().
195 */
196 (void) strcpy(cttyname, DEFAULT_CONSOLE);
197 (void) strcpy(scratchlist, namedlist);
198 ptr = scratchlist;
199 while (ptr != NULL) {
200 p = strchr(ptr, ' ');
201 if (p == NULL) {
202 if (stat(ptr, &st))
203 return (EXIT_FAILURE);
204 if (st.st_rdev == cttyd)
205 (void) strcpy(cttyname, ptr);
206 break;
207 }
208 *p++ = '\0';
209 if (stat(ptr, &st))
210 return (EXIT_FAILURE);
211 if (st.st_rdev == cttyd) {
212 (void) strcpy(cttyname, ptr);
213 break;
214 }
215 ptr = p;
216 }
217
218 /*
219 * Use the same value of SLEEPTIME that login(1) uses. This
220 * is obtained by reading the file /etc/default/login using
221 * the def*() functions.
222 */
223
224 if (defopen(DEFAULT_LOGIN) == 0) {
225
226 /* ignore case */
227
228 flags = defcntl(DC_GETFLAGS, 0);
229 TURNOFF(flags, DC_CASE);
230 (void) defcntl(DC_SETFLAGS, flags);
231
232 if ((ptr = defread("SLEEPTIME=")) != NULL)
233 sleeptime = atoi(ptr);
234
235 if (sleeptime < 0 || sleeptime > SLEEPTIME_MAX)
236 sleeptime = SLEEPTIME;
237
238 (void) defopen(NULL); /* closes DEFAULT_LOGIN */
239 }
240
241 /*
242 * Use our own value of PASSREQ, separate from the one login(1) uses.
243 * This is obtained by reading the file /etc/default/sulogin using
244 * the def*() functions.
245 */
246
247 if (defopen(DEFAULT_SULOGIN) == 0) {
248 if ((ptr = defread("PASSREQ=")) != NULL)
249 if (strcmp("NO", ptr) == 0)
250 passreq = B_FALSE;
251
252 (void) defopen(NULL); /* closes DEFAULT_SULOGIN */
253 }
254
255 if (passreq == B_FALSE)
256 single(shell, NULL);
257
258 /*
259 * if no 'root' entry in /etc/shadow, give maint. mode single
260 * user shell prompt
261 */
262 setspent();
263 if ((shpw = getspnam("root")) == NULL) {
264 (void) fprintf(stderr, "\n*** Unable to retrieve `root' entry "
265 "in shadow password file ***\n\n");
266 single(shell, NULL);
267 }
268 endspent();
269 /*
270 * if no 'root' entry in /etc/passwd, give maint. mode single
271 * user shell prompt
272 */
273 setpwent();
274 if (getpwnam("root") == NULL) {
275 (void) fprintf(stderr, "\n*** Unable to retrieve `root' entry "
276 "in password file ***\n\n");
277 single(shell, NULL);
278 }
279 endpwent();
280 /* process with controlling tty treated special */
281 if ((pid = fork()) != (pid_t)0) {
282 if (pid == -1)
283 return (EXIT_FAILURE);
284 else {
285 setupsigs();
286 masterpid = pid;
287 originalpid = getpid();
288 /*
289 * init() was invoked from a console that was not
290 * the default console, nor was it an auxiliary.
291 */
292 if (cttyname[0] == NULL)
293 termhandler(0);
294 /* Never returns */
295
296 main_loop(cttyname, B_TRUE);
297 /* Never returns */
298 }
299 }
300 masterpid = getpid();
301 originalpid = getppid();
302 pidlist[nchild++] = originalpid;
303
304 sa.sa_handler = childcleanup;
305 sa.sa_flags = 0;
306 (void) sigemptyset(&sa.sa_mask);
307 (void) sigaction(SIGTERM, &sa, NULL);
308 (void) sigaction(SIGHUP, &sa, NULL);
309 sa.sa_handler = parenthandler;
310 sa.sa_flags = SA_SIGINFO;
311 (void) sigemptyset(&sa.sa_mask);
312 (void) sigaction(SIGUSR1, &sa, NULL);
313
314 sa.sa_handler = SIG_IGN;
315 sa.sa_flags = 0;
316 (void) sigemptyset(&sa.sa_mask);
317 (void) sigaction(SIGCHLD, &sa, NULL);
318 /*
319 * If there isn't a password on root, then don't permit
320 * the fanout capability of sulogin.
321 */
322 if (*shpw->sp_pwdp != '\0') {
323 ptr = namedlist;
324 while (ptr != NULL) {
325 p = strchr(ptr, ' ');
326 if (p == NULL) {
327 doit(ptr, cttyname);
328 break;
329 }
330 *p++ = '\0';
331 doit(ptr, cttyname);
332 ptr = p;
333 }
334 }
335 if (pathcmp(cttyname, DEFAULT_CONSOLE) != 0) {
336 if ((pid = fork()) == (pid_t)0) {
337 setupsigs();
338 main_loop(DEFAULT_CONSOLE, B_FALSE);
339 } else if (pid == -1)
340 return (EXIT_FAILURE);
341 pidlist[nchild++] = pid;
342 }
343 /*
344 * When parent is all done, it pauses until one of its children
345 * signals that its time to kill the underpriviledged.
346 */
347 (void) wait(NULL);
348
349 return (0);
350 }
351
352 /*
353 * These flags are taken from stty's "sane" table entries in
354 * usr/src/cmd/ttymon/sttytable.c
355 */
356 #define SET_IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON|IMAXBEL)
357 #define RESET_IFLAG (IGNBRK|PARMRK|INPCK|INLCR|IGNCR|IUCLC|IXOFF|IXANY)
358 #define SET_OFLAG (OPOST|ONLCR)
359 #define RESET_OFLAG (OLCUC|OCRNL|ONOCR|ONLRET|OFILL|OFDEL| \
360 NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY)
361 #define SET_LFLAG (ISIG|ICANON|IEXTEN|ECHO|ECHOK|ECHOE|ECHOKE|ECHOCTL)
362 #define RESET_LFLAG (XCASE|ECHONL|NOFLSH|STFLUSH|STWRAP|STAPPL)
363
364 /*
365 * Do the equivalent of 'stty sane' on the terminal since we don't know
366 * what state it was in on startup.
367 */
368 static void
sanitize_tty(int fd)369 sanitize_tty(int fd)
370 {
371 (void) ioctl(fd, TCGETA, &ttymodes);
372 ttymodes.c_iflag &= ~RESET_IFLAG;
373 ttymodes.c_iflag |= SET_IFLAG;
374 ttymodes.c_oflag &= ~RESET_OFLAG;
375 ttymodes.c_oflag |= SET_OFLAG;
376 ttymodes.c_lflag &= ~RESET_LFLAG;
377 ttymodes.c_lflag |= SET_LFLAG;
378 ttymodes.c_cc[VERASE] = CERASE;
379 ttymodes.c_cc[VKILL] = CKILL;
380 ttymodes.c_cc[VQUIT] = CQUIT;
381 ttymodes.c_cc[VINTR] = CINTR;
382 ttymodes.c_cc[VEOF] = CEOF;
383 ttymodes.c_cc[VEOL] = CNUL;
384 (void) ioctl(fd, TCSETAF, &ttymodes);
385 }
386
387 /*
388 * Fork a child of sulogin for each of the auxiliary consoles.
389 */
390 static void
doit(char * ptr,char * cttyname)391 doit(char *ptr, char *cttyname)
392 {
393 pid_t pid;
394
395 if (pathcmp(ptr, DEFAULT_CONSOLE) != 0 &&
396 pathcmp(ptr, cttyname) != 0) {
397 if ((pid = fork()) == (pid_t)0) {
398 setupsigs();
399 main_loop(ptr, B_FALSE);
400 } else if (pid == -1)
401 exit(EXIT_FAILURE);
402 pidlist[nchild++] = pid;
403 }
404 }
405
406 static int
pathcmp(char * adev,char * bdev)407 pathcmp(char *adev, char *bdev)
408 {
409 struct stat st1;
410 struct stat st2;
411
412 if (adev == NULL || bdev == NULL)
413 return (1);
414
415 if (strcmp(adev, bdev) == 0)
416 return (0);
417
418 if (stat(adev, &st1) || !S_ISCHR(st1.st_mode))
419 return (1);
420
421 if (stat(bdev, &st2) || !S_ISCHR(st2.st_mode))
422 return (1);
423
424 if (st1.st_rdev == st2.st_rdev)
425 return (0);
426
427 return (1);
428 }
429
430 /* Handlers for the children at initialization */
431 static void
setupsigs()432 setupsigs()
433 {
434 sa.sa_handler = noop;
435 sa.sa_flags = 0;
436 (void) sigemptyset(&sa.sa_mask);
437 (void) sigaction(SIGINT, &sa, NULL);
438 (void) sigaction(SIGQUIT, &sa, NULL);
439
440 sa.sa_handler = termhandler;
441 sa.sa_flags = 0;
442 (void) sigemptyset(&sa.sa_mask);
443 (void) sigaction(SIGTERM, &sa, NULL);
444 (void) sigaction(SIGKILL, &sa, NULL);
445 (void) sigaction(SIGHUP, &sa, NULL);
446 }
447
448 static void
main_loop(char * devname,boolean_t cttyflag)449 main_loop(char *devname, boolean_t cttyflag)
450 {
451 int fd, fb, i;
452 char *user = NULL; /* authorized user */
453 char *pass; /* password from user */
454 char *cpass; /* crypted password */
455 struct spwd spwd;
456 struct spwd *lshpw; /* local shadow */
457 char shadow[NSS_BUFLEN_SHADOW];
458 FILE *sysmsgfd;
459
460 for (i = 0; i < 3; i++)
461 (void) close(i);
462 if (cttyflag == B_FALSE) {
463 if (setsid() == -1)
464 exit(EXIT_FAILURE);
465 }
466 if ((fd = open(devname, O_RDWR)) < 0)
467 exit(EXIT_FAILURE);
468
469 /*
470 * In system maintenance mode, all virtual console instances
471 * of the svc:/system/console-login service are not available
472 * any more, and only the system console is available. So here
473 * we always switch to the system console in case at the moment
474 * the active console isn't it.
475 */
476 (void) ioctl(fd, VT_ACTIVATE, 1);
477
478 if (fd != 0)
479 (void) dup2(fd, STDIN_FILENO);
480 if (fd != 1)
481 (void) dup2(fd, STDOUT_FILENO);
482 if (fd != 2)
483 (void) dup2(fd, STDERR_FILENO);
484 if (fd > 2)
485 (void) close(fd);
486
487 /* Stop progress bar and reset console mode to text */
488 if ((fb = open("/dev/fb", O_RDONLY)) >= 0) {
489 (void) ioctl(fb, KDSETMODE, KD_RESETTEXT);
490 (void) close(fb);
491 }
492
493 sysmsgfd = fopen("/dev/sysmsg", "w");
494
495 sanitize_tty(fileno(stdin));
496
497 for (;;) {
498 do {
499 (void) printf("\nEnter user name for system "
500 "maintenance (control-d to bypass): ");
501 user = sulogin_getinput(devname, ECHOON);
502 if (user == NULL) {
503 /* signal other children to exit */
504 (void) sigsend(P_PID, masterpid, SIGUSR1);
505 /* ^D, so straight to default init state */
506 exit(EXIT_FAILURE);
507 }
508 } while (user[0] == '\0');
509 (void) printf("Enter %s password (control-d to bypass): ",
510 user);
511
512 if ((pass = sulogin_getinput(devname, ECHOOFF)) == NULL) {
513 /* signal other children to exit */
514 (void) sigsend(P_PID, masterpid, SIGUSR1);
515 /* ^D, so straight to default init state */
516 free(user);
517 exit(EXIT_FAILURE);
518 }
519 lshpw = getspnam_r(user, &spwd, shadow, sizeof (shadow));
520 if (lshpw == NULL) {
521 /*
522 * the user entered doesn't exist, too bad.
523 */
524 goto sorry;
525 }
526
527 /*
528 * There is a special case error to catch here:
529 * If the password is hashed with an algorithm
530 * other than the old unix crypt the call to crypt(3c)
531 * could fail if /usr is corrupt or not available
532 * since by default /etc/security/crypt.conf will
533 * have the crypt_ modules located under /usr/lib.
534 * Or it could happen if /etc/security/crypt.conf
535 * is corrupted.
536 *
537 * If this happens crypt(3c) will return NULL and
538 * set errno to ELIBACC for the former condition or
539 * EINVAL for the latter, in this case we bypass
540 * authentication and just verify that the user is
541 * authorized.
542 */
543
544 errno = 0;
545 cpass = crypt(pass, lshpw->sp_pwdp);
546 if (((cpass == NULL) && (lshpw->sp_pwdp[0] == '$')) &&
547 ((errno == ELIBACC) || (errno == EINVAL))) {
548 goto checkauth;
549 } else if ((cpass == NULL) ||
550 (strcmp(cpass, lshpw->sp_pwdp) != 0)) {
551 goto sorry;
552 }
553
554 checkauth:
555 /*
556 * There is a special case error here as well.
557 * If /etc/user_attr is corrupt, getusernam("root")
558 * returns NULL.
559 * In this case, we just give access because this is similar
560 * to the case of root not existing in /etc/passwd.
561 */
562
563 if ((getusernam("root") != NULL) &&
564 (chkauthattr(MAINTENANCE_AUTH, user) != 1)) {
565 goto sorry;
566 }
567 (void) fprintf(sysmsgfd, "\nsingle-user privilege "
568 "assigned to %s on %s.\n", user, devname);
569 (void) sigsend(P_PID, masterpid, SIGUSR1);
570 (void) wait(NULL);
571 free(user);
572 free(pass);
573 single(su, devname);
574 /* single never returns */
575
576 sorry:
577 (void) printf("\nLogin incorrect or user %s not authorized\n",
578 user);
579 free(user);
580 free(pass);
581 (void) sleep(sleeptime);
582 }
583 }
584
585 /*
586 * single() - exec shell for single user mode
587 */
588
589 static void
single(const char * cmd,char * ttyn)590 single(const char *cmd, char *ttyn)
591 {
592 struct utmpx *u;
593 char found = B_FALSE;
594
595 if (ttyn == NULL)
596 ttyn = findttyname(STDIN_FILENO);
597
598 /*
599 * utmpx records on the console device are expected to be "console"
600 * by other processes, such as dtlogin.
601 */
602 ttyn = stripttyname(ttyn);
603
604 /* update the utmpx file. */
605 while ((u = getutxent()) != NULL) {
606 if (strcmp(u->ut_line, ttyn) == 0) {
607 u->ut_tv.tv_sec = time(NULL);
608 u->ut_type = USER_PROCESS;
609 u->ut_pid = getpid();
610 if (strcmp(u->ut_user, "root") != 0)
611 (void) strcpy(u->ut_user, "root");
612 (void) pututxline(u);
613 found = B_TRUE;
614 break;
615 }
616 }
617 if (!found) {
618 struct utmpx entryx;
619
620 entryx.ut_tv.tv_sec = time(NULL);
621 entryx.ut_type = USER_PROCESS;
622 entryx.ut_pid = getpid();
623 (void) strcpy(entryx.ut_user, "root");
624 (void) strcpy(entryx.ut_line, ttyn);
625 entryx.ut_tv.tv_usec = 0;
626 entryx.ut_session = 0;
627 entryx.ut_id[0] = 'c';
628 entryx.ut_id[1] = 'o';
629 entryx.ut_id[2] = 's';
630 entryx.ut_id[3] = 'u';
631 entryx.ut_syslen = 1;
632 entryx.ut_host[0] = '\0';
633 entryx.ut_exit.e_termination = WTERMSIG(0);
634 entryx.ut_exit.e_exit = WEXITSTATUS(0);
635 (void) pututxline(&entryx);
636 }
637 endutxent();
638 (void) printf("Entering System Maintenance Mode\n\n");
639
640 if (execl(cmd, cmd, "-", (char *)0) < 0)
641 exit(EXIT_FAILURE);
642 }
643
644 /*
645 * sulogin_getinput() - hacked from the standard PAM tty conversation
646 * function getpassphrase() library version
647 * so we can distinguish newline and EOF.
648 * also don't need this routine to give a prompt.
649 *
650 * returns the password string, or NULL if the used typed EOF.
651 */
652
653 static char *
sulogin_getinput(char * devname,int echooff)654 sulogin_getinput(char *devname, int echooff)
655 {
656 struct termio ttyb;
657 int c;
658 FILE *fi;
659 static char input[PASS_MAX + 1];
660 void (*saved_handler)();
661 char *rval = input;
662 int i = 0;
663
664 if ((fi = fopen(devname, "r")) == NULL) {
665 fi = stdin;
666 }
667
668 saved_handler = signal(SIGINT, SIG_IGN);
669
670 if (echooff) {
671 ttyb = ttymodes;
672 ttyb.c_lflag &= ~(ECHO | ECHOE | ECHONL);
673 (void) ioctl(fileno(fi), TCSETAF, &ttyb);
674 }
675
676 /* get characters up to PASS_MAX, but don't overflow */
677 while ((c = getc(fi)) != '\n' && (c != '\r')) {
678 if (c == EOF && i == 0) { /* ^D, no input */
679 rval = NULL;
680 break;
681 }
682 if (i < PASS_MAX) {
683 input[i++] = (char)c;
684 }
685 }
686 input[i] = '\0';
687 (void) fputc('\n', fi);
688 if (echooff) {
689 (void) ioctl(fileno(fi), TCSETAW, &ttymodes);
690 }
691
692 if (saved_handler != SIG_ERR)
693 (void) signal(SIGINT, saved_handler);
694 return (rval == NULL ? NULL : strdup(rval));
695 }
696
697 static char *
findttyname(int fd)698 findttyname(int fd)
699 {
700 char *ttyn = ttyname(fd);
701
702 if (ttyn == NULL)
703 ttyn = "/dev/???";
704 else {
705 /*
706 * /dev/syscon and /dev/systty are usually links to
707 * /dev/console. prefer /dev/console.
708 */
709 if (((strcmp(ttyn, "/dev/syscon") == 0) ||
710 (strcmp(ttyn, "/dev/systty") == 0)) &&
711 access("/dev/console", F_OK))
712 ttyn = "/dev/console";
713 }
714 return (ttyn);
715 }
716
717 static char *
stripttyname(char * ttyn)718 stripttyname(char *ttyn)
719 {
720 /* saw off the /dev/ */
721 if (strncmp(ttyn, "/dev/", sizeof ("/dev/") -1) == 0)
722 return (ttyn + sizeof ("/dev/") - 1);
723 else
724 return (ttyn);
725 }
726
727
728 /* ARGSUSED */
729 static void
noop(int sig)730 noop(int sig)
731 {
732 /*
733 * This signal handler does nothing except return. We use it
734 * as the signal disposition in this program instead of
735 * SIG_IGN so that we do not have to restore the disposition
736 * back to SIG_DFL. Instead we allow exec(2) to set the
737 * dispostion to SIG_DFL to avoid a race condition.
738 */
739 }
740
741 /* ARGSUSED */
742 static void
parenthandler(int sig,siginfo_t * si,ucontext_t * uc)743 parenthandler(int sig, siginfo_t *si, ucontext_t *uc)
744 {
745 int i;
746
747 /*
748 * We get here if someone has successfully entered a password
749 * from the auxiliary console and is getting the single-user shell.
750 * When this happens, the parent needs to kill the children
751 * that didn't get the shell.
752 *
753 */
754 for (i = 0; i < nchild; i++) {
755 if (pidlist[i] != si->__data.__proc.__pid)
756 (void) sigsend(P_PID, pidlist[i], SIGTERM);
757 }
758 sa.sa_handler = SIG_IGN;
759 sa.sa_flags = 0;
760 (void) sigemptyset(&sa.sa_mask);
761 (void) sigaction(SIGINT, &sa, NULL);
762 (void) sigaction(SIGQUIT, &sa, NULL);
763 (void) sigaction(SIGTERM, &sa, NULL);
764 (void) wait(NULL);
765 }
766
767 /*
768 * The master pid will get SIGTERM or SIGHUP from init, and then
769 * has to make sure the shell isn't still running.
770 */
771
772 /* ARGSUSED */
773 static void
childcleanup(int sig)774 childcleanup(int sig)
775 {
776 int i;
777
778 /* Only need to kill the child that became the shell. */
779 for (i = 0; i < nchild; i++) {
780 /* Don't kill gramps before his time */
781 if (pidlist[i] != getppid())
782 (void) sigsend(P_PID, pidlist[i], SIGHUP);
783 }
784 }
785
786 /* ARGSUSED */
787 static void
termhandler(int sig)788 termhandler(int sig)
789 {
790 FILE *fi;
791 pid_t pid;
792
793 /* Processes come here when they fail to receive the password. */
794 if ((fi = fopen("/dev/tty", "r+")) == NULL)
795 fi = stdin;
796 else
797 setbuf(fi, NULL);
798 sanitize_tty(fileno(fi));
799 /* If you're the controlling tty, then just wait */
800 pid = getpid();
801 if (pid == originalpid || pid == masterpid) {
802 sa.sa_handler = SIG_IGN;
803 sa.sa_flags = 0;
804 (void) sigemptyset(&sa.sa_mask);
805 (void) sigaction(SIGINT, &sa, NULL);
806 (void) sigaction(SIGQUIT, &sa, NULL);
807 sa.sa_handler = SIG_DFL;
808 sa.sa_flags = 0;
809 (void) sigemptyset(&sa.sa_mask);
810 (void) sigaction(SIGTERM, &sa, NULL);
811 (void) sigaction(SIGHUP, &sa, NULL);
812 (void) wait(NULL);
813 }
814 exit(0);
815 }
816