"Fossies" - the Fresh Open Source Software Archive

Member "sysvinit-2.99/src/sulogin.c" (21 Feb 2021, 22922 Bytes) of package /linux/misc/sysvinit-2.99.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "sulogin.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.98_vs_2.99.

    1 /*
    2  * sulogin  This program gives Linux machines a reasonable
    3  *      secure way to boot single user. It forces the
    4  *      user to supply the root password before a
    5  *      shell is started.
    6  *
    7  *      If there is a shadow password file and the
    8  *      encrypted root password is "x" the shadow
    9  *      password will be used.
   10  *
   11  * Version: @(#)sulogin 2.85-3 23-Apr-2003 miquels@cistron.nl
   12  *
   13  * Copyright (C) 1998-2003 Miquel van Smoorenburg.
   14  *
   15  * This program is free software; you can redistribute it and/or modify
   16  * it under the terms of the GNU General Public License as published by
   17  * the Free Software Foundation; either version 2 of the License, or
   18  * (at your option) any later version.
   19  * 
   20  * This program is distributed in the hope that it will be useful,
   21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   23  * GNU General Public License for more details.
   24  * 
   25  * You should have received a copy of the GNU General Public License
   26  * along with this program; if not, write to the Free Software
   27  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   28  *
   29  */
   30 
   31 #include <sys/mman.h>
   32 #include <sys/types.h>
   33 #include <sys/stat.h>
   34 #include <sys/wait.h>
   35 #ifdef __linux__
   36 #include <sys/sysmacros.h> 
   37 #endif
   38 #include <stdio.h>
   39 #include <string.h>
   40 #include <stdlib.h>
   41 #include <unistd.h>
   42 #include <crypt.h>    /* added to make this compile on Fedora 28 */
   43 #include <fcntl.h>
   44 #include <signal.h>
   45 #include <pwd.h>
   46 #include <shadow.h>
   47 #include <termios.h>
   48 #include <sys/ttydefaults.h>
   49 #include <errno.h>
   50 #include <sys/ioctl.h>
   51 #ifdef __linux__
   52 #  include <sys/statfs.h>
   53 #  include <sys/mount.h>
   54 #  include <linux/fs.h>
   55 #  include <linux/magic.h>
   56 #  include <linux/major.h>
   57 #  ifndef TMPFS_MAGIC
   58 #    define TMPFS_MAGIC     0x01021994
   59 #  endif
   60 #  ifndef MNT_DETACH
   61 #    define MNT_DETACH      2
   62 #  endif
   63 #  define dovoid(f)     if ((f)){}
   64 #endif
   65 
   66 #define BS  CTRL('h')
   67 #define NL  CTRL('j')
   68 #define CR  CTRL('m')
   69 
   70 #ifdef WITH_SELINUX
   71 #  include <selinux/selinux.h>
   72 #  include <selinux/get_context_list.h>
   73 #endif
   74 
   75 #define PASSWORD_LENGTH 256
   76 
   77 #include "consoles.h"
   78 #define CONMAX      16
   79 
   80 #define CHECK_DES   1
   81 #define CHECK_MD5   1
   82 
   83 #define F_PASSWD    "/etc/passwd"
   84 #define F_SHADOW    "/etc/shadow"
   85 #define BINSH       "/bin/sh"
   86 #define STATICSH    "/bin/sash"
   87 
   88 char *Version = "@(#)sulogin 2.85-3 23-Apr-2003 miquels@cistron.nl";
   89 
   90 static int timeout;
   91 static int profile;
   92 static volatile uint32_t openfd;        /* Remember higher file descriptors */
   93 static volatile uint32_t *usemask;
   94 
   95 static sighandler_t saved_sigint  = SIG_DFL;
   96 static sighandler_t saved_sigtstp = SIG_DFL;
   97 static sighandler_t saved_sigquit = SIG_DFL;
   98 static sighandler_t saved_sighup  = SIG_DFL;
   99 
  100 static volatile sig_atomic_t alarm_rised;
  101 static volatile sig_atomic_t sigchild;
  102 
  103 #ifndef IUCLC
  104 #  define IUCLC 0
  105 #endif
  106 
  107 /*
  108  *  Fix the tty modes and set reasonable defaults.
  109  */
  110 static
  111 void tcinit(struct console *con)
  112 {
  113     int serial, flags;
  114     struct termios *tio = &con->tio;
  115     int fd = con->fd;
  116 
  117     /* Expected error */
  118     serial = errno = 0;
  119 
  120     /* Get line attributes */
  121     if (tcgetattr(fd, tio) < 0) {
  122         con->flags |= CON_NOTTY;
  123         return;
  124     }
  125 
  126     /* Handle serial lines here */
  127     if (ioctl (fd, TIOCMGET, (char*)&serial) == 0) {
  128         speed_t ispeed, ospeed;
  129         struct winsize ws;
  130 
  131         /* this is a modem line */
  132         con->flags |= CON_SERIAL;
  133 
  134         /* Flush input and output queues on modem lines */
  135         (void) tcflush(fd, TCIOFLUSH);
  136 
  137         ispeed = cfgetispeed(tio);
  138         ospeed = cfgetospeed(tio);
  139         
  140         if (!ispeed) ispeed = TTYDEF_SPEED;
  141         if (!ospeed) ospeed = TTYDEF_SPEED;
  142 
  143         tio->c_iflag = tio->c_lflag = tio->c_oflag = 0;
  144         tio->c_cflag = CREAD | CS8 | HUPCL | (tio->c_cflag & CLOCAL);
  145 
  146         cfsetispeed(tio, ispeed);
  147         cfsetospeed(tio, ospeed);
  148 
  149 #ifndef __GNU__
  150         tio->c_line         = 0;
  151 #endif
  152         tio->c_cc[VTIME]    = 0;
  153         tio->c_cc[VMIN]     = 1;
  154 
  155         if (ioctl(fd, TIOCGWINSZ, &ws) == 0) {
  156             int set = 0;
  157             if (ws.ws_row == 0) {
  158                 ws.ws_row = 24;
  159                 set++;
  160             }
  161             if (ws.ws_col == 0) {
  162                 ws.ws_col = 80;
  163                 set++;
  164             }
  165             (void)ioctl(fd, TIOCSWINSZ, &ws);
  166         }
  167 
  168         goto setattr;
  169     }
  170 #if defined(SANE_TIO) && (SANE_TIO == 1)
  171     /*
  172      * Use defaults of <sys/ttydefaults.h> for base settings
  173      * of a local terminal line like a virtual console.
  174      */
  175     tio->c_iflag |= TTYDEF_IFLAG;
  176     tio->c_oflag |= TTYDEF_OFLAG;
  177     tio->c_lflag |= TTYDEF_LFLAG;
  178 # ifdef CBAUD
  179     tio->c_lflag &= ~CBAUD;
  180 # endif
  181     tio->c_cflag |= (B38400 | TTYDEF_CFLAG);
  182 
  183     /* Sane setting, allow eight bit characters, no carriage return delay
  184      * the same result as `stty sane cr0 pass8'
  185      */
  186     tio->c_iflag |=  (BRKINT | ICRNL | IMAXBEL);
  187     tio->c_iflag &= ~(IGNBRK | INLCR | IGNCR | IXOFF | IUCLC | IXANY | INPCK | ISTRIP);
  188     tio->c_oflag |=  (OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0);
  189     tio->c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | OFDEL |\
  190              NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
  191     tio->c_lflag |=  (ISIG | ICANON | IEXTEN | ECHO|ECHOE|ECHOK|ECHOKE);
  192     tio->c_lflag &= ~(ECHONL|ECHOCTL|ECHOPRT | NOFLSH | XCASE | TOSTOP);
  193     tio->c_cflag |=  (CREAD | CS8 | HUPCL);
  194     tio->c_cflag &= ~(PARODD | PARENB);
  195 
  196     /*
  197      * VTIME and VMIN can overlap with VEOF and VEOL since they are
  198      * only used for non-canonical mode. We just set the at the
  199      * beginning, so nothing bad should happen.
  200      */
  201     tio->c_cc[VTIME]    = 0;
  202     tio->c_cc[VMIN]     = CMIN;
  203     tio->c_cc[VINTR]    = CINTR;
  204     tio->c_cc[VQUIT]    = CQUIT;
  205     tio->c_cc[VERASE]   = CERASE; /* ASCII DEL (0177) */
  206     tio->c_cc[VKILL]    = CKILL;
  207     tio->c_cc[VEOF]     = CEOF;
  208 # ifdef VSWTC
  209     tio->c_cc[VSWTC]    = _POSIX_VDISABLE;
  210 # elif defined(VSWTCH)
  211     tio->c_cc[VSWTCH]   = _POSIX_VDISABLE;
  212 # endif
  213     tio->c_cc[VSTART]   = CSTART;
  214     tio->c_cc[VSTOP]    = CSTOP;
  215     tio->c_cc[VSUSP]    = CSUSP;
  216     tio->c_cc[VEOL]     = _POSIX_VDISABLE;
  217     tio->c_cc[VREPRINT] = CREPRINT;
  218     tio->c_cc[VDISCARD] = CDISCARD;
  219     tio->c_cc[VWERASE]  = CWERASE;
  220     tio->c_cc[VLNEXT]   = CLNEXT;
  221     tio->c_cc[VEOL2]    = _POSIX_VDISABLE;
  222 #endif
  223 setattr:
  224     /* Set line attributes */
  225     tcsetattr(fd, TCSANOW, tio);
  226 
  227     /* Enable blocking mode for read and write */
  228     if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
  229         (void)fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
  230 }
  231 
  232 
  233 /*
  234  *  Finalize the tty modes on modem lines.
  235  */
  236 static
  237 void tcfinal(struct console *con)
  238 {
  239     struct termios *tio = &con->tio;
  240     int fd = con->fd;
  241 
  242     /* Expected error */
  243     errno = 0;
  244 
  245     if ((con->flags & CON_SERIAL) == 0) {
  246 #ifdef __linux__
  247         setenv("TERM", "linux", 1);
  248 #else
  249         setenv("TERM", "vt100", 1);
  250 #endif
  251         return;
  252     }
  253     if (con->flags & CON_NOTTY)
  254         return;
  255     setenv("TERM", "vt100", 1);
  256 
  257     tio->c_iflag |= (IXON | IXOFF);
  258     tio->c_lflag |= (ICANON | ISIG | ECHO|ECHOE|ECHOK|ECHOKE);
  259     tio->c_oflag |= OPOST;
  260 
  261     tio->c_cc[VINTR]    = CINTR;
  262     tio->c_cc[VQUIT]    = CQUIT;
  263     tio->c_cc[VERASE]   = con->cp.erase;
  264     tio->c_cc[VKILL]    = con->cp.kill;
  265     tio->c_cc[VEOF]     = CEOF;
  266 #ifdef VSWTC
  267     tio->c_cc[VSWTC]    = _POSIX_VDISABLE;
  268 #elif defined(VSWTCH)
  269     tio->c_cc[VSWTCH]   = _POSIX_VDISABLE;
  270 #endif
  271     tio->c_cc[VSTART]   = CSTART;
  272     tio->c_cc[VSTOP]    = CSTOP;
  273     tio->c_cc[VSUSP]    = CSUSP;
  274     tio->c_cc[VEOL]     = _POSIX_VDISABLE;
  275 
  276     if (con->cp.eol == CR) {
  277         tio->c_iflag |= ICRNL;
  278         tio->c_iflag &= ~(INLCR|IGNCR);
  279         tio->c_oflag |= ONLCR;
  280         tio->c_oflag &= ~(OCRNL|ONLRET);
  281     }
  282 
  283     switch (con->cp.parity) {
  284     default:
  285     case 0:
  286         tio->c_cflag &= ~(PARODD | PARENB);
  287         tio->c_iflag &= ~(INPCK | ISTRIP);
  288         break;
  289     case 1:             /* odd parity */
  290         tio->c_cflag |= PARODD;
  291         /* fall through */
  292     case 2:             /* even parity */
  293         tio->c_cflag |= PARENB;
  294         tio->c_iflag |= (INPCK | ISTRIP);
  295         /* fall through */
  296     case (1 | 2):           /* no parity bit */
  297         tio->c_cflag &= ~CSIZE;
  298         tio->c_cflag |= CS7;
  299         break;
  300     }
  301 
  302     /* Set line attributes */
  303     (void)tcsetattr(fd, TCSANOW, tio);
  304 }
  305 
  306 /*
  307  *  Called at timeout.
  308  */
  309 static
  310 # ifdef __GNUC__
  311 __attribute__((__noinline__))
  312 void alrm_handler(int sig __attribute__((unused)))
  313 # else
  314 void alrm_handler(int sig)
  315 # endif
  316 {
  317     alarm_rised++;
  318 }
  319 
  320 /*
  321  *  Called at timeout.
  322  */
  323 static
  324 # ifdef __GNUC__
  325 __attribute__((__noinline__))
  326 void chld_handler(int sig __attribute__((unused)))
  327 # else
  328 void chld_handler(int sig)
  329 # endif
  330 {
  331     sigchild++;
  332 }
  333 
  334 /*
  335  *  See if an encrypted password is valid. The encrypted
  336  *  password is checked for traditional-style DES and
  337  *  FreeBSD-style MD5 encryption.
  338  */
  339 static
  340 int valid(const char *pass)
  341 {
  342     const char *s;
  343     char id[5];
  344     size_t len;
  345     off_t off;
  346 
  347     if (pass[0] == 0) return 1;
  348 #if CHECK_MD5
  349     if (pass[0] != '$') goto check_des;
  350 
  351     /*
  352      *  up to 4 bytes for the signature e.g. $1$
  353      */
  354     for(s = pass+1; *s && *s != '$'; s++)
  355         ;
  356     if (*s++ != '$') return 0;
  357     if ((off = (off_t)(s-pass)) > 4 || off < 3) return 0;
  358 
  359     memset(id, '\0', sizeof(id));
  360     strncpy(id, pass, off);
  361 
  362     /*
  363      *  up to 16 bytes for the salt
  364      */
  365     for(; *s && *s != '$'; s++)
  366         ;
  367     if (*s++ != '$') return 0;
  368     if ((off_t)(s-pass) > 16) return 0;
  369     len = strlen(s);
  370 
  371     /*
  372      *  the MD5 hash (128 bits or 16 bytes) encoded in base64 = 22 bytes
  373      */
  374     if ((strcmp(id, "$1$") == 0) && (len < 22 || len > 24)) return 0;
  375 
  376     /*
  377      *  the SHA-256 hash 43 bytes
  378      */
  379     if ((strcmp(id, "$5$") == 0) && (len < 42 || len > 44)) return 0;
  380 
  381     /*
  382      *      the SHA-512 hash 86 bytes
  383      */
  384     if ((strcmp(id, "$6$") == 0) && (len < 85 || len > 87)) return 0;
  385 
  386     /*
  387      *  e.g. Blowfish hash
  388      */
  389     return 1;
  390 check_des:
  391 #endif
  392 #if CHECK_DES
  393     if (strlen(pass) != 13) return 0;
  394     for (s = pass; *s; s++) {
  395         if ((*s < '0' || *s > '9') &&
  396             (*s < 'a' || *s > 'z') &&
  397             (*s < 'A' || *s > 'Z') &&
  398             *s != '.' && *s != '/') return 0;
  399     }
  400 #endif
  401     return 1;
  402 }
  403 
  404 /*
  405  *  Set a variable if the value is not NULL.
  406  */
  407 static
  408 void set(char **var, char *val)
  409 {
  410     if (val) *var = val;
  411 }
  412 
  413 /*
  414  *  Get the root password entry.
  415  */
  416 static
  417 struct passwd *getrootpwent(int try_manually)
  418 {
  419     static struct passwd pwd;
  420     struct passwd *pw;
  421     struct spwd *spw;
  422     FILE *fp;
  423     static char line[PASSWORD_LENGTH];
  424     static char sline[PASSWORD_LENGTH];
  425     char *p;
  426 
  427     /*
  428      *  First, we try to get the password the standard
  429      *  way using normal library calls.
  430      */
  431     if ((pw = getpwnam("root")) &&
  432         !strcmp(pw->pw_passwd, "x") &&
  433         (spw = getspnam("root")))
  434         pw->pw_passwd = spw->sp_pwdp;
  435     if (pw || !try_manually) return pw;
  436 
  437     /*
  438      *  If we come here, we could not retrieve the root
  439      *  password through library calls and we try to
  440      *  read the password and shadow files manually.
  441      */
  442     pwd.pw_name = "root";
  443     pwd.pw_passwd = "";
  444     pwd.pw_gecos = "Super User";
  445     pwd.pw_dir = "/";
  446     pwd.pw_shell = "";
  447     pwd.pw_uid = 0;
  448     pwd.pw_gid = 0;
  449 
  450     if ((fp = fopen(F_PASSWD, "r")) == NULL) {
  451         perror(F_PASSWD);
  452         return &pwd;
  453     }
  454 
  455     /*
  456      *  Find root in the password file.
  457      */
  458     while((p = fgets(line, PASSWORD_LENGTH, fp)) != NULL) {
  459         if (strncmp(line, "root:", 5) != 0)
  460             continue;
  461         p += 5;
  462         set(&pwd.pw_passwd, strsep(&p, ":"));
  463         (void)strsep(&p, ":");
  464         (void)strsep(&p, ":");
  465         set(&pwd.pw_gecos, strsep(&p, ":"));
  466         set(&pwd.pw_dir, strsep(&p, ":"));
  467         set(&pwd.pw_shell, strsep(&p, "\n"));
  468         p = line;
  469         break;
  470     }
  471     fclose(fp);
  472 
  473     /*
  474      *  If the encrypted password is valid
  475      *  or not found, return.
  476      */
  477     if (p == NULL) {
  478         fprintf(stderr, "sulogin: %s: no entry for root\n\r", F_PASSWD);
  479         return &pwd;
  480     }
  481     if (valid(pwd.pw_passwd)) return &pwd;
  482 
  483     /*
  484      *  The password is invalid. If there is a
  485      *  shadow password, try it.
  486      */
  487     strcpy(pwd.pw_passwd, "");
  488     if ((fp = fopen(F_SHADOW, "r")) == NULL) {
  489         fprintf(stderr, "sulogin: %s: root password garbled\n\r", F_PASSWD);
  490         return &pwd;
  491     }
  492     while((p = fgets(sline, PASSWORD_LENGTH, fp)) != NULL) {
  493         if (strncmp(sline, "root:", 5) != 0)
  494             continue;
  495         p += 5;
  496         set(&pwd.pw_passwd, strsep(&p, ":"));
  497         break;
  498     }
  499     fclose(fp);
  500 
  501     /*
  502      *  If the password is still invalid,
  503      *  NULL it, and return.
  504      */
  505     if (p == NULL) {
  506         fprintf(stderr, "sulogin: %s: no entry for root\n\r", F_SHADOW);
  507         strcpy(pwd.pw_passwd, "");
  508     }
  509     if (!valid(pwd.pw_passwd)) {
  510         fprintf(stderr, "sulogin: %s: root password garbled\n\r", F_SHADOW);
  511         strcpy(pwd.pw_passwd, ""); }
  512     return &pwd;
  513 }
  514 
  515 /*
  516  *  Ask by prompt for the password.
  517  */
  518 static
  519 void doprompt(const char *crypted, struct console *con)
  520 {
  521     struct termios tty;
  522 
  523     if (con->flags & CON_SERIAL) {
  524         tty = con->tio;
  525         /*
  526          * For prompting: map NL in output to CR-NL
  527          * otherwise we may see stairs in the output.
  528          */
  529         tty.c_oflag |= (ONLCR | OPOST);
  530         (void) tcsetattr(con->fd, TCSADRAIN, &tty);
  531     }
  532     if (con->file == (FILE*)0) {
  533         if  ((con->file = fdopen(con->fd, "r+")) == (FILE*)0)
  534             goto err;
  535     }
  536 #if defined(USE_ONELINE)
  537     if (crypted[0])
  538         fprintf(con->file, "Give root password for login: ");
  539     else
  540         fprintf(con->file, "Press enter for login: ");
  541 #else
  542     if (crypted[0])
  543         fprintf(con->file, "Give root password for maintenance\n\r");
  544     else
  545         fprintf(con->file, "Press enter for maintenance");
  546     fprintf(con->file, "(or type Control-D to continue): ");
  547 #endif
  548     fflush(con->file);
  549 err:
  550     if (con->flags & CON_SERIAL)
  551         (void) tcsetattr(con->fd, TCSADRAIN, &con->tio);
  552 }
  553 
  554 /*
  555  * Make sure to have an own session and controlling terminal
  556  */
  557 static
  558 void setup(struct console *con)
  559 {
  560     pid_t pid, pgrp, ppgrp, ttypgrp;
  561     int fd;
  562 
  563     if (con->flags & CON_NOTTY)
  564         return;
  565     fd = con->fd;
  566 
  567     /*
  568      *  Only go through this trouble if the new
  569      *  tty doesn't fall in this process group.
  570      */
  571     pid = getpid();
  572     pgrp = getpgid(0);
  573     ppgrp = getpgid(getppid());
  574     ttypgrp = tcgetpgrp(fd);
  575 
  576     if (pgrp != ttypgrp && ppgrp != ttypgrp) {
  577         if (pid != getsid(0)) {
  578             if (pid == getpgid(0))
  579                 setpgid(0, getpgid(getppid()));
  580             setsid();
  581         }
  582 
  583         signal(SIGHUP, SIG_IGN);
  584         if (ttypgrp > 0)
  585             ioctl(0, TIOCNOTTY, (char *)1);
  586         signal(SIGHUP, saved_sighup);
  587         if (fd > 0) close(0);
  588         if (fd > 1) close(1);
  589         if (fd > 2) close(2);
  590 
  591         ioctl(fd, TIOCSCTTY, (char *)1);
  592         tcsetpgrp(fd, ppgrp);
  593     }
  594     dup2(fd, 0);
  595     dup2(fd, 1);
  596     dup2(fd, 2);
  597     con->fd = 0;
  598 
  599     for (fd = 3; fd < 32; fd++) {
  600         if (openfd & (1<<fd)) {
  601             close(fd);
  602             openfd &= ~(1<<fd);
  603         }
  604     }
  605 }
  606 
  607 /*
  608  *  Fetch the password. Note that there is no
  609  *  default timeout as we normally skip this during boot.
  610  */
  611 static
  612 char *getpasswd(struct console *con)
  613 {
  614     static char pass[128], *ptr;
  615     struct sigaction sa;
  616     struct chardata *cp;
  617     struct termios tty;
  618     char *ret = pass;
  619     unsigned char tc;
  620     char c, ascval;
  621     int eightbit;
  622     int fd;
  623 
  624     if (con->flags & CON_NOTTY)
  625         goto out;
  626     fd = con->fd;
  627     cp = &con->cp;
  628 
  629     tty = con->tio;
  630     tty.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
  631     tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP|ISIG);
  632     tc = (tcsetattr(fd, TCSAFLUSH, &tty) == 0);
  633 
  634     sa.sa_handler = alrm_handler;
  635     sa.sa_flags = 0;
  636     sigaction(SIGALRM, &sa, NULL);
  637     if (timeout) alarm(timeout);
  638 
  639     ptr = &pass[0];
  640     cp->eol = *ptr = '\0';
  641 
  642     eightbit = ((con->flags & CON_SERIAL) == 0 || (tty.c_cflag & (PARODD|PARENB)) == 0);
  643     while (cp->eol == '\0') {
  644         if (read(fd, &c, 1) < 1) {
  645             if (errno == EINTR || errno == EAGAIN) {
  646                 usleep(1000);
  647                 continue;
  648             }
  649             ret = (char*)0;
  650             switch (errno) {
  651             case 0:
  652             case EIO:
  653             case ESRCH:
  654             case EINVAL:
  655             case ENOENT:
  656                 break;
  657             default:
  658                 fprintf(stderr, "sulogin: read(%s): %m\n\r", con->tty);
  659                 break;
  660             }
  661             goto quit;
  662         }
  663 
  664         if (eightbit)
  665             ascval = c;
  666         else if (c != (ascval = (c & 0177))) {
  667             uint32_t bits, mask;
  668             for (bits = 1, mask = 1; mask & 0177; mask <<= 1) {
  669                 if (mask & ascval)
  670                     bits++;
  671             }
  672             cp->parity |= ((bits & 1) ? 1 : 2);
  673         }
  674 
  675         switch (ascval) {
  676         case 0:
  677             *ptr = '\0';
  678             goto quit; 
  679         case CR:
  680         case NL:
  681             *ptr = '\0';
  682             cp->eol = ascval;
  683             break;
  684         case BS:
  685         case CERASE:
  686             cp->erase = ascval;
  687             if (ptr > &pass[0])
  688                 ptr--;
  689             break;
  690         case CKILL:
  691             cp->kill = ascval;
  692             while (ptr > &pass[0])
  693                 ptr--;
  694             break;
  695         case CEOF:
  696             goto quit;      
  697         default:
  698             if ((size_t)(ptr - &pass[0]) >= (sizeof(pass) -1 )) {
  699                  fprintf(stderr, "sulogin: input overrun at %s\n\r", con->tty);
  700                  ret = (char*)0;
  701                  goto quit;
  702             }
  703             *ptr++ = ascval;
  704             break;
  705         }
  706     }
  707 quit:
  708     alarm(0);
  709     if (tc)
  710         (void)tcsetattr(fd, TCSAFLUSH, &con->tio);
  711     if (ret && *ret != '\0')
  712         tcfinal(con);
  713     printf("\r\n");
  714 out:
  715     return ret;
  716 }
  717 
  718 /*
  719  *  Password was OK, execute a shell.
  720  */
  721 static
  722 void sushell(struct passwd *pwd)
  723 {
  724     char shell[128];
  725     char home[128];
  726     char *p;
  727     char *sushell;
  728 
  729     /*
  730      *  Set directory and shell.
  731      */
  732     if (chdir(pwd->pw_dir) < 0) {
  733         if (chdir("/") < 0)
  734             fprintf(stderr, "sulogin: change of working directory failed: %m\n\r");
  735     }
  736     if ((p = getenv("SUSHELL")) != NULL)
  737         sushell = p;
  738     else if ((p = getenv("sushell")) != NULL)
  739         sushell = p;
  740     else {
  741         if (pwd->pw_shell[0])
  742             sushell = pwd->pw_shell;
  743         else
  744             sushell = BINSH;
  745     }
  746     if ((p = strrchr(sushell, '/')) == NULL)
  747         p = sushell;
  748     else
  749         p++;
  750     snprintf(shell, sizeof(shell), profile ? "-%s" : "%s", p);
  751 
  752     /*
  753      *  Set some important environment variables.
  754      */
  755     if (getcwd(home, sizeof(home)) == (char*)0)
  756         strcpy(home, "/");
  757     setenv("HOME", home, 1);
  758     setenv("LOGNAME", "root", 1);
  759     setenv("USER", "root", 1);
  760     if (!profile)
  761         setenv("SHLVL","0",1);
  762 
  763     /*
  764      *  Try to execute a shell.
  765      */
  766     setenv("SHELL", sushell, 1);
  767     signal(SIGINT,  saved_sigint);
  768     signal(SIGTSTP, saved_sigtstp);
  769     signal(SIGQUIT, saved_sigquit);
  770     signal(SIGHUP,  SIG_DFL);
  771 #ifdef WITH_SELINUX
  772     if (is_selinux_enabled() > 0) {
  773         security_context_t scon=NULL;
  774         char *seuser=NULL;
  775         char *level=NULL;
  776         if (getseuserbyname("root", &seuser, &level) == 0)
  777             if (get_default_context_with_level(seuser, level, 0, &scon) == 0) {
  778                 if (setexeccon(scon) != 0) 
  779                     fprintf(stderr, "sulogin: setexeccon failed\n\r");
  780                 freecon(scon);
  781             }
  782         free(seuser);
  783         free(level);
  784     }
  785 #endif
  786     execl(sushell, shell, NULL);
  787     perror(sushell);
  788 
  789     setenv("SHELL", BINSH, 1);
  790     execl(BINSH, profile ? "-sh" : "sh", NULL);
  791     perror(BINSH);
  792 
  793     /* Fall back to statically linked shell if both the users shell
  794        and /bin/sh failed to execute. */
  795     setenv("SHELL", STATICSH, 1);
  796     execl(STATICSH, STATICSH, NULL);
  797     perror(STATICSH);
  798 }
  799 
  800 #ifdef __linux__
  801 /*
  802  * Make C library standard calls like ttyname(3) work.
  803  */
  804 static uint32_t mounts;
  805 #define MNT_PROCFS  0x0001
  806 #define MNT_DEVTMPFS    0x0002
  807 
  808 static __attribute__((__noinline__))
  809 void putmounts(void)
  810 {
  811     if (mounts & MNT_DEVTMPFS)
  812         umount2("/dev", MNT_DETACH);
  813     if (mounts & MNT_PROCFS)
  814         umount2("/proc", MNT_DETACH);
  815 }
  816 
  817 static __attribute__((__constructor__))
  818 void getmounts(void)
  819 {
  820     struct statfs st;
  821     if (statfs("/proc", &st) == 0 && st.f_type != PROC_SUPER_MAGIC) {
  822         if (mount("proc", "/proc", "proc", MS_RELATIME, NULL) == 0)
  823             mounts |= MNT_PROCFS;
  824     }
  825     if (statfs("/dev", &st) == 0 && st.f_type != TMPFS_MAGIC) {
  826         if (mount("devtmpfs", "/dev", "devtmpfs", MS_RELATIME, "mode=0755,nr_inodes=0") == 0) {
  827             mounts |= MNT_DEVTMPFS;
  828             (void)mknod("/dev/console", S_IFCHR|S_IRUSR|S_IWUSR, makedev(TTYAUX_MAJOR, 1));
  829             if (symlink("/proc/self/fd", "/dev/fd") == 0) {
  830                 dovoid(symlink("fd/0", "/dev/stdin"));
  831                 dovoid(symlink("fd/1", "/dev/stdout"));
  832                 dovoid(symlink("fd/2", "/dev/stderr"));
  833             }
  834         }
  835     }
  836     if (mounts) atexit(putmounts);
  837 }
  838 #endif
  839 
  840 static
  841 void usage(void)
  842 {
  843     fprintf(stderr, "Usage: sulogin [-e] [-p] [-t timeout] [tty device]\n\r");
  844 }
  845 
  846 int main(int argc, char **argv)
  847 {
  848     char *tty = NULL;
  849     struct passwd *pwd;
  850     int c, status = 0;
  851     int reconnect = 0;
  852     int opt_e = 0;
  853     struct console *con;
  854     pid_t pid;
  855 
  856     /*
  857      * We are init. We hence need to set uo a session.
  858      */
  859     if ((pid = getpid()) == 1) {
  860         setsid();
  861         (void)ioctl(0, TIOCSCTTY, (char *)1);
  862     }
  863 
  864     /*
  865      * See if we have a timeout flag.
  866      */
  867     opterr = 0;
  868     while((c = getopt(argc, argv, "ept:")) != EOF) switch(c) {
  869         case 't':
  870             timeout = atoi(optarg);
  871             break;
  872         case 'p':
  873             profile = 1;
  874             break;
  875         case 'e':
  876             opt_e = 1;
  877             break;
  878         default:
  879             usage();
  880             /* Do not exit! */
  881             break;
  882     }
  883 
  884     if (geteuid() != 0) {
  885         fprintf(stderr, "sulogin: only root can run sulogin.\n\r");
  886         exit(1);
  887     }
  888 
  889     saved_sigint  = signal(SIGINT,  SIG_IGN);
  890     saved_sigquit = signal(SIGQUIT, SIG_IGN);
  891     saved_sigtstp = signal(SIGTSTP, SIG_IGN);
  892     saved_sighup  = signal(SIGHUP,  SIG_IGN);
  893 
  894     /*
  895      * See if we need to open an other tty device.
  896      */
  897     if (optind < argc)
  898         tty = argv[optind];
  899     if (!tty || *tty == '\0') 
  900         tty = getenv("CONSOLE");
  901 
  902     /*
  903      * Detect possible consoles, use stdin as fallback.
  904      * If an optional tty is given, reconnect it to stdin.
  905      */
  906     reconnect = detect_consoles(tty, 0);
  907 
  908     /*
  909      * Should not happen
  910      */
  911     if (!consoles) {
  912         if (!errno)
  913             errno = ENOMEM;
  914         fprintf(stderr, "sulogin: cannot open console: %m\n\r");
  915         exit(1);
  916     }
  917 
  918     /*
  919      * If previous stdin was not the speified tty and therefore reconnected
  920      * to the specified tty also reconnect stdout and stderr.
  921      */
  922     if (reconnect) {
  923         if (isatty(1) == 0)
  924             dup2(0, 1);
  925         if (isatty(2) == 0)
  926             dup2(0, 2);
  927     }
  928 
  929     /*
  930      *  Get the root password.
  931      */
  932     if ((pwd = getrootpwent(opt_e)) == NULL) {
  933         fprintf(stderr, "sulogin: cannot open password database!\n\r");
  934         sleep(2);
  935     }
  936 
  937     /*
  938      * Prompt for input on the consoles
  939      */
  940     for (con = consoles; con && con->id < CONMAX; con = con->next) {
  941         if (con->fd >= 0) {
  942             openfd |= (1<<con->fd);
  943             tcinit(con);
  944             continue;
  945         }
  946         if ((con->fd = open(con->tty, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0)
  947             continue;
  948         openfd |= (1<<con->fd);
  949         tcinit(con);
  950     }
  951     con = consoles;
  952     usemask = (uint32_t*)mmap(NULL, sizeof(uint32_t), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0);
  953 
  954     if (con->next == (struct console*)0)
  955         goto nofork;
  956 
  957     signal(SIGCHLD, chld_handler);
  958     do {
  959         switch ((con->pid = fork())) {
  960         case 0:
  961             signal(SIGCHLD,  SIG_DFL);
  962             /* fall through */
  963         nofork:
  964             setup(con);
  965             while (1) {
  966                 char *passwd = pwd->pw_passwd;
  967                 char *answer;
  968                 int failed = 0, doshell = 0;
  969 
  970                 doprompt(passwd, con);
  971                 if ((answer = getpasswd(con)) == NULL)
  972                     break;
  973 
  974                 if (passwd[0] == '\0')
  975                     doshell++;
  976                 else {
  977                     char *cryptbuf;
  978                     cryptbuf = crypt(answer, passwd);
  979                     if (cryptbuf == NULL)
  980                         fprintf(stderr, "sulogin: crypt failed: %m\n\r");
  981                     else if (strcmp(cryptbuf, pwd->pw_passwd) == 0)
  982                         doshell++;
  983                 }
  984 
  985                 if (doshell) {
  986                     *usemask |= (1<<con->id);
  987                     sushell(pwd);
  988                     *usemask &= ~(1<<con->id);
  989                     failed++;
  990                 }
  991 
  992                 signal(SIGQUIT, SIG_IGN);
  993                 signal(SIGTSTP, SIG_IGN);
  994                 signal(SIGINT,  SIG_IGN);
  995 
  996                 if (failed) {
  997                     fprintf(stderr, "sulogin: can not execute su shell.\n\r");
  998                     break;
  999                 }
 1000                 fprintf(stderr, "Login incorrect.\n\r");
 1001                 sleep(3);
 1002             }
 1003             if (alarm_rised) {
 1004                 tcfinal(con);
 1005                 fprintf(stderr, "Timed out.\n\r");
 1006             }
 1007             /*
 1008              *  User may pressed Control-D.
 1009              */
 1010             exit(0);
 1011         case -1:
 1012             fprintf(stderr, "sulogin: can not fork: %m\n\r");
 1013             /* fall through */
 1014         default:
 1015             break;
 1016         }
 1017     } while ((con = con->next) && (con->id < CONMAX));
 1018 
 1019     while ((pid = wait(&status))) {
 1020         if (errno == ECHILD)
 1021             break;
 1022         if (pid < 0)
 1023             continue;
 1024         for (con = consoles; con && con->id < CONMAX; con = con->next) {
 1025             if (con->pid == pid) {
 1026                 *usemask &= ~(1<<con->id);
 1027                 continue;
 1028             }
 1029             if (kill(con->pid, 0) < 0) {
 1030                 *usemask &= ~(1<<con->id);
 1031                 continue;
 1032             }
 1033             if (*usemask & (1<<con->id))
 1034                 continue;
 1035             kill(con->pid, SIGHUP);
 1036             usleep(5000);
 1037             kill(con->pid, SIGKILL);
 1038         }
 1039     }
 1040     signal(SIGCHLD,  SIG_DFL);
 1041 
 1042     return 0;
 1043 }