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