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