]> git.ipfire.org Git - thirdparty/util-linux.git/blame - login-utils/sulogin.c
scriptreplay: cleanup usage()
[thirdparty/util-linux.git] / login-utils / sulogin.c
CommitLineData
7d8a8d2c 1/*
adfbaecf 2 * sulogin
7d8a8d2c 3 *
adfbaecf
KZ
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.
7d8a8d2c
DR
8 *
9 * Copyright (C) 1998-2003 Miquel van Smoorenburg.
7c6bc22b 10 * Copyright (C) 2012 Karel Zak <kzak@redhat.com>
729bfb80 11 * Copyright (C) 2012 Werner Fink <werner@suse.de>
7d8a8d2c
DR
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
7d8a8d2c 26 */
dfbc7c89 27#include <sys/mman.h>
7d8a8d2c
DR
28#include <sys/types.h>
29#include <sys/stat.h>
dfbc7c89 30#include <sys/wait.h>
7d8a8d2c
DR
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>
7d8a8d2c 40#include <errno.h>
8614335f 41#include <getopt.h>
7d8a8d2c 42#include <sys/ioctl.h>
5fe090d8 43#ifdef HAVE_CRYPT_H
adfbaecf 44# include <crypt.h>
7d8a8d2c
DR
45#endif
46
47#ifdef HAVE_LIBSELINUX
adfbaecf
KZ
48# include <selinux/selinux.h>
49# include <selinux/get_context_list.h>
7d8a8d2c
DR
50#endif
51
b9c73909
WF
52#ifdef __linux__
53# include <sys/kd.h>
54# include <sys/param.h>
55#endif
56
9211be30 57#include "c.h"
439cdf1e 58#include "closestream.h"
984a6096 59#include "env.h"
9211be30 60#include "nls.h"
97e04909 61#include "pathnames.h"
1eb16fd7
KZ
62#ifdef USE_PLYMOUTH_SUPPORT
63# include "plymouth-ctrl.h"
64#endif
1d28b828 65#include "strutils.h"
59ef660f 66#include "ttyutils.h"
a73f59fa 67#include "sulogin-consoles.h"
dfbc7c89
WF
68#define CONMAX 16
69
1d28b828 70static unsigned int timeout;
7d8a8d2c 71static int profile;
dfbc7c89 72static volatile uint32_t openfd; /* Remember higher file descriptors */
7d8a8d2c 73
2ba641e5
SK
74static struct sigaction saved_sigint;
75static struct sigaction saved_sigtstp;
76static struct sigaction saved_sigquit;
77static struct sigaction saved_sighup;
78static struct sigaction saved_sigchld;
7d8a8d2c 79
729bfb80 80static volatile sig_atomic_t alarm_rised;
dfbc7c89
WF
81static volatile sig_atomic_t sigchild;
82
83#ifndef IUCLC
84# define IUCLC 0
85#endif
86
36f12008
RM
87#ifndef WEXITED
88# warning "WEXITED is missing, sulogin may not work as expected"
89# define WEXITED 0
90#endif
91
71f207a5 92static int locked_account_password(const char * const passwd)
7ff1162e
KZ
93{
94 if (passwd && (*passwd == '*' || *passwd == '!'))
95 return 1;
96 return 0;
97}
98
dfbc7c89
WF
99/*
100 * Fix the tty modes and set reasonable defaults.
101 */
102static void tcinit(struct console *con)
103{
71f207a5 104 int flags = 0, mode = 0;
dfbc7c89 105 struct termios *tio = &con->tio;
626aa651 106 const int fd = con->fd;
1eb16fd7
KZ
107#ifdef USE_PLYMOUTH_SUPPORT
108 struct termios lock;
fe3f7e17
WF
109 int i = (plymouth_command(MAGIC_PING)) ? PLYMOUTH_TERMIOS_FLAGS_DELAY : 0;
110 if (i)
111 plymouth_command(MAGIC_QUIT);
bb280f79
WF
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;
bb280f79
WF
122 sleep(1);
123 }
124 memset(&lock, 0, sizeof(struct termios));
125 ioctl(fd, TIOCSLCKTRMIOS, &lock);
88e0f3df 126#endif
dfbc7c89
WF
127 errno = 0;
128
129 if (tcgetattr(fd, tio) < 0) {
130 warn(_("tcgetattr failed"));
131 con->flags |= CON_NOTTY;
132 return;
133 }
134
b9c73909
WF
135 /* Handle lines other than virtual consoles here */
136#if defined(KDGKBMODE)
137 if (ioctl(fd, KDGKBMODE, &mode) < 0)
138#endif
139 {
dfbc7c89
WF
140 speed_t ispeed, ospeed;
141 struct winsize ws;
b9c73909 142 errno = 0;
dfbc7c89
WF
143
144 /* this is a modem line */
145 con->flags |= CON_SERIAL;
146
147 /* Flush input and output queues on modem lines */
cba09b5f 148 tcflush(fd, TCIOFLUSH);
dfbc7c89
WF
149
150 ispeed = cfgetispeed(tio);
151 ospeed = cfgetospeed(tio);
152
153 if (!ispeed) ispeed = TTYDEF_SPEED;
154 if (!ospeed) ospeed = TTYDEF_SPEED;
155
dfbc7c89 156 tio->c_cflag = CREAD | CS8 | HUPCL | (tio->c_cflag & CLOCAL);
84ece698
KZ
157 tio->c_iflag = 0;
158 tio->c_lflag = 0;
159 tio->c_oflag &= OPOST | ONLCR;
dfbc7c89
WF
160
161 cfsetispeed(tio, ispeed);
162 cfsetospeed(tio, ospeed);
163
e4544ec1 164#ifdef HAVE_STRUCT_TERMIOS_C_LINE
dfbc7c89 165 tio->c_line = 0;
e4544ec1 166#endif
dfbc7c89
WF
167 tio->c_cc[VTIME] = 0;
168 tio->c_cc[VMIN] = 1;
169
170 if (ioctl(fd, TIOCGWINSZ, &ws) == 0) {
ff09a512
SK
171 int update = 0;
172
dfbc7c89
WF
173 if (ws.ws_row == 0) {
174 ws.ws_row = 24;
ff09a512 175 update++;
dfbc7c89
WF
176 }
177 if (ws.ws_col == 0) {
178 ws.ws_col = 80;
ff09a512 179 update++;
dfbc7c89 180 }
ff09a512 181 if (update)
cba09b5f 182 ignore_result( ioctl(fd, TIOCSWINSZ, &ws) );
dfbc7c89
WF
183 }
184
185 setlocale(LC_CTYPE, "POSIX");
186 goto setattr;
187 }
188#if defined(IUTF8) && defined(KDGKBMODE)
b9c73909 189 /* Handle mode of current keyboard setup, e.g. for UTF-8 */
dfbc7c89
WF
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);
206setattr:
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)
cba09b5f 212 ignore_result( fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) );
dfbc7c89
WF
213}
214
215/*
216 * Finalize the tty modes on modem lines.
217 */
218static void tcfinal(struct console *con)
219{
71f207a5
SK
220 struct termios *tio = &con->tio;
221 const int fd = con->fd;
dfbc7c89
WF
222
223 if ((con->flags & CON_SERIAL) == 0) {
984a6096 224 xsetenv("TERM", "linux", 1);
dfbc7c89
WF
225 return;
226 }
f2bcda51 227 if (con->flags & CON_NOTTY) {
984a6096 228 xsetenv("TERM", "dumb", 1);
dfbc7c89 229 return;
f2bcda51 230 }
dfbc7c89 231
f2bcda51 232#if defined (__s390__) || defined (__s390x__)
984a6096 233 xsetenv("TERM", "dumb", 1);
f2bcda51 234#else
984a6096 235 xsetenv("TERM", "vt102", 1);
f2bcda51 236#endif
dfbc7c89
WF
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;
e4544ec1 248#elif defined(VSWTCH)
dfbc7c89
WF
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;
b1557fe9 271 /* fallthrough */
dfbc7c89
WF
272 case 2: /* even parity */
273 tio->c_cflag |= PARENB;
274 tio->c_iflag |= (INPCK | ISTRIP);
b1557fe9 275 /* fallthrough */
dfbc7c89
WF
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 */
cba09b5f 283 tcsetattr(fd, TCSANOW, tio);
dfbc7c89 284}
729bfb80 285
7d8a8d2c 286/*
adfbaecf 287 * Called at timeout.
7d8a8d2c 288 */
97795a73 289static void alrm_handler(int sig __attribute__((unused)))
7d8a8d2c 290{
729bfb80
WF
291 /* Timeout expired */
292 alarm_rised++;
97795a73
DR
293}
294
dfbc7c89
WF
295static void chld_handler(int sig __attribute__((unused)))
296{
297 sigchild++;
298}
299
97795a73
DR
300static 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
dfbc7c89 309 sigaction(signal, &newaction, origaction);
97795a73
DR
310}
311
312static void unmask_signal(int signal, struct sigaction *sa)
313{
314 sigaction(signal, sa, NULL);
7d8a8d2c
DR
315}
316
317/*
adfbaecf
KZ
318 * See if an encrypted password is valid. The encrypted password is checked for
319 * traditional-style DES and FreeBSD-style MD5 encryption.
7d8a8d2c 320 */
60706626 321static int valid(const char *pass)
7d8a8d2c
DR
322{
323 const char *s;
324 char id[5];
325 size_t len;
326 off_t off;
327
60706626
DR
328 if (pass[0] == 0)
329 return 1;
60706626
DR
330 if (pass[0] != '$')
331 goto check_des;
7d8a8d2c
DR
332
333 /*
adfbaecf 334 * up to 4 bytes for the signature e.g. $1$
7d8a8d2c 335 */
60706626
DR
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;
7d8a8d2c
DR
343
344 memset(id, '\0', sizeof(id));
345 strncpy(id, pass, off);
346
347 /*
adfbaecf 348 * up to 16 bytes for the salt
7d8a8d2c 349 */
60706626
DR
350 for (; *s && *s != '$'; s++);
351
352 if (*s++ != '$')
353 return 0;
354
355 if ((off_t)(s-pass) > 16)
356 return 0;
357
7d8a8d2c
DR
358 len = strlen(s);
359
360 /*
adfbaecf 361 * the MD5 hash (128 bits or 16 bytes) encoded in base64 = 22 bytes
7d8a8d2c 362 */
60706626
DR
363 if ((strcmp(id, "$1$") == 0) && (len < 22 || len > 24))
364 return 0;
7d8a8d2c
DR
365
366 /*
adfbaecf 367 * the SHA-256 hash 43 bytes
7d8a8d2c 368 */
60706626
DR
369 if ((strcmp(id, "$5$") == 0) && (len < 42 || len > 44))
370 return 0;
7d8a8d2c
DR
371
372 /*
adfbaecf 373 * the SHA-512 hash 86 bytes
7d8a8d2c 374 */
60706626
DR
375 if ((strcmp(id, "$6$") == 0) && (len < 85 || len > 87))
376 return 0;
7d8a8d2c
DR
377
378 /*
adfbaecf 379 * e.g. Blowfish hash
7d8a8d2c
DR
380 */
381 return 1;
382check_des:
60706626
DR
383 if (strlen(pass) != 13)
384 return 0;
385
7d8a8d2c
DR
386 for (s = pass; *s; s++) {
387 if ((*s < '0' || *s > '9') &&
388 (*s < 'a' || *s > 'z') &&
389 (*s < 'A' || *s > 'Z') &&
60706626
DR
390 *s != '.' && *s != '/')
391 return 0;
7d8a8d2c 392 }
7d8a8d2c
DR
393 return 1;
394}
395
396/*
adfbaecf 397 * Set a variable if the value is not NULL.
7d8a8d2c 398 */
24ff2f57 399static inline void set(char **var, char *val)
7d8a8d2c 400{
60706626
DR
401 if (val)
402 *var = val;
7d8a8d2c
DR
403}
404
405/*
adfbaecf 406 * Get the root password entry.
7d8a8d2c 407 */
60706626 408static struct passwd *getrootpwent(int try_manually)
7d8a8d2c
DR
409{
410 static struct passwd pwd;
411 struct passwd *pw;
412 struct spwd *spw;
413 FILE *fp;
d681e095
KZ
414 static char line[2 * BUFSIZ];
415 static char sline[2 * BUFSIZ];
7d8a8d2c
DR
416 char *p;
417
418 /*
adfbaecf
KZ
419 * First, we try to get the password the standard way using normal
420 * library calls.
7d8a8d2c
DR
421 */
422 if ((pw = getpwnam("root")) &&
423 !strcmp(pw->pw_passwd, "x") &&
424 (spw = getspnam("root")))
425 pw->pw_passwd = spw->sp_pwdp;
adfbaecf 426
60706626
DR
427 if (pw || !try_manually)
428 return pw;
7d8a8d2c
DR
429
430 /*
adfbaecf
KZ
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.
7d8a8d2c
DR
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
97e04909 443 if ((fp = fopen(_PATH_PASSWD, "r")) == NULL) {
289dcc90 444 warn(_("cannot open %s"), _PATH_PASSWD);
7d8a8d2c
DR
445 return &pwd;
446 }
447
448 /*
adfbaecf 449 * Find root in the password file.
7d8a8d2c 450 */
d681e095 451 while ((p = fgets(line, sizeof(line), fp)) != NULL) {
7d8a8d2c
DR
452 if (strncmp(line, "root:", 5) != 0)
453 continue;
454 p += 5;
455 set(&pwd.pw_passwd, strsep(&p, ":"));
60706626
DR
456 strsep(&p, ":");
457 strsep(&p, ":");
7d8a8d2c
DR
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 /*
adfbaecf 467 * If the encrypted password is valid or not found, return.
7d8a8d2c
DR
468 */
469 if (p == NULL) {
b6506dc9 470 warnx(_("%s: no entry for root\n"), _PATH_PASSWD);
7d8a8d2c
DR
471 return &pwd;
472 }
60706626
DR
473 if (valid(pwd.pw_passwd))
474 return &pwd;
7d8a8d2c
DR
475
476 /*
adfbaecf 477 * The password is invalid. If there is a shadow password, try it.
7d8a8d2c 478 */
d681e095 479 *pwd.pw_passwd = '\0';
97e04909 480 if ((fp = fopen(_PATH_SHADOW_PASSWD, "r")) == NULL) {
289dcc90 481 warn(_("cannot open %s"), _PATH_PASSWD);
7d8a8d2c
DR
482 return &pwd;
483 }
d681e095 484 while ((p = fgets(sline, sizeof(sline), fp)) != NULL) {
7d8a8d2c
DR
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 /*
adfbaecf 494 * If the password is still invalid, NULL it, and return.
7d8a8d2c
DR
495 */
496 if (p == NULL) {
d45e5dff 497 warnx(_("%s: no entry for root"), _PATH_SHADOW_PASSWD);
d681e095 498 *pwd.pw_passwd = '\0';
7d8a8d2c 499 }
9e930041 500 /* locked account passwords are valid too */
7ff1162e 501 if (!locked_account_password(pwd.pw_passwd) && !valid(pwd.pw_passwd)) {
d45e5dff 502 warnx(_("%s: root password garbled"), _PATH_SHADOW_PASSWD);
d681e095 503 *pwd.pw_passwd = '\0';
60706626 504 }
7d8a8d2c
DR
505 return &pwd;
506}
507
dfbc7c89
WF
508/*
509 * Ask by prompt for the password.
510 */
7ff1162e 511static void doprompt(const char *crypted, struct console *con, int deny)
dfbc7c89
WF
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);
cba09b5f 522 tcsetattr(con->fd, TCSADRAIN, &tty);
dfbc7c89 523 }
f9915d6d
KZ
524 if (!con->file) {
525 con->file = fdopen(con->fd, "r+");
526 if (!con->file)
dfbc7c89
WF
527 goto err;
528 }
7ff1162e
KZ
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 {
dfbc7c89 535#if defined(USE_ONELINE)
7ff1162e
KZ
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: "));
dfbc7c89 540#else
7ff1162e
KZ
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): "));
dfbc7c89 546#endif
7ff1162e 547 }
dfbc7c89
WF
548 fflush(con->file);
549err:
550 if (con->flags & CON_SERIAL)
cba09b5f 551 tcsetattr(con->fd, TCSADRAIN, &con->tio);
dfbc7c89
WF
552}
553
554/*
555 * Make sure to have an own session and controlling terminal
556 */
557static void setup(struct console *con)
558{
71f207a5
SK
559 int fd = con->fd;
560 const pid_t pid = getpid(), pgrp = getpgid(0), ppgrp =
561 getpgid(getppid()), ttypgrp = tcgetpgrp(fd);
dfbc7c89
WF
562
563 if (con->flags & CON_NOTTY)
564 return;
dfbc7c89
WF
565
566 /*
567 * Only go through this trouble if the new
568 * tty doesn't fall in this process group.
569 */
dfbc7c89
WF
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
7d8a8d2c 601/*
adfbaecf
KZ
602 * Ask for the password. Note that there is no default timeout as we normally
603 * skip this during boot.
7d8a8d2c 604 */
71f207a5 605static const char *getpasswd(struct console *con)
7d8a8d2c
DR
606{
607 struct sigaction sa;
dfbc7c89
WF
608 struct termios tty;
609 static char pass[128], *ptr;
610 struct chardata *cp;
71f207a5 611 const char *ret = pass;
dfbc7c89
WF
612 unsigned char tc;
613 char c, ascval;
614 int eightbit;
71f207a5 615 const int fd = con->fd;
5ec4a799 616
dfbc7c89
WF
617 if (con->flags & CON_NOTTY)
618 goto out;
dfbc7c89
WF
619 cp = &con->cp;
620 tty = con->tio;
7d8a8d2c 621
7d8a8d2c 622 tty.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
dfbc7c89
WF
623 tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP|ISIG);
624 tc = (tcsetattr(fd, TCSAFLUSH, &tty) == 0);
7d8a8d2c 625
fa936c9f 626 sigemptyset(&sa.sa_mask);
7d8a8d2c
DR
627 sa.sa_handler = alrm_handler;
628 sa.sa_flags = 0;
629 sigaction(SIGALRM, &sa, NULL);
cba09b5f
KZ
630
631 if (timeout)
632 alarm(timeout);
7d8a8d2c 633
dfbc7c89
WF
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) {
1c4b2d43
AH
641 if (alarm_rised) {
642 ret = NULL;
643 goto quit;
644 }
a5bd7939 645 xusleep(250000);
dfbc7c89
WF
646 continue;
647 }
f9915d6d 648 ret = NULL;
dfbc7c89
WF
649 switch (errno) {
650 case 0:
651 case EIO:
652 case ESRCH:
653 case EINVAL:
654 case ENOENT:
7d8a8d2c 655 break;
dfbc7c89 656 default:
47481cbd 657 warn(_("cannot read %s"), con->tty);
dfbc7c89
WF
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:
60dea9d1 695 ret = NULL;
dfbc7c89
WF
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);
f9915d6d 700 ret = NULL;
dfbc7c89 701 goto quit;
7d8a8d2c 702 }
dfbc7c89
WF
703 *ptr++ = ascval;
704 break;
705 }
7d8a8d2c 706 }
dfbc7c89 707quit:
7d8a8d2c 708 alarm(0);
dfbc7c89 709 if (tc)
cba09b5f 710 tcsetattr(fd, TCSAFLUSH, &con->tio);
6988998b 711 tcfinal(con);
dfbc7c89
WF
712 printf("\r\n");
713out:
7d8a8d2c
DR
714 return ret;
715}
716
717/*
adfbaecf 718 * Password was OK, execute a shell.
7d8a8d2c 719 */
60706626 720static void sushell(struct passwd *pwd)
7d8a8d2c 721{
adf97f07
KZ
722 char shell[PATH_MAX];
723 char home[PATH_MAX];
71f207a5
SK
724 char const *p;
725 char const *su_shell;
7d8a8d2c
DR
726
727 /*
adfbaecf 728 * Set directory and shell.
7d8a8d2c 729 */
adf97f07
KZ
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
7d8a8d2c 738 if ((p = getenv("SUSHELL")) != NULL)
9f94bdce 739 su_shell = p;
7d8a8d2c 740 else if ((p = getenv("sushell")) != NULL)
9f94bdce 741 su_shell = p;
7d8a8d2c
DR
742 else {
743 if (pwd->pw_shell[0])
9f94bdce 744 su_shell = pwd->pw_shell;
7d8a8d2c 745 else
9f94bdce 746 su_shell = "/bin/sh";
7d8a8d2c 747 }
9f94bdce
SK
748 if ((p = strrchr(su_shell, '/')) == NULL)
749 p = su_shell;
7d8a8d2c
DR
750 else
751 p++;
adfbaecf 752
7d8a8d2c
DR
753 snprintf(shell, sizeof(shell), profile ? "-%s" : "%s", p);
754
755 /*
adfbaecf 756 * Set some important environment variables.
7d8a8d2c 757 */
dfbc7c89
WF
758 if (getcwd(home, sizeof(home)) == NULL)
759 strcpy(home, "/");
adf97f07 760
984a6096
SK
761 xsetenv("HOME", home, 1);
762 xsetenv("LOGNAME", "root", 1);
763 xsetenv("USER", "root", 1);
7d8a8d2c 764 if (!profile)
984a6096 765 xsetenv("SHLVL","0",1);
7d8a8d2c
DR
766
767 /*
adfbaecf 768 * Try to execute a shell.
7d8a8d2c 769 */
984a6096 770 xsetenv("SHELL", su_shell, 1);
97795a73
DR
771 unmask_signal(SIGINT, &saved_sigint);
772 unmask_signal(SIGTSTP, &saved_sigtstp);
773 unmask_signal(SIGQUIT, &saved_sigquit);
dfbc7c89 774 mask_signal(SIGHUP, SIG_DFL, NULL);
c3e706a9
KZ
775
776#ifdef HAVE_LIBSELINUX
7d8a8d2c 777 if (is_selinux_enabled() > 0) {
60706626
DR
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)
d45e5dff 784 warnx(_("setexeccon failed"));
60706626
DR
785 freecon(scon);
786 }
787 }
7d8a8d2c
DR
788 free(seuser);
789 free(level);
790 }
791#endif
9f94bdce 792 execl(su_shell, shell, NULL);
07ff972e 793 warn(_("failed to execute %s"), su_shell);
7d8a8d2c 794
984a6096 795 xsetenv("SHELL", "/bin/sh", 1);
97e04909 796 execl("/bin/sh", profile ? "-sh" : "sh", NULL);
07ff972e 797 warn(_("failed to execute %s"), "/bin/sh");
7d8a8d2c
DR
798}
799
9325dbfd 800static void usage(void)
7d8a8d2c 801{
9325dbfd 802 FILE *out = stdout;
8614335f 803 fputs(USAGE_HEADER, out);
9211be30 804 fprintf(out, _(
8614335f
KZ
805 " %s [options] [tty device]\n"), program_invocation_short_name);
806
451dbcfa
BS
807 fputs(USAGE_SEPARATOR, out);
808 fputs(_("Single-user login.\n"), out);
809
8614335f
KZ
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);
f45f3ec3
RM
817 printf(USAGE_HELP_OPTIONS(26));
818 printf(USAGE_MAN_TAIL("sulogin(8)"));
2c308875
KZ
819
820 exit(EXIT_SUCCESS);
7d8a8d2c
DR
821}
822
823int main(int argc, char **argv)
824{
5d74cf00 825 struct list_head *ptr, consoles;
dfbc7c89 826 struct console *con;
7d8a8d2c 827 char *tty = NULL;
7d8a8d2c 828 struct passwd *pwd;
71f207a5
SK
829 const struct timespec sigwait = { .tv_sec = 0, .tv_nsec = 50000000 };
830 siginfo_t status = { 0 };
3d0cf042 831 sigset_t set;
cde7699c 832 int c, reconnect = 0;
7d8a8d2c 833 int opt_e = 0;
cde7699c 834 int wait = 0;
dfbc7c89 835 pid_t pid;
7d8a8d2c 836
8614335f 837 static const struct option longopts[] = {
87918040
SK
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 }
8614335f
KZ
844 };
845
5d74cf00
KZ
846 INIT_LIST_HEAD(&consoles);
847
dfbc7c89
WF
848 /*
849 * If we are init we need to set up a own session.
850 */
851 if ((pid = getpid()) == 1) {
852 setsid();
cba09b5f 853 ignore_result( ioctl(STDIN_FILENO, TIOCSCTTY, (char *) 1) );
dfbc7c89
WF
854 }
855
24ff2f57
KZ
856 setlocale(LC_ALL, "");
857 bindtextdomain(PACKAGE, LOCALEDIR);
858 textdomain(PACKAGE);
2c308875 859 close_stdout_atexit();
24ff2f57 860
7d8a8d2c 861 /*
adfbaecf 862 * See if we have a timeout flag.
7d8a8d2c 863 */
8614335f 864 while ((c = getopt_long(argc, argv, "ehpt:V", longopts, NULL)) != -1) {
60706626 865 switch(c) {
7d8a8d2c 866 case 't':
92bb28a4 867 timeout = strtou32_or_err(optarg, _("invalid timeout argument"));
7d8a8d2c
DR
868 break;
869 case 'p':
870 profile = 1;
871 break;
872 case 'e':
873 opt_e = 1;
874 break;
8614335f 875 case 'V':
2c308875 876 print_version(EXIT_SUCCESS);
9211be30 877 case 'h':
9325dbfd 878 usage();
7d8a8d2c 879 default:
9325dbfd 880 /* Do not exit! getopt prints a warning. */
7d8a8d2c 881 break;
60706626 882 }
7d8a8d2c
DR
883 }
884
d45e5dff 885 if (geteuid() != 0)
1d231190 886 errx(EXIT_FAILURE, _("only superuser can run this program"));
7d8a8d2c 887
97795a73
DR
888 mask_signal(SIGQUIT, SIG_IGN, &saved_sigquit);
889 mask_signal(SIGTSTP, SIG_IGN, &saved_sigtstp);
890 mask_signal(SIGINT, SIG_IGN, &saved_sigint);
dfbc7c89
WF
891 mask_signal(SIGHUP, SIG_IGN, &saved_sighup);
892
4226f910
KZ
893
894 emergency_do_mounts();
895 atexit( emergency_do_umounts );
896
dfbc7c89
WF
897 /*
898 * See if we need to open an other tty device.
899 */
60706626
DR
900 if (optind < argc)
901 tty = argv[optind];
7d8a8d2c 902
dfbc7c89
WF
903 if (!tty || *tty == '\0')
904 tty = getenv("CONSOLE");
7d8a8d2c 905
dfbc7c89
WF
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);
7d8a8d2c 911
dfbc7c89 912 /*
9e930041 913 * If previous stdin was not the specified tty and therefore reconnected
dfbc7c89
WF
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);
7d8a8d2c
DR
921 }
922
dfbc7c89
WF
923 /*
924 * Should not happen
925 */
926 if (list_empty(&consoles)) {
927 if (!errno)
928 errno = ENOENT;
4b3624ca 929 err(EXIT_FAILURE, _("cannot open console"));
dfbc7c89 930 }
59ef660f 931
7d8a8d2c 932 /*
adfbaecf 933 * Get the root password.
7d8a8d2c
DR
934 */
935 if ((pwd = getrootpwent(opt_e)) == NULL) {
1d231190 936 warnx(_("cannot open password database"));
7d8a8d2c 937 sleep(2);
ad85c7ba 938 return EXIT_FAILURE;
7d8a8d2c
DR
939 }
940
941 /*
dfbc7c89 942 * Ask for the password on the consoles.
7d8a8d2c 943 */
dfbc7c89
WF
944 list_for_each(ptr, &consoles) {
945 con = list_entry(ptr, struct console, entry);
946 if (con->id >= CONMAX)
60706626 947 break;
dfbc7c89 948 if (con->fd >= 0) {
cba09b5f 949 openfd |= (1 << con->fd);
dfbc7c89
WF
950 tcinit(con);
951 continue;
729bfb80 952 }
dfbc7c89
WF
953 if ((con->fd = open(con->tty, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0)
954 continue;
cba09b5f 955 openfd |= (1 << con->fd);
dfbc7c89 956 tcinit(con);
7d8a8d2c 957 }
dfbc7c89 958 ptr = (&consoles)->next;
7d8a8d2c 959
dfbc7c89
WF
960 if (ptr->next == &consoles) {
961 con = list_entry(ptr, struct console, entry);
962 goto nofork;
963 }
729bfb80 964
7ff1162e 965
dfbc7c89
WF
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);
dfbc7c89
WF
975 nofork:
976 setup(con);
977 while (1) {
978 const char *passwd = pwd->pw_passwd;
979 const char *answer;
a8224e8e 980 int doshell = 0;
7ff1162e
KZ
981 int deny = !opt_e && locked_account_password(pwd->pw_passwd);
982
983 doprompt(passwd, con, deny);
dfbc7c89 984
dfbc7c89
WF
985 if ((answer = getpasswd(con)) == NULL)
986 break;
7ff1162e
KZ
987 if (deny)
988 exit(EXIT_FAILURE);
dfbc7c89 989
7ff1162e
KZ
990 /* no password or locked account */
991 if (!passwd[0] || locked_account_password(passwd))
dfbc7c89
WF
992 doshell++;
993 else {
994 const char *cryptbuf;
995 cryptbuf = crypt(answer, passwd);
996 if (cryptbuf == NULL)
4b3624ca 997 warn(_("crypt failed"));
dfbc7c89
WF
998 else if (strcmp(cryptbuf, pwd->pw_passwd) == 0)
999 doshell++;
1000 }
1001
1002 if (doshell) {
a8224e8e 1003 /* sushell() unmask signals */
dfbc7c89 1004 sushell(pwd);
dfbc7c89 1005
a8224e8e
KZ
1006 mask_signal(SIGQUIT, SIG_IGN, &saved_sigquit);
1007 mask_signal(SIGTSTP, SIG_IGN, &saved_sigtstp);
1008 mask_signal(SIGINT, SIG_IGN, &saved_sigint);
dfbc7c89 1009
223939d9 1010 fprintf(stderr, _("cannot execute su shell\n\n"));
dfbc7c89
WF
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:
4b3624ca 1024 warn(_("fork failed"));
b1557fe9 1025 /* fallthrough */
dfbc7c89
WF
1026 default:
1027 break;
1028 }
1029
1030 ptr = ptr->next;
1031
1032 } while (ptr != &consoles);
1033
cde7699c
WF
1034 do {
1035 int ret;
1036
1037 status.si_pid = 0;
1038 ret = waitid(P_ALL, 0, &status, WEXITED);
1039
1040 if (ret == 0)
dfbc7c89 1041 break;
cde7699c
WF
1042 if (ret < 0) {
1043 if (errno == ECHILD)
1044 break;
1045 if (errno == EINTR)
dfbc7c89 1046 continue;
cde7699c
WF
1047 }
1048
223939d9 1049 errx(EXIT_FAILURE, _("cannot wait on su shell\n\n"));
cde7699c
WF
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)
dfbc7c89 1084 continue;
cde7699c
WF
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 }
dfbc7c89 1099 }
cde7699c 1100 continue;
dfbc7c89 1101 }
cde7699c
WF
1102
1103 signum = sigtimedwait(&set, NULL, &sigwait);
74ce680a
SK
1104 if (signum != SIGCHLD && signum < 0 && errno == EAGAIN)
1105 break;
cde7699c
WF
1106
1107 } while (1);
dfbc7c89
WF
1108
1109 mask_signal(SIGCHLD, SIG_DFL, NULL);
cd8703ad 1110 return EXIT_SUCCESS;
7d8a8d2c 1111}