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