]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/sulogin.c
Merge branch 'dmesg-subsecond' of https://github.com/t-8ch/util-linux
[thirdparty/util-linux.git] / login-utils / sulogin.c
1 /*
2 * sulogin
3 *
4 * This program gives Linux machines a reasonable secure way to boot single
5 * user. It forces the user to supply the root password before a shell is
6 * started. If there is a shadow password file and the encrypted root password
7 * is "x" the shadow password will be used.
8 *
9 * Copyright (C) 1998-2003 Miquel van Smoorenburg.
10 * Copyright (C) 2012 Karel Zak <kzak@redhat.com>
11 * Copyright (C) 2012 Werner Fink <werner@suse.de>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 */
27 #include <sys/mman.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <signal.h>
37 #include <pwd.h>
38 #include <shadow.h>
39 #include <termios.h>
40 #include <errno.h>
41 #include <getopt.h>
42 #include <sys/ioctl.h>
43 #ifdef HAVE_CRYPT_H
44 # include <crypt.h>
45 #endif
46
47 #ifdef HAVE_LIBSELINUX
48 # include <selinux/selinux.h>
49 # include <selinux/get_context_list.h>
50 #endif
51
52 #ifdef __linux__
53 # include <sys/kd.h>
54 # include <sys/param.h>
55 # include <linux/serial.h>
56 #endif
57
58 #include "c.h"
59 #include "closestream.h"
60 #include "env.h"
61 #include "nls.h"
62 #include "pathnames.h"
63 #ifdef USE_PLYMOUTH_SUPPORT
64 # include "plymouth-ctrl.h"
65 #endif
66 #include "strutils.h"
67 #include "ttyutils.h"
68 #include "sulogin-consoles.h"
69 #define CONMAX 16
70
71 static unsigned int timeout;
72 static int profile;
73 static volatile uint32_t openfd; /* Remember higher file descriptors */
74
75 static struct sigaction saved_sigint;
76 static struct sigaction saved_sigtstp;
77 static struct sigaction saved_sigquit;
78 static struct sigaction saved_sighup;
79 static struct sigaction saved_sigchld;
80
81 static volatile sig_atomic_t alarm_rised;
82 static volatile sig_atomic_t sigchild;
83
84 #define SULOGIN_PASSWORD_BUFSIZ 128
85
86 #ifndef IUCLC
87 # define IUCLC 0
88 #endif
89
90 #ifndef WEXITED
91 # warning "WEXITED is missing, sulogin may not work as expected"
92 # define WEXITED 0
93 #endif
94
95 static int locked_account_password(const char * const passwd)
96 {
97 if (passwd && (*passwd == '*' || *passwd == '!'))
98 return 1;
99 return 0;
100 }
101
102 /*
103 * Fix the tty modes and set reasonable defaults.
104 */
105 static void tcinit(struct console *con)
106 {
107 int flags = 0, mode = 0;
108 struct termios *tio = &con->tio;
109 const int fd = con->fd;
110 #if defined(TIOCGSERIAL)
111 struct serial_struct serinfo;
112 #endif
113 #ifdef USE_PLYMOUTH_SUPPORT
114 struct termios lock;
115 int i = (plymouth_command(MAGIC_PING)) ? PLYMOUTH_TERMIOS_FLAGS_DELAY : 0;
116 if (i)
117 plymouth_command(MAGIC_QUIT);
118 while (i-- > 0) {
119 /*
120 * With plymouth the termios flags become changed after this
121 * function had changed the termios.
122 */
123 memset(&lock, 0, sizeof(struct termios));
124 if (ioctl(fd, TIOCGLCKTRMIOS, &lock) < 0)
125 break;
126 if (!lock.c_iflag && !lock.c_oflag && !lock.c_cflag && !lock.c_lflag)
127 break;
128 sleep(1);
129 }
130 memset(&lock, 0, sizeof(struct termios));
131 ioctl(fd, TIOCSLCKTRMIOS, &lock);
132 errno = 0;
133 #endif
134
135 #if defined(TIOCGSERIAL)
136 if (ioctl(fd, TIOCGSERIAL, &serinfo) >= 0)
137 con->flags |= CON_SERIAL;
138 errno = 0;
139 #else
140 # if defined(KDGKBMODE)
141 if (ioctl(fd, KDGKBMODE, &mode) < 0)
142 con->flags |= CON_SERIAL;
143 errno = 0;
144 # endif
145 #endif
146
147 if (tcgetattr(fd, tio) < 0) {
148 int saveno = errno;
149 #if defined(KDGKBMODE) || defined(TIOCGSERIAL)
150 if (con->flags & CON_SERIAL) { /* Try to recover this */
151
152 # if defined(TIOCGSERIAL)
153 serinfo.flags |= ASYNC_SKIP_TEST; /* Skip test of UART */
154
155 if (ioctl(fd, TIOCSSERIAL, &serinfo) < 0)
156 goto tcgeterr;
157 if (ioctl(fd, TIOCSERCONFIG) < 0) /* Try to autoconfigure */
158 goto tcgeterr;
159 if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0)
160 goto tcgeterr; /* Ouch */
161 # endif
162 if (tcgetattr(fd, tio) < 0) /* Retry to get tty attributes */
163 saveno = errno;
164 }
165 # if defined(TIOCGSERIAL)
166 tcgeterr:
167 # endif
168 if (saveno)
169 #endif
170 {
171 FILE *fcerr = fdopen(fd, "w");
172 if (fcerr) {
173 fprintf(fcerr, _("tcgetattr failed"));
174 fclose(fcerr);
175 }
176 warn(_("tcgetattr failed"));
177
178 con->flags &= ~CON_SERIAL;
179 if (saveno != EIO)
180 con->flags |= CON_NOTTY;
181 else
182 con->flags |= CON_EIO;
183
184 errno = 0;
185 return;
186 }
187 }
188
189 /* Handle lines other than virtual consoles here */
190 #if defined(KDGKBMODE) || defined(TIOCGSERIAL)
191 if (con->flags & CON_SERIAL)
192 #endif
193 {
194 speed_t ispeed, ospeed;
195 struct winsize ws;
196 errno = 0;
197
198 /* Flush input and output queues on modem lines */
199 tcflush(fd, TCIOFLUSH);
200
201 ispeed = cfgetispeed(tio);
202 ospeed = cfgetospeed(tio);
203
204 if (!ispeed) ispeed = TTYDEF_SPEED;
205 if (!ospeed) ospeed = TTYDEF_SPEED;
206
207 tio->c_cflag = CREAD | CS8 | HUPCL | (tio->c_cflag & CLOCAL);
208 tio->c_iflag = 0;
209 tio->c_lflag = 0;
210 tio->c_oflag &= OPOST | ONLCR;
211
212 cfsetispeed(tio, ispeed);
213 cfsetospeed(tio, ospeed);
214
215 #ifdef HAVE_STRUCT_TERMIOS_C_LINE
216 tio->c_line = 0;
217 #endif
218 tio->c_cc[VTIME] = 0;
219 tio->c_cc[VMIN] = 1;
220
221 if (ioctl(fd, TIOCGWINSZ, &ws) == 0) {
222 int update = 0;
223
224 if (ws.ws_row == 0) {
225 ws.ws_row = 24;
226 update++;
227 }
228 if (ws.ws_col == 0) {
229 ws.ws_col = 80;
230 update++;
231 }
232 if (update)
233 ignore_result( ioctl(fd, TIOCSWINSZ, &ws) );
234 }
235
236 setlocale(LC_CTYPE, "POSIX");
237 goto setattr;
238 }
239 #if defined(IUTF8) && defined(KDGKBMODE)
240 /* Handle mode of current keyboard setup, e.g. for UTF-8 */
241 switch(mode) {
242 case K_UNICODE:
243 setlocale(LC_CTYPE, "C.UTF-8");
244 flags |= UL_TTY_UTF8;
245 break;
246 case K_RAW:
247 case K_MEDIUMRAW:
248 case K_XLATE:
249 default:
250 setlocale(LC_CTYPE, "POSIX");
251 break;
252 }
253 #else
254 setlocale(LC_CTYPE, "POSIX");
255 #endif
256 reset_virtual_console(tio, flags);
257 setattr:
258 if (tcsetattr(fd, TCSANOW, tio))
259 warn(_("tcsetattr failed"));
260
261 /* Enable blocking mode for read and write */
262 if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
263 ignore_result( fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) );
264 }
265
266 /*
267 * Finalize the tty modes on modem lines.
268 */
269 static void tcfinal(struct console *con)
270 {
271 struct termios *tio = &con->tio;
272 const int fd = con->fd;
273
274 if (con->flags & CON_EIO)
275 return;
276 if ((con->flags & CON_SERIAL) == 0) {
277 xsetenv("TERM", "linux", 1);
278 return;
279 }
280 if (con->flags & CON_NOTTY) {
281 xsetenv("TERM", "dumb", 1);
282 return;
283 }
284
285 #if defined (__s390__) || defined (__s390x__)
286 xsetenv("TERM", "dumb", 1);
287 #else
288 xsetenv("TERM", "vt102", 1);
289 #endif
290 tio->c_iflag |= (IXON | IXOFF);
291 tio->c_lflag |= (ICANON | ISIG | ECHO|ECHOE|ECHOK|ECHOKE);
292 tio->c_oflag |= OPOST;
293
294 tio->c_cc[VINTR] = CINTR;
295 tio->c_cc[VQUIT] = CQUIT;
296 tio->c_cc[VERASE] = con->cp.erase;
297 tio->c_cc[VKILL] = con->cp.kill;
298 tio->c_cc[VEOF] = CEOF;
299 #ifdef VSWTC
300 tio->c_cc[VSWTC] = _POSIX_VDISABLE;
301 #elif defined(VSWTCH)
302 tio->c_cc[VSWTCH] = _POSIX_VDISABLE;
303 #endif
304 tio->c_cc[VSTART] = CSTART;
305 tio->c_cc[VSTOP] = CSTOP;
306 tio->c_cc[VSUSP] = CSUSP;
307 tio->c_cc[VEOL] = _POSIX_VDISABLE;
308
309 if (con->cp.eol == CR) {
310 tio->c_iflag |= ICRNL;
311 tio->c_iflag &= ~(INLCR|IGNCR);
312 tio->c_oflag |= ONLCR;
313 tio->c_oflag &= ~(OCRNL|ONLRET);
314 }
315
316 switch (con->cp.parity) {
317 default:
318 case 0:
319 tio->c_cflag &= ~(PARODD | PARENB);
320 tio->c_iflag &= ~(INPCK | ISTRIP);
321 break;
322 case 1: /* odd parity */
323 tio->c_cflag |= PARODD;
324 /* fallthrough */
325 case 2: /* even parity */
326 tio->c_cflag |= PARENB;
327 tio->c_iflag |= (INPCK | ISTRIP);
328 /* fallthrough */
329 case (1 | 2): /* no parity bit */
330 tio->c_cflag &= ~CSIZE;
331 tio->c_cflag |= CS7;
332 break;
333 }
334
335 /* Set line attributes */
336 tcsetattr(fd, TCSANOW, tio);
337 }
338
339 /*
340 * Called at timeout.
341 */
342 static void alrm_handler(int sig __attribute__((unused)))
343 {
344 /* Timeout expired */
345 alarm_rised = 1;
346 }
347
348 static void chld_handler(int sig __attribute__((unused)))
349 {
350 sigchild = 1;
351 }
352
353 static void mask_signal(int signal, void (*handler)(int),
354 struct sigaction *origaction)
355 {
356 struct sigaction newaction;
357
358 newaction.sa_handler = handler;
359 sigemptyset(&newaction.sa_mask);
360 newaction.sa_flags = 0;
361
362 sigaction(signal, &newaction, origaction);
363 }
364
365 static void unmask_signal(int signal, struct sigaction *sa)
366 {
367 sigaction(signal, sa, NULL);
368 }
369
370 /*
371 * See if an encrypted password is valid. The encrypted password is checked for
372 * traditional-style DES and FreeBSD-style MD5 encryption.
373 */
374 static int valid(const char *pass)
375 {
376 const char *s;
377 char id[5];
378 size_t len;
379 off_t off;
380
381 if (pass[0] == 0)
382 return 1;
383 if (pass[0] != '$')
384 goto check_des;
385
386 /*
387 * up to 4 bytes for the signature e.g. $1$
388 */
389 for (s = pass+1; *s && *s != '$'; s++);
390
391 if (*s++ != '$')
392 return 0;
393
394 if ((off = (off_t)(s-pass)) > 4 || off < 3)
395 return 0;
396
397 memset(id, '\0', sizeof(id));
398 strncpy(id, pass, off);
399
400 /*
401 * up to 16 bytes for the salt
402 */
403 for (; *s && *s != '$'; s++);
404
405 if (*s++ != '$')
406 return 0;
407
408 if ((off_t)(s-pass) > 16)
409 return 0;
410
411 len = strlen(s);
412
413 /*
414 * the MD5 hash (128 bits or 16 bytes) encoded in base64 = 22 bytes
415 */
416 if ((strcmp(id, "$1$") == 0) && (len < 22 || len > 24))
417 return 0;
418
419 /*
420 * the SHA-256 hash 43 bytes
421 */
422 if ((strcmp(id, "$5$") == 0) && (len < 42 || len > 44))
423 return 0;
424
425 /*
426 * the SHA-512 hash 86 bytes
427 */
428 if ((strcmp(id, "$6$") == 0) && (len < 85 || len > 87))
429 return 0;
430
431 /*
432 * e.g. Blowfish hash
433 */
434 return 1;
435 check_des:
436 if (strlen(pass) != 13)
437 return 0;
438
439 for (s = pass; *s; s++) {
440 if ((*s < '0' || *s > '9') &&
441 (*s < 'a' || *s > 'z') &&
442 (*s < 'A' || *s > 'Z') &&
443 *s != '.' && *s != '/')
444 return 0;
445 }
446 return 1;
447 }
448
449 /*
450 * Set a variable if the value is not NULL.
451 */
452 static inline void set(char **var, char *val)
453 {
454 if (val)
455 *var = val;
456 }
457
458 /*
459 * Get the root password entry.
460 */
461 static struct passwd *getrootpwent(int try_manually)
462 {
463 static struct passwd pwd;
464 struct passwd *pw;
465 struct spwd *spw;
466 FILE *fp;
467 static char line[2 * BUFSIZ];
468 static char sline[2 * BUFSIZ];
469 char *p;
470
471 /*
472 * First, we try to get the password the standard way using normal
473 * library calls.
474 */
475 if ((pw = getpwnam("root")) &&
476 !strcmp(pw->pw_passwd, "x") &&
477 (spw = getspnam("root")))
478 pw->pw_passwd = spw->sp_pwdp;
479
480 if (pw || !try_manually)
481 return pw;
482
483 /*
484 * If we come here, we could not retrieve the root password through
485 * library calls and we try to read the password and shadow files
486 * manually.
487 */
488 pwd.pw_name = "root";
489 pwd.pw_passwd = "";
490 pwd.pw_gecos = "Super User";
491 pwd.pw_dir = "/";
492 pwd.pw_shell = "";
493 pwd.pw_uid = 0;
494 pwd.pw_gid = 0;
495
496 if ((fp = fopen(_PATH_PASSWD, "r")) == NULL) {
497 warn(_("cannot open %s"), _PATH_PASSWD);
498 return &pwd;
499 }
500
501 /*
502 * Find root in the password file.
503 */
504 while ((p = fgets(line, sizeof(line), fp)) != NULL) {
505 if (strncmp(line, "root:", 5) != 0)
506 continue;
507 p += 5;
508 set(&pwd.pw_passwd, strsep(&p, ":"));
509 strsep(&p, ":");
510 strsep(&p, ":");
511 set(&pwd.pw_gecos, strsep(&p, ":"));
512 set(&pwd.pw_dir, strsep(&p, ":"));
513 set(&pwd.pw_shell, strsep(&p, "\n"));
514 p = line;
515 break;
516 }
517 fclose(fp);
518
519 /*
520 * If the encrypted password is valid or not found, return.
521 */
522 if (p == NULL) {
523 warnx(_("%s: no entry for root\n"), _PATH_PASSWD);
524 return &pwd;
525 }
526 if (valid(pwd.pw_passwd))
527 return &pwd;
528
529 /*
530 * The password is invalid. If there is a shadow password, try it.
531 */
532 *pwd.pw_passwd = '\0';
533 if ((fp = fopen(_PATH_SHADOW_PASSWD, "r")) == NULL) {
534 warn(_("cannot open %s"), _PATH_PASSWD);
535 return &pwd;
536 }
537 while ((p = fgets(sline, sizeof(sline), fp)) != NULL) {
538 if (strncmp(sline, "root:", 5) != 0)
539 continue;
540 p += 5;
541 set(&pwd.pw_passwd, strsep(&p, ":"));
542 break;
543 }
544 fclose(fp);
545
546 /*
547 * If the password is still invalid, NULL it, and return.
548 */
549 if (p == NULL) {
550 warnx(_("%s: no entry for root"), _PATH_SHADOW_PASSWD);
551 *pwd.pw_passwd = '\0';
552 }
553 /* locked account passwords are valid too */
554 if (!locked_account_password(pwd.pw_passwd) && !valid(pwd.pw_passwd)) {
555 warnx(_("%s: root password garbled"), _PATH_SHADOW_PASSWD);
556 *pwd.pw_passwd = '\0';
557 }
558 return &pwd;
559 }
560
561 /*
562 * Ask by prompt for the password.
563 */
564 static void doprompt(const char *crypted, struct console *con, int deny)
565 {
566 struct termios tty;
567
568 if (con->flags & CON_SERIAL) {
569 tty = con->tio;
570 /*
571 * For prompting: map NL in output to CR-NL
572 * otherwise we may see stairs in the output.
573 */
574 tty.c_oflag |= (ONLCR | OPOST);
575 tcsetattr(con->fd, TCSADRAIN, &tty);
576 }
577 if (!con->file) {
578 con->file = fdopen(con->fd, "r+");
579 if (!con->file)
580 goto err;
581 }
582
583 if (deny)
584 fprintf(con->file, _("\nCannot open access to console, the root account is locked.\n"
585 "See sulogin(8) man page for more details.\n\n"
586 "Press Enter to continue.\n"));
587 else {
588 #if defined(USE_ONELINE)
589 if (crypted[0] && !locked_account_password(crypted))
590 fprintf(con->file, _("Give root password for login: "));
591 else
592 fprintf(con->file, _("Press Enter for login: "));
593 #else
594 if (crypted[0] && !locked_account_password(crypted))
595 fprintf(con->file, _("Give root password for maintenance\n"));
596 else
597 fprintf(con->file, _("Press Enter for maintenance\n"));
598 fprintf(con->file, _("(or press Control-D to continue): "));
599 #endif
600 }
601 fflush(con->file);
602 err:
603 if (con->flags & CON_SERIAL)
604 tcsetattr(con->fd, TCSADRAIN, &con->tio);
605 }
606
607 /*
608 * Make sure to have an own session and controlling terminal
609 */
610 static void setup(struct console *con)
611 {
612 int fd = con->fd;
613 const pid_t pid = getpid(), pgrp = getpgid(0), ppgrp = getpgid(getppid());
614 pid_t ttypgrp;
615
616 if (con->flags & CON_NOTTY)
617 goto notty;
618 if (con->flags & CON_EIO)
619 return;
620
621 ttypgrp = tcgetpgrp(fd);
622
623 /*
624 * Only go through this trouble if the new
625 * tty doesn't fall in this process group.
626 */
627 if (pgrp != ttypgrp && ppgrp != ttypgrp) {
628 if (pid != getsid(0)) {
629 if (pid == getpgid(0))
630 setpgid(0, getpgid(getppid()));
631 setsid();
632 }
633
634 mask_signal(SIGHUP, SIG_IGN, &saved_sighup);
635 if (ttypgrp > 0)
636 ioctl(STDIN_FILENO, TIOCNOTTY, (char *)1);
637 unmask_signal(SIGHUP, &saved_sighup);
638 if (fd > STDIN_FILENO) close(STDIN_FILENO);
639 if (fd > STDOUT_FILENO) close(STDOUT_FILENO);
640 if (fd > STDERR_FILENO) close(STDERR_FILENO);
641
642 ioctl(fd, TIOCSCTTY, (char *)1);
643 tcsetpgrp(fd, ppgrp);
644 }
645 notty:
646 dup2(fd, STDIN_FILENO);
647 dup2(fd, STDOUT_FILENO);
648 dup2(fd, STDERR_FILENO);
649 con->fd = STDIN_FILENO;
650
651 for (fd = STDERR_FILENO+1; fd < 32; fd++) {
652 if (openfd & (1<<fd)) {
653 close(fd);
654 openfd &= ~(1<<fd);
655 }
656 }
657 }
658
659 /*
660 * Ask for the password. Note that there is no default timeout as we normally
661 * skip this during boot.
662 */
663 static char *getpasswd(struct console *con)
664 {
665 struct sigaction sa;
666 struct termios tty;
667 static char pass[SULOGIN_PASSWORD_BUFSIZ], *ptr;
668 struct chardata *cp;
669 char *ret = NULL;
670 unsigned char tc;
671 char c, ascval;
672 int eightbit;
673 const int fd = con->fd;
674
675 if (con->flags & CON_EIO)
676 goto out;
677
678 cp = &con->cp;
679 tty = con->tio;
680 tc = 0;
681 ret = pass;
682
683 tty.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
684 tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP|ISIG);
685
686 if ((con->flags & CON_NOTTY) == 0)
687 tc = (tcsetattr(fd, TCSAFLUSH, &tty) == 0);
688
689 sigemptyset(&sa.sa_mask);
690 sa.sa_handler = alrm_handler;
691 sa.sa_flags = 0;
692 sigaction(SIGALRM, &sa, NULL);
693
694 if (timeout)
695 alarm(timeout);
696
697 ptr = &pass[0];
698 cp->eol = *ptr = '\0';
699
700 eightbit = ((con->flags & CON_SERIAL) == 0 || (tty.c_cflag & (PARODD|PARENB)) == 0);
701
702 while (cp->eol == '\0') {
703 errno = 0;
704
705 if (read(fd, &c, 1) < 1) {
706 if (errno == EINTR || errno == EAGAIN) {
707 if (alarm_rised) {
708 ret = NULL;
709 goto quit;
710 }
711 xusleep(250000);
712 continue;
713 }
714 ret = NULL;
715
716 switch (errno) {
717 case EIO:
718 con->flags |= CON_EIO;
719 /* fallthrough */
720 default:
721 warn(_("cannot read %s"), con->tty);
722 break;
723 case 0:
724 break;
725 }
726 goto quit;
727 }
728
729 if (eightbit)
730 ascval = c;
731 else if (c != (ascval = (c & 0177))) {
732 uint32_t bits, mask;
733 for (bits = 1, mask = 1; mask & 0177; mask <<= 1) {
734 if (mask & ascval)
735 bits++;
736 }
737 cp->parity |= ((bits & 1) ? 1 : 2);
738 }
739
740 switch (ascval) {
741 case 0:
742 *ptr = '\0';
743 goto quit;
744 case CR:
745 case NL:
746 *ptr = '\0';
747 cp->eol = ascval;
748 break;
749 case BS:
750 case CERASE:
751 cp->erase = ascval;
752 if (ptr > &pass[0])
753 ptr--;
754 break;
755 case CKILL:
756 cp->kill = ascval;
757 while (ptr > &pass[0])
758 ptr--;
759 break;
760 case CEOF:
761 ret = NULL;
762 goto quit;
763 default:
764 if ((size_t)(ptr - &pass[0]) >= (sizeof(pass) -1 )) {
765 fprintf(stderr, "sulogin: input overrun at %s\n\r", con->tty);
766 ret = NULL;
767 goto quit;
768 }
769 *ptr++ = ascval;
770 break;
771 }
772 }
773 quit:
774 alarm(0);
775 if (tc)
776 tcsetattr(fd, TCSAFLUSH, &con->tio);
777 tcfinal(con);
778 printf("\r\n");
779 out:
780 #ifdef HAVE_EXPLICIT_BZERO
781 if (ret == NULL)
782 explicit_bzero(pass, sizeof(pass));
783 #endif
784 return ret;
785 }
786
787 /*
788 * Password was OK, execute a shell.
789 */
790 static void sushell(struct passwd *pwd)
791 {
792 char shell[PATH_MAX];
793 char home[PATH_MAX];
794 char const *p;
795 char const *su_shell;
796
797 /*
798 * Set directory and shell.
799 */
800 if (chdir(pwd->pw_dir) != 0) {
801 warn(_("%s: change directory failed"), pwd->pw_dir);
802 printf(_("Logging in with home = \"/\".\n"));
803
804 if (chdir("/") != 0)
805 warn(_("change directory to system root failed"));
806 }
807
808 if ((p = getenv("SUSHELL")) != NULL)
809 su_shell = p;
810 else if ((p = getenv("sushell")) != NULL)
811 su_shell = p;
812 else {
813 if (pwd->pw_shell[0])
814 su_shell = pwd->pw_shell;
815 else
816 su_shell = "/bin/sh";
817 }
818 if ((p = strrchr(su_shell, '/')) == NULL)
819 p = su_shell;
820 else
821 p++;
822
823 snprintf(shell, sizeof(shell), profile ? "-%s" : "%s", p);
824
825 /*
826 * Set some important environment variables.
827 */
828 if (getcwd(home, sizeof(home)) == NULL)
829 strcpy(home, "/");
830
831 xsetenv("HOME", home, 1);
832 xsetenv("LOGNAME", "root", 1);
833 xsetenv("USER", "root", 1);
834 if (!profile)
835 xsetenv("SHLVL","0",1);
836
837 /*
838 * Try to execute a shell.
839 */
840 xsetenv("SHELL", su_shell, 1);
841 unmask_signal(SIGINT, &saved_sigint);
842 unmask_signal(SIGTSTP, &saved_sigtstp);
843 unmask_signal(SIGQUIT, &saved_sigquit);
844 mask_signal(SIGHUP, SIG_DFL, NULL);
845
846 #ifdef HAVE_LIBSELINUX
847 if (is_selinux_enabled() > 0) {
848 char *scon = NULL;
849 char *seuser = NULL;
850 char *level = NULL;
851
852 if (getseuserbyname("root", &seuser, &level) == 0) {
853 if (get_default_context_with_level(seuser, level, 0, &scon) == 0) {
854 if (setexeccon(scon) != 0)
855 warnx(_("setexeccon failed"));
856 freecon(scon);
857 }
858 }
859 free(seuser);
860 free(level);
861 }
862 #endif
863 execl(su_shell, shell, (char *)NULL);
864 warn(_("failed to execute %s"), su_shell);
865
866 xsetenv("SHELL", "/bin/sh", 1);
867 execl("/bin/sh", profile ? "-sh" : "sh", (char *)NULL);
868 warn(_("failed to execute %s"), "/bin/sh");
869 }
870
871 static void usage(void)
872 {
873 FILE *out = stdout;
874 fputs(USAGE_HEADER, out);
875 fprintf(out, _(
876 " %s [options] [tty device]\n"), program_invocation_short_name);
877
878 fputs(USAGE_SEPARATOR, out);
879 fputs(_("Single-user login.\n"), out);
880
881 fputs(USAGE_OPTIONS, out);
882 fputs(_(" -p, --login-shell start a login shell\n"
883 " -t, --timeout <seconds> max time to wait for a password (default: no limit)\n"
884 " -e, --force examine password files directly if getpwnam(3) fails\n"),
885 out);
886
887 fputs(USAGE_SEPARATOR, out);
888 printf(USAGE_HELP_OPTIONS(26));
889 printf(USAGE_MAN_TAIL("sulogin(8)"));
890
891 exit(EXIT_SUCCESS);
892 }
893
894 int main(int argc, char **argv)
895 {
896 struct list_head *ptr, consoles;
897 struct console *con;
898 char *tty = NULL;
899 struct passwd *pwd;
900 const struct timespec sigwait = { .tv_sec = 0, .tv_nsec = 50000000 };
901 siginfo_t status = { 0 };
902 sigset_t set;
903 int c, reconnect = 0;
904 int opt_e = 0;
905 int wait = 0;
906 pid_t pid;
907
908 static const struct option longopts[] = {
909 { "login-shell", no_argument, NULL, 'p' },
910 { "timeout", required_argument, NULL, 't' },
911 { "force", no_argument, NULL, 'e' },
912 { "help", no_argument, NULL, 'h' },
913 { "version", no_argument, NULL, 'V' },
914 { NULL, 0, NULL, 0 }
915 };
916
917 INIT_LIST_HEAD(&consoles);
918
919 /*
920 * If we are init we need to set up an own session.
921 */
922 if ((pid = getpid()) == 1) {
923 setsid();
924 ignore_result( ioctl(STDIN_FILENO, TIOCSCTTY, (char *) 1) );
925 }
926
927 setlocale(LC_ALL, "");
928 bindtextdomain(PACKAGE, LOCALEDIR);
929 textdomain(PACKAGE);
930 close_stdout_atexit();
931
932 /*
933 * See if we have a timeout flag.
934 */
935 while ((c = getopt_long(argc, argv, "ehpt:V", longopts, NULL)) != -1) {
936 switch(c) {
937 case 't':
938 timeout = strtou32_or_err(optarg, _("invalid timeout argument"));
939 break;
940 case 'p':
941 profile = 1;
942 break;
943 case 'e':
944 opt_e = 1;
945 break;
946 case 'V':
947 {
948 static const char *features[] = {
949 #ifdef USE_SULOGIN_EMERGENCY_MOUNT
950 "emergency-mount",
951 #endif
952 NULL
953 };
954 print_version_with_features(EXIT_SUCCESS, features);
955 }
956 case 'h':
957 usage();
958 default:
959 /* Do not exit! getopt prints a warning. */
960 break;
961 }
962 }
963
964 if (geteuid() != 0)
965 errx(EXIT_FAILURE, _("only superuser can run this program"));
966
967 mask_signal(SIGQUIT, SIG_IGN, &saved_sigquit);
968 mask_signal(SIGTSTP, SIG_IGN, &saved_sigtstp);
969 mask_signal(SIGINT, SIG_IGN, &saved_sigint);
970 mask_signal(SIGHUP, SIG_IGN, &saved_sighup);
971
972
973 emergency_do_mounts();
974 atexit( emergency_do_umounts );
975
976 /*
977 * See if we need to open an other tty device.
978 */
979 if (optind < argc)
980 tty = argv[optind];
981
982 if (!tty || *tty == '\0')
983 tty = getenv("CONSOLE");
984
985 /*
986 * Detect possible consoles, use stdin as fallback.
987 * If an optional tty is given, reconnect it to stdin.
988 */
989 reconnect = detect_consoles(tty, STDIN_FILENO, &consoles);
990
991 /*
992 * If previous stdin was not the specified tty and therefore reconnected
993 * to the specified tty also reconnect stdout and stderr.
994 */
995 if (reconnect) {
996 if (isatty(STDOUT_FILENO) == 0)
997 dup2(STDOUT_FILENO, STDIN_FILENO);
998 if (isatty(STDERR_FILENO) == 0)
999 dup2(STDOUT_FILENO, STDERR_FILENO);
1000 }
1001
1002 /*
1003 * Should not happen
1004 */
1005 if (list_empty(&consoles)) {
1006 if (!errno)
1007 errno = ENOENT;
1008 err(EXIT_FAILURE, _("cannot open console"));
1009 }
1010
1011 /*
1012 * Get the root password.
1013 */
1014 if ((pwd = getrootpwent(opt_e)) == NULL) {
1015 warnx(_("cannot open password database"));
1016 sleep(2);
1017 return EXIT_FAILURE;
1018 }
1019
1020 /*
1021 * Ask for the password on the consoles.
1022 */
1023 list_for_each(ptr, &consoles) {
1024 con = list_entry(ptr, struct console, entry);
1025 if (con->id >= CONMAX)
1026 break;
1027 if (con->fd >= 0) {
1028 openfd |= (1 << con->fd);
1029 tcinit(con);
1030 continue;
1031 }
1032 if ((con->fd = open(con->tty, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0)
1033 continue;
1034 openfd |= (1 << con->fd);
1035 tcinit(con);
1036 }
1037 ptr = (&consoles)->next;
1038
1039 if (ptr->next == &consoles) {
1040 con = list_entry(ptr, struct console, entry);
1041 goto nofork;
1042 }
1043
1044
1045 mask_signal(SIGCHLD, chld_handler, &saved_sigchld);
1046 do {
1047 con = list_entry(ptr, struct console, entry);
1048 if (con->id >= CONMAX)
1049 break;
1050 if (con->flags & CON_EIO)
1051 goto next;
1052
1053 switch ((con->pid = fork())) {
1054 case 0:
1055 mask_signal(SIGCHLD, SIG_DFL, NULL);
1056 dup2(con->fd, STDERR_FILENO);
1057 nofork:
1058 setup(con);
1059 while (1) {
1060 const char *passwd = pwd->pw_passwd;
1061 char *answer;
1062 int doshell = 0;
1063 int deny = !opt_e && locked_account_password(pwd->pw_passwd);
1064
1065 doprompt(passwd, con, deny);
1066
1067 if ((answer = getpasswd(con)) == NULL)
1068 break;
1069 if (deny) {
1070 #ifdef HAVE_EXPLICIT_BZERO
1071 explicit_bzero(answer, SULOGIN_PASSWORD_BUFSIZ);
1072 #endif
1073 exit(EXIT_FAILURE);
1074 }
1075
1076 /* no password or locked account */
1077 if (!passwd[0] || locked_account_password(passwd))
1078 doshell++;
1079 else {
1080 const char *cryptbuf;
1081 cryptbuf = crypt(answer, passwd);
1082 if (cryptbuf == NULL)
1083 warn(_("crypt failed"));
1084 else if (strcmp(cryptbuf, pwd->pw_passwd) == 0)
1085 doshell++;
1086 }
1087 #ifdef HAVE_EXPLICIT_BZERO
1088 explicit_bzero(answer, SULOGIN_PASSWORD_BUFSIZ);
1089 #endif
1090 if (doshell) {
1091 /* sushell() unmask signals */
1092 sushell(pwd);
1093
1094 mask_signal(SIGQUIT, SIG_IGN, &saved_sigquit);
1095 mask_signal(SIGTSTP, SIG_IGN, &saved_sigtstp);
1096 mask_signal(SIGINT, SIG_IGN, &saved_sigint);
1097
1098 fprintf(stderr, _("cannot execute su shell\n\n"));
1099 break;
1100 }
1101 fprintf(stderr, _("Login incorrect\n\n"));
1102 }
1103 if (alarm_rised) {
1104 tcfinal(con);
1105 warnx(_("Timed out\n\n"));
1106 }
1107 /*
1108 * User pressed Control-D.
1109 */
1110 exit(0);
1111 case -1:
1112 warn(_("fork failed"));
1113 /* fallthrough */
1114 default:
1115 break;
1116 }
1117 next:
1118 ptr = ptr->next;
1119
1120 } while (ptr != &consoles);
1121
1122 do {
1123 int ret;
1124
1125 status.si_pid = 0;
1126 ret = waitid(P_ALL, 0, &status, WEXITED);
1127
1128 if (ret == 0)
1129 break;
1130 if (ret < 0) {
1131 if (errno == ECHILD)
1132 break;
1133 if (errno == EINTR)
1134 continue;
1135 }
1136
1137 errx(EXIT_FAILURE, _("cannot wait on su shell\n\n"));
1138
1139 } while (1);
1140
1141 list_for_each(ptr, &consoles) {
1142 con = list_entry(ptr, struct console, entry);
1143
1144 if (con->fd < 0)
1145 continue;
1146 if (con->pid < 0)
1147 continue;
1148 if (con->pid == status.si_pid)
1149 con->pid = -1;
1150 else {
1151 kill(con->pid, SIGTERM);
1152 wait++;
1153 }
1154 }
1155
1156 sigemptyset(&set);
1157 sigaddset(&set, SIGCHLD);
1158
1159 do {
1160 int signum, ret;
1161
1162 if (!wait)
1163 break;
1164
1165 status.si_pid = 0;
1166 ret = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
1167
1168 if (ret < 0) {
1169 if (errno == ECHILD)
1170 break;
1171 if (errno == EINTR)
1172 continue;
1173 }
1174
1175 if (!ret && status.si_pid > 0) {
1176 list_for_each(ptr, &consoles) {
1177 con = list_entry(ptr, struct console, entry);
1178
1179 if (con->fd < 0)
1180 continue;
1181 if (con->pid < 0)
1182 continue;
1183 if (con->pid == status.si_pid) {
1184 con->pid = -1;
1185 wait--;
1186 }
1187 }
1188 continue;
1189 }
1190
1191 signum = sigtimedwait(&set, NULL, &sigwait);
1192 if (signum != SIGCHLD && signum < 0 && errno == EAGAIN)
1193 break;
1194
1195 } while (1);
1196
1197 mask_signal(SIGCHLD, SIG_DFL, NULL);
1198 return EXIT_SUCCESS;
1199 }