1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64#include "libbb.h"
65#include "common_bufsiz.h"
66#include <syslog.h>
67
68#if ENABLE_SELINUX
69# include <selinux/selinux.h>
70# include <selinux/get_context_list.h>
71#
72# undef SECCLASS_CHR_FILE
73# define SECCLASS_CHR_FILE 10
74#endif
75
76#if ENABLE_PAM
77
78# undef setlocale
79
80
81# include <security/pam_appl.h>
82# include <security/pam_misc.h>
83
84# if 0
85
86
87
88
89
90
91
92
93
94
95
96static char *passwd = NULL;
97static int my_conv(int num_msg, const struct pam_message **msg,
98 struct pam_response **resp, void *data)
99{
100 int i;
101 for (i = 0; i < num_msg; i++) {
102 switch (msg[i]->msg_style) {
103 case PAM_PROMPT_ECHO_OFF:
104 if (passwd == NULL) {
105 misc_conv(num_msg, msg, resp, data);
106 passwd = xstrdup(resp[i]->resp);
107 return PAM_SUCCESS;
108 }
109
110 resp[0] = xzalloc(sizeof(struct pam_response));
111 resp[0]->resp = passwd;
112 passwd = NULL;
113 resp[0]->resp_retcode = PAM_SUCCESS;
114 resp[1] = NULL;
115 return PAM_SUCCESS;
116
117 default:
118 break;
119 }
120 }
121
122 return PAM_SUCCESS;
123}
124# endif
125
126static const struct pam_conv conv = {
127 misc_conv,
128 NULL
129};
130#endif
131
132enum {
133 TIMEOUT = 60,
134 EMPTY_USERNAME_COUNT = 10,
135
136 USERNAME_SIZE = 64,
137 TTYNAME_SIZE = 32,
138};
139
140struct globals {
141 struct termios tty_attrs;
142} FIX_ALIASING;
143#define G (*(struct globals*)bb_common_bufsiz1)
144#define INIT_G() do { setup_common_bufsiz(); } while (0)
145
146
147#if ENABLE_FEATURE_NOLOGIN
148static void die_if_nologin(void)
149{
150 FILE *fp;
151 int c;
152 int empty = 1;
153
154 fp = fopen_for_read("/etc/nologin");
155 if (!fp)
156 return;
157
158 while ((c = getc(fp)) != EOF) {
159 if (c == '\n')
160 bb_putchar('\r');
161 bb_putchar(c);
162 empty = 0;
163 }
164 if (empty)
165 puts("\r\nSystem closed for routine maintenance\r");
166
167 fclose(fp);
168 fflush_all();
169
170 tcdrain(STDOUT_FILENO);
171 exit(EXIT_FAILURE);
172}
173#else
174# define die_if_nologin() ((void)0)
175#endif
176
177#if ENABLE_SELINUX
178static void initselinux(char *username, char *full_tty,
179 security_context_t *user_sid)
180{
181 security_context_t old_tty_sid, new_tty_sid;
182
183 if (!is_selinux_enabled())
184 return;
185
186 if (get_default_context(username, NULL, user_sid)) {
187 bb_error_msg_and_die("can't get SID for %s", username);
188 }
189 if (getfilecon(full_tty, &old_tty_sid) < 0) {
190 bb_perror_msg_and_die("getfilecon(%s) failed", full_tty);
191 }
192 if (security_compute_relabel(*user_sid, old_tty_sid,
193 SECCLASS_CHR_FILE, &new_tty_sid) != 0) {
194 bb_perror_msg_and_die("security_change_sid(%s) failed", full_tty);
195 }
196 if (setfilecon(full_tty, new_tty_sid) != 0) {
197 bb_perror_msg_and_die("chsid(%s, %s) failed", full_tty, new_tty_sid);
198 }
199}
200#endif
201
202#if ENABLE_LOGIN_SCRIPTS
203static void run_login_script(struct passwd *pw, char *full_tty)
204{
205 char *t_argv[2];
206
207 t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT");
208 if (t_argv[0]) {
209 t_argv[1] = NULL;
210 xsetenv("LOGIN_TTY", full_tty);
211 xsetenv("LOGIN_USER", pw->pw_name);
212 xsetenv("LOGIN_UID", utoa(pw->pw_uid));
213 xsetenv("LOGIN_GID", utoa(pw->pw_gid));
214 xsetenv("LOGIN_SHELL", pw->pw_shell);
215 spawn_and_wait(t_argv);
216 unsetenv("LOGIN_TTY");
217 unsetenv("LOGIN_USER");
218 unsetenv("LOGIN_UID");
219 unsetenv("LOGIN_GID");
220 unsetenv("LOGIN_SHELL");
221 }
222}
223#else
224void run_login_script(struct passwd *pw, char *full_tty);
225#endif
226
227#if ENABLE_LOGIN_SESSION_AS_CHILD && ENABLE_PAM
228static void login_pam_end(pam_handle_t *pamh)
229{
230 int pamret;
231
232 pamret = pam_setcred(pamh, PAM_DELETE_CRED);
233 if (pamret != PAM_SUCCESS) {
234 bb_error_msg("pam_%s failed: %s (%d)", "setcred",
235 pam_strerror(pamh, pamret), pamret);
236 }
237 pamret = pam_close_session(pamh, 0);
238 if (pamret != PAM_SUCCESS) {
239 bb_error_msg("pam_%s failed: %s (%d)", "close_session",
240 pam_strerror(pamh, pamret), pamret);
241 }
242 pamret = pam_end(pamh, pamret);
243 if (pamret != PAM_SUCCESS) {
244 bb_error_msg("pam_%s failed: %s (%d)", "end",
245 pam_strerror(pamh, pamret), pamret);
246 }
247}
248#else
249# define login_pam_end(pamh) ((void)0)
250#endif
251
252static void get_username_or_die(char *buf, int size_buf)
253{
254 int c, cntdown;
255
256 cntdown = EMPTY_USERNAME_COUNT;
257 prompt:
258 print_login_prompt();
259
260 do {
261 c = getchar();
262 if (c == EOF)
263 exit(EXIT_FAILURE);
264 if (c == '\n') {
265 if (!--cntdown)
266 exit(EXIT_FAILURE);
267 goto prompt;
268 }
269 } while (isspace(c));
270
271 *buf++ = c;
272 if (!fgets(buf, size_buf-2, stdin))
273 exit(EXIT_FAILURE);
274 if (!strchr(buf, '\n'))
275 exit(EXIT_FAILURE);
276 while ((unsigned char)*buf > ' ')
277 buf++;
278 *buf = '\0';
279}
280
281static void motd(void)
282{
283 int fd;
284
285 fd = open(bb_path_motd_file, O_RDONLY);
286 if (fd >= 0) {
287 fflush_all();
288 bb_copyfd_eof(fd, STDOUT_FILENO);
289 close(fd);
290 }
291}
292
293static void alarm_handler(int sig UNUSED_PARAM)
294{
295
296
297
298 ndelay_on(STDOUT_FILENO);
299
300
301
302
303
304 tcsetattr_stdin_TCSANOW(&G.tty_attrs);
305 printf("\r\nLogin timed out after %u seconds\r\n", TIMEOUT);
306 fflush_all();
307
308
309 ndelay_off(STDOUT_FILENO);
310 _exit(EXIT_SUCCESS);
311}
312
313int login_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
314int login_main(int argc UNUSED_PARAM, char **argv)
315{
316 enum {
317 LOGIN_OPT_f = (1<<0),
318 LOGIN_OPT_h = (1<<1),
319 LOGIN_OPT_p = (1<<2),
320 };
321 char *fromhost;
322 char username[USERNAME_SIZE];
323 int run_by_root;
324 unsigned opt;
325 int count = 0;
326 struct passwd *pw;
327 char *opt_host = NULL;
328 char *opt_user = opt_user;
329 char *full_tty;
330 char *short_tty;
331 IF_SELINUX(security_context_t user_sid = NULL;)
332#if ENABLE_PAM
333 int pamret;
334 pam_handle_t *pamh;
335 const char *pamuser;
336 const char *failed_msg;
337 struct passwd pwdstruct;
338 char pwdbuf[256];
339 char **pamenv;
340#endif
341#if ENABLE_LOGIN_SESSION_AS_CHILD
342 pid_t child_pid;
343#endif
344
345 INIT_G();
346
347
348
349 run_by_root = !sanitize_env_if_suid();
350
351
352
353
354
355 bb_daemon_helper(DAEMON_CLOSE_EXTRA_FDS);
356
357 username[0] = '\0';
358 opt = getopt32(argv, "f:h:p", &opt_user, &opt_host);
359 if (opt & LOGIN_OPT_f) {
360 if (!run_by_root)
361 bb_error_msg_and_die("-f is for root only");
362 safe_strncpy(username, opt_user, sizeof(username));
363 }
364 argv += optind;
365 if (argv[0])
366 safe_strncpy(username, argv[0], sizeof(username));
367
368
369 if (tcgetattr(STDIN_FILENO, &G.tty_attrs) < 0
370 || !isatty(STDOUT_FILENO)
371
372 ) {
373 return EXIT_FAILURE;
374 }
375
376
377 signal(SIGALRM, alarm_handler);
378 alarm(TIMEOUT);
379
380
381 full_tty = xmalloc_ttyname(STDIN_FILENO);
382 if (!full_tty)
383 full_tty = xstrdup("UNKNOWN");
384 short_tty = skip_dev_pfx(full_tty);
385
386 if (opt_host) {
387 fromhost = xasprintf(" on '%s' from '%s'", short_tty, opt_host);
388 } else {
389 fromhost = xasprintf(" on '%s'", short_tty);
390 }
391
392
393
394
395 openlog(applet_name, LOG_PID | LOG_CONS, LOG_AUTH);
396
397 while (1) {
398
399 tcflush(0, TCIFLUSH);
400
401 if (!username[0])
402 get_username_or_die(username, sizeof(username));
403
404#if ENABLE_PAM
405 pamret = pam_start("login", username, &conv, &pamh);
406 if (pamret != PAM_SUCCESS) {
407 failed_msg = "start";
408 goto pam_auth_failed;
409 }
410
411 pamret = pam_set_item(pamh, PAM_TTY, short_tty);
412 if (pamret != PAM_SUCCESS) {
413 failed_msg = "set_item(TTY)";
414 goto pam_auth_failed;
415 }
416
417 if (opt_host) {
418 pamret = pam_set_item(pamh, PAM_RHOST, opt_host);
419 if (pamret != PAM_SUCCESS) {
420 failed_msg = "set_item(RHOST)";
421 goto pam_auth_failed;
422 }
423 }
424 if (!(opt & LOGIN_OPT_f)) {
425 pamret = pam_authenticate(pamh, 0);
426 if (pamret != PAM_SUCCESS) {
427 failed_msg = "authenticate";
428 goto pam_auth_failed;
429
430
431
432
433 }
434 }
435
436 pamret = pam_acct_mgmt(pamh, 0);
437 if (pamret != PAM_SUCCESS) {
438 failed_msg = "acct_mgmt";
439 goto pam_auth_failed;
440 }
441
442 pamuser = NULL;
443
444
445 if (pam_get_item(pamh, PAM_USER, (void*)&pamuser) != PAM_SUCCESS) {
446 failed_msg = "get_item(USER)";
447 goto pam_auth_failed;
448 }
449 if (!pamuser || !pamuser[0])
450 goto auth_failed;
451 safe_strncpy(username, pamuser, sizeof(username));
452
453
454
455 pw = NULL;
456 getpwnam_r(username, &pwdstruct, pwdbuf, sizeof(pwdbuf), &pw);
457 if (!pw)
458 goto auth_failed;
459 pamret = pam_open_session(pamh, 0);
460 if (pamret != PAM_SUCCESS) {
461 failed_msg = "open_session";
462 goto pam_auth_failed;
463 }
464 pamret = pam_setcred(pamh, PAM_ESTABLISH_CRED);
465 if (pamret != PAM_SUCCESS) {
466 failed_msg = "setcred";
467 goto pam_auth_failed;
468 }
469 break;
470
471 pam_auth_failed:
472
473
474 syslog(LOG_WARNING, "pam_%s call failed: %s (%d)", failed_msg,
475 pam_strerror(pamh, pamret), pamret);
476 login_pam_end(pamh);
477 safe_strncpy(username, "UNKNOWN", sizeof(username));
478#else
479 pw = getpwnam(username);
480 if (!pw) {
481 strcpy(username, "UNKNOWN");
482 goto fake_it;
483 }
484
485 if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*')
486 goto auth_failed;
487
488 if (opt & LOGIN_OPT_f)
489 break;
490
491 if (pw->pw_uid == 0 && !is_tty_secure(short_tty))
492 goto auth_failed;
493
494
495 if (!pw->pw_passwd[0])
496 break;
497 fake_it:
498
499
500
501
502 if (ask_and_check_password(pw) > 0)
503 break;
504#endif
505 auth_failed:
506 opt &= ~LOGIN_OPT_f;
507 bb_do_delay(LOGIN_FAIL_DELAY);
508
509 puts("Login incorrect");
510 if (++count == 3) {
511 syslog(LOG_WARNING, "invalid password for '%s'%s",
512 username, fromhost);
513
514 if (ENABLE_FEATURE_CLEAN_UP)
515 free(fromhost);
516
517 return EXIT_FAILURE;
518 }
519 username[0] = '\0';
520 }
521
522 alarm(0);
523
524
525 if (pw->pw_uid != 0)
526 die_if_nologin();
527
528#if ENABLE_LOGIN_SESSION_AS_CHILD
529 child_pid = vfork();
530 if (child_pid != 0) {
531 if (child_pid < 0)
532 bb_perror_msg("vfork");
533 else {
534 wait_for_exitstatus(child_pid);
535 update_utmp_DEAD_PROCESS(child_pid);
536 }
537 login_pam_end(pamh);
538 return 0;
539 }
540#endif
541
542 IF_SELINUX(initselinux(username, full_tty, &user_sid);)
543
544
545
546 fchown(0, pw->pw_uid, pw->pw_gid);
547 fchmod(0, 0600);
548
549 update_utmp(getpid(), USER_PROCESS, short_tty, username, run_by_root ? opt_host : NULL);
550
551
552 if (ENABLE_LOGIN_SCRIPTS && run_by_root)
553 run_login_script(pw, full_tty);
554
555 change_identity(pw);
556 setup_environment(pw->pw_shell,
557 (!(opt & LOGIN_OPT_p) * SETUP_ENV_CLEARENV) + SETUP_ENV_CHANGEENV,
558 pw);
559
560#if ENABLE_PAM
561
562
563 pamenv = pam_getenvlist(pamh);
564 if (pamenv) while (*pamenv) {
565 putenv(*pamenv);
566 pamenv++;
567 }
568#endif
569
570 if (access(".hushlogin", F_OK) != 0)
571 motd();
572
573 if (pw->pw_uid == 0)
574 syslog(LOG_INFO, "root login%s", fromhost);
575
576 if (ENABLE_FEATURE_CLEAN_UP)
577 free(fromhost);
578
579
580
581 IF_SELINUX(set_current_security_context(user_sid);)
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602 signal(SIGINT, SIG_DFL);
603
604
605 run_shell(pw->pw_shell, 1, NULL);
606
607
608}
609