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