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