X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=loginutils%2Flogin.c;h=8732b99f1e2b3c28404f7871bb2198f894ffb6cd;hb=9b44613202a6f2f080ec23746d0680dcef88628d;hp=7b60fd017caa6fdab1ead31deae522d60efbc32e;hpb=fe7cd642b0b732f5d41403c2f6983ad676b69dd9;p=oweals%2Fbusybox.git diff --git a/loginutils/login.c b/loginutils/login.c index 7b60fd017..8732b99f1 100644 --- a/loginutils/login.c +++ b/loginutils/login.c @@ -4,9 +4,9 @@ */ #include "libbb.h" +#include #include #include -#include #if ENABLE_SELINUX #include /* for is_selinux_enabled() */ @@ -14,6 +14,19 @@ #include /* for security class definitions */ #endif +#if ENABLE_PAM +/* PAM may include . We may need to undefine bbox's stub define: */ +#undef setlocale +/* For some obscure reason, PAM is not in pam/xxx, but in security/xxx. + * Apparently they like to confuse people. */ +#include +#include +static const struct pam_conv conv = { + misc_conv, + NULL +}; +#endif + enum { TIMEOUT = 60, EMPTY_USERNAME_COUNT = 10, @@ -67,7 +80,7 @@ static void read_or_build_utent(struct utmp *utptr, int picky) * remotely meaningful by skipping "tty"... */ strncpy(utptr->ut_id, short_tty + 3, sizeof(utptr->ut_id)); strncpy(utptr->ut_user, "LOGIN", sizeof(utptr->ut_user)); - utptr->ut_time = time(NULL); + utptr->ut_tv.tv_sec = time(NULL); } if (!picky) /* root login */ memset(utptr->ut_host, 0, sizeof(utptr->ut_host)); @@ -83,7 +96,7 @@ static void write_utent(struct utmp *utptr, const char *username) { utptr->ut_type = USER_PROCESS; strncpy(utptr->ut_user, username, sizeof(utptr->ut_user)); - utptr->ut_time = time(NULL); + utptr->ut_tv.tv_sec = time(NULL); /* other fields already filled in by read_or_build_utent above */ setutent(); pututline(utptr); @@ -101,7 +114,7 @@ static void write_utent(struct utmp *utptr, const char *username) #endif /* !ENABLE_FEATURE_UTMP */ #if ENABLE_FEATURE_NOLOGIN -static void die_if_nologin_and_non_root(int amroot) +static void die_if_nologin(void) { FILE *fp; int c; @@ -109,49 +122,34 @@ static void die_if_nologin_and_non_root(int amroot) if (access("/etc/nologin", F_OK)) return; - fp = fopen("/etc/nologin", "r"); + fp = fopen_for_read("/etc/nologin"); if (fp) { while ((c = getc(fp)) != EOF) - putchar((c=='\n') ? '\r' : c); + bb_putchar((c=='\n') ? '\r' : c); fflush(stdout); fclose(fp); } else puts("\r\nSystem closed for routine maintenance\r"); - if (!amroot) - exit(1); - puts("\r\n[Disconnect bypassed -- root login allowed]\r"); + exit(EXIT_FAILURE); } #else -static ALWAYS_INLINE void die_if_nologin_and_non_root(int amroot) {} +static ALWAYS_INLINE void die_if_nologin(void) {} #endif -#if ENABLE_FEATURE_SECURETTY +#if ENABLE_FEATURE_SECURETTY && !ENABLE_PAM static int check_securetty(void) { - FILE *fp; - int i; - char buf[256]; - - fp = fopen("/etc/securetty", "r"); - if (!fp) { - /* A missing securetty file is not an error. */ - return 1; - } - while (fgets(buf, sizeof(buf)-1, fp)) { - for (i = strlen(buf)-1; i >= 0; --i) { - if (!isspace(buf[i])) - break; - } - buf[++i] = '\0'; - if (!buf[0] || (buf[0] == '#')) - continue; - if (strcmp(buf, short_tty) == 0) { - fclose(fp); - return 1; - } + char *buf = (char*)"/etc/securetty"; /* any non-NULL is ok */ + parser_t *parser = config_open2("/etc/securetty", fopen_for_read); + while (config_read(parser, &buf, 1, 1, "# \t", PARSE_NORMAL)) { + if (strcmp(buf, short_tty) == 0) + break; + buf = NULL; } - fclose(fp); - return 0; + config_close(parser); + /* buf != NULL here if config file was not found, empty + * or line was found which equals short_tty */ + return buf != NULL; } #else static ALWAYS_INLINE int check_securetty(void) { return 1; } @@ -167,18 +165,18 @@ static void get_username_or_die(char *buf, int size_buf) /* skip whitespace */ do { c = getchar(); - if (c == EOF) exit(1); + if (c == EOF) exit(EXIT_FAILURE); if (c == '\n') { - if (!--cntdown) exit(1); + if (!--cntdown) exit(EXIT_FAILURE); goto prompt; } } while (isspace(c)); *buf++ = c; if (!fgets(buf, size_buf-2, stdin)) - exit(1); + exit(EXIT_FAILURE); if (!strchr(buf, '\n')) - exit(1); + exit(EXIT_FAILURE); while (isgraph(*buf)) buf++; *buf = '\0'; } @@ -188,51 +186,64 @@ static void motd(void) int fd; fd = open(bb_path_motd_file, O_RDONLY); - if (fd) { + if (fd >= 0) { fflush(stdout); bb_copyfd_eof(fd, STDOUT_FILENO); close(fd); } } -static void alarm_handler(int sig ATTRIBUTE_UNUSED) +static void alarm_handler(int sig UNUSED_PARAM) { /* This is the escape hatch! Poor serial line users and the like * arrive here when their connection is broken. * We don't want to block here */ ndelay_on(1); - ndelay_on(2); printf("\r\nLogin timed out after %d seconds\r\n", TIMEOUT); - exit(EXIT_SUCCESS); + fflush(stdout); + /* unix API is brain damaged regarding O_NONBLOCK, + * we should undo it, or else we can affect other processes */ + ndelay_off(1); + _exit(EXIT_SUCCESS); } -int login_main(int argc, char **argv); -int login_main(int argc, char **argv) +int login_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int login_main(int argc UNUSED_PARAM, char **argv) { enum { LOGIN_OPT_f = (1<<0), LOGIN_OPT_h = (1<<1), LOGIN_OPT_p = (1<<2), }; - char fromhost[512]; + char *fromhost; char username[USERNAME_SIZE]; const char *tmp; int amroot; unsigned opt; int count = 0; struct passwd *pw; - char *opt_host = NULL; - char *opt_user = NULL; + char *opt_host = opt_host; /* for compiler */ + char *opt_user = opt_user; /* for compiler */ char full_tty[TTYNAME_SIZE]; USE_SELINUX(security_context_t user_sid = NULL;) USE_FEATURE_UTMP(struct utmp utent;) +#if ENABLE_PAM + int pamret; + pam_handle_t *pamh; + const char *pamuser; + const char *failed_msg; + struct passwd pwdstruct; + char pwdbuf[256]; +#endif short_tty = full_tty; username[0] = '\0'; - amroot = (getuid() == 0); signal(SIGALRM, alarm_handler); alarm(TIMEOUT); + /* More of suid paranoia if called by non-root */ + amroot = !sanitize_env_if_suid(); /* Clear dangerous stuff, set PATH */ + /* Mandatory paranoia for suid applet: * ensure that fd# 0,1,2 are opened (at least to /dev/null) * and any extra open fd's are closed. @@ -245,8 +256,9 @@ int login_main(int argc, char **argv) bb_error_msg_and_die("-f is for root only"); safe_strncpy(username, opt_user, sizeof(username)); } - if (optind < argc) /* user from command line (getty) */ - safe_strncpy(username, argv[optind], sizeof(username)); + argv += optind; + if (argv[0]) /* user from command line (getty) */ + safe_strncpy(username, argv[0], sizeof(username)); /* Let's find out and memorize our tty */ if (!isatty(0) || !isatty(1) || !isatty(2)) @@ -261,24 +273,88 @@ int login_main(int argc, char **argv) read_or_build_utent(&utent, !amroot); - if (opt_host) { + if (opt & LOGIN_OPT_h) { USE_FEATURE_UTMP( safe_strncpy(utent.ut_host, opt_host, sizeof(utent.ut_host)); ) - snprintf(fromhost, sizeof(fromhost)-1, " on '%.100s' from " - "'%.200s'", short_tty, opt_host); + fromhost = xasprintf(" on '%s' from '%s'", short_tty, opt_host); } else - snprintf(fromhost, sizeof(fromhost)-1, " on '%.100s'", short_tty); + fromhost = xasprintf(" on '%s'", short_tty); - // Was breaking "login " from shell command line: - // bb_setpgrp(); + /* Was breaking "login " from shell command line: */ + /*bb_setpgrp();*/ openlog(applet_name, LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH); while (1) { + /* flush away any type-ahead (as getty does) */ + ioctl(0, TCFLSH, TCIFLUSH); + if (!username[0]) get_username_or_die(username, sizeof(username)); +#if ENABLE_PAM + pamret = pam_start("login", username, &conv, &pamh); + if (pamret != PAM_SUCCESS) { + failed_msg = "start"; + goto pam_auth_failed; + } + /* set TTY (so things like securetty work) */ + pamret = pam_set_item(pamh, PAM_TTY, short_tty); + if (pamret != PAM_SUCCESS) { + failed_msg = "set_item(TTY)"; + goto pam_auth_failed; + } + pamret = pam_authenticate(pamh, 0); + if (pamret != PAM_SUCCESS) { + failed_msg = "authenticate"; + goto pam_auth_failed; + /* TODO: or just "goto auth_failed" + * since user seems to enter wrong password + * (in this case pamret == 7) + */ + } + /* check that the account is healthy */ + pamret = pam_acct_mgmt(pamh, 0); + if (pamret != PAM_SUCCESS) { + failed_msg = "acct_mgmt"; + goto pam_auth_failed; + } + /* read user back */ + pamuser = NULL; + /* gcc: "dereferencing type-punned pointer breaks aliasing rules..." + * thus we cast to (void*) */ + if (pam_get_item(pamh, PAM_USER, (void*)&pamuser) != PAM_SUCCESS) { + failed_msg = "get_item(USER)"; + goto pam_auth_failed; + } + if (!pamuser || !pamuser[0]) + goto auth_failed; + safe_strncpy(username, pamuser, sizeof(username)); + /* Don't use "pw = getpwnam(username);", + * PAM is said to be capable of destroying static storage + * used by getpwnam(). We are using safe(r) function */ + pw = NULL; + getpwnam_r(username, &pwdstruct, pwdbuf, sizeof(pwdbuf), &pw); + if (!pw) + goto auth_failed; + pamret = pam_open_session(pamh, 0); + if (pamret != PAM_SUCCESS) { + failed_msg = "open_session"; + goto pam_auth_failed; + } + pamret = pam_setcred(pamh, PAM_ESTABLISH_CRED); + if (pamret != PAM_SUCCESS) { + failed_msg = "setcred"; + goto pam_auth_failed; + } + break; /* success, continue login process */ + + pam_auth_failed: + bb_error_msg("pam_%s call failed: %s (%d)", failed_msg, + pam_strerror(pamh, pamret), pamret); + safe_strncpy(username, "UNKNOWN", sizeof(username)); +#else /* not PAM */ pw = getpwnam(username); if (!pw) { strcpy(username, "UNKNOWN"); @@ -301,9 +377,11 @@ int login_main(int argc, char **argv) /* authorization takes place here */ if (correct_password(pw)) break; +#endif /* ENABLE_PAM */ auth_failed: opt &= ~LOGIN_OPT_f; bb_do_delay(FAIL_DELAY); + /* TODO: doesn't sound like correct English phrase to me */ puts("Login incorrect"); if (++count == 3) { syslog(LOG_WARNING, "invalid password for '%s'%s", @@ -314,7 +392,8 @@ int login_main(int argc, char **argv) } alarm(0); - die_if_nologin_and_non_root(pw->pw_uid == 0); + if (!amroot) + die_if_nologin(); write_utent(&utent, username); @@ -346,7 +425,8 @@ int login_main(int argc, char **argv) fchown(0, pw->pw_uid, pw->pw_gid); fchmod(0, 0600); - if (ENABLE_LOGIN_SCRIPTS) { + /* We trust environment only if we run by root */ + if (ENABLE_LOGIN_SCRIPTS && amroot) { char *t_argv[2]; t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT"); @@ -357,9 +437,12 @@ int login_main(int argc, char **argv) xsetenv("LOGIN_UID", utoa(pw->pw_uid)); xsetenv("LOGIN_GID", utoa(pw->pw_gid)); xsetenv("LOGIN_SHELL", pw->pw_shell); - xspawn(t_argv); /* NOMMU-friendly */ - /* All variables are unset by setup_environment */ - wait(NULL); + spawn_and_wait(t_argv); /* NOMMU-friendly */ + unsetenv("LOGIN_TTY" ); + unsetenv("LOGIN_USER" ); + unsetenv("LOGIN_UID" ); + unsetenv("LOGIN_GID" ); + unsetenv("LOGIN_SHELL"); } } @@ -367,7 +450,8 @@ int login_main(int argc, char **argv) tmp = pw->pw_shell; if (!tmp || !*tmp) tmp = DEFAULT_SHELL; - setup_environment(tmp, 1, !(opt & LOGIN_OPT_p), pw); + /* setup_environment params: shell, clear_env, change_env, pw */ + setup_environment(tmp, !(opt & LOGIN_OPT_p), 1, pw); motd(); @@ -398,7 +482,8 @@ int login_main(int argc, char **argv) * should it leave SIGINT etc enabled or disabled? */ signal(SIGINT, SIG_DFL); - run_shell(tmp, 1, 0, 0); /* exec the shell finally */ + /* Exec login shell with no additional parameters */ + run_shell(tmp, 1, NULL, NULL); - return EXIT_FAILURE; + /* return EXIT_FAILURE; - not reached */ }