]>
Commit | Line | Data |
---|---|---|
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 | 71 | static unsigned int timeout; |
7d8a8d2c | 72 | static int profile; |
dfbc7c89 | 73 | static volatile uint32_t openfd; /* Remember higher file descriptors */ |
7d8a8d2c | 74 | |
2ba641e5 SK |
75 | static struct sigaction saved_sigint; |
76 | static struct sigaction saved_sigtstp; | |
77 | static struct sigaction saved_sigquit; | |
78 | static struct sigaction saved_sighup; | |
79 | static struct sigaction saved_sigchld; | |
7d8a8d2c | 80 | |
729bfb80 | 81 | static volatile sig_atomic_t alarm_rised; |
dfbc7c89 WF |
82 | static 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 | 95 | static 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 | */ | |
105 | static 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); | |
257 | setattr: | |
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 | */ | |
269 | static 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 | 340 | static void alrm_handler(int sig __attribute__((unused))) |
7d8a8d2c | 341 | { |
729bfb80 | 342 | /* Timeout expired */ |
3665ff8a | 343 | alarm_rised = 1; |
97795a73 DR |
344 | } |
345 | ||
dfbc7c89 WF |
346 | static void chld_handler(int sig __attribute__((unused))) |
347 | { | |
3665ff8a | 348 | sigchild = 1; |
dfbc7c89 WF |
349 | } |
350 | ||
97795a73 DR |
351 | static 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 | ||
363 | static 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 | 372 | static 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; | |
433 | check_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 | 450 | static 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 | 459 | static 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 | 562 | static 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); |
600 | err: | |
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 | */ | |
608 | static 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 | 643 | notty: |
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 | 661 | static 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 | 771 | quit: |
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"); |
777 | out: | |
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 | 788 | static 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 | 869 | static 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 | ||
892 | int 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 | } |