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