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