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
 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
 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
 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
 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
 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
 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
 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 *
 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 *
 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 *
 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
 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
 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
 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
 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 }