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