login: fix /etc/nologin handling (should prohibit non-root LOGINS, not running login by non-root). minor code shrink.
No binary (build failed)
—
(log yok)
| @@ -52,7 +52,7 @@ static char* short_tty; | |
| * command line flags. | |
| */ | |
| - | static void read_or_build_utent(struct utmp *utptr, int picky) |
| + | static void read_or_build_utent(struct utmp *utptr, int run_by_root) |
| { | |
| struct utmp *ut; | |
| pid_t pid = getpid(); | |
| @@ -60,30 +60,33 @@ static void read_or_build_utent(struct utmp *utptr, int picky) | |
| setutent(); | |
| /* First, try to find a valid utmp entry for this process. */ | |
| - | while ((ut = getutent())) |
| - | if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] && |
| - | (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS)) |
| - | break; |
| + | /* If there is one, just use it. */ |
| + | while ((ut = getutent()) != NULL) |
| + | if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] |
| + | && (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS) |
| + | ) { |
| + | *utptr = *ut; /* struct copy */ |
| + | if (run_by_root) /* why only for root? */ |
| + | memset(utptr->ut_host, 0, sizeof(utptr->ut_host)); |
| + | return; |
| + | } |
| - | /* If there is one, just use it, otherwise create a new one. */ |
| - | if (ut) { |
| - | *utptr = *ut; |
| - | } else { |
| - | if (picky) |
| - | bb_error_msg_and_die("no utmp entry found"); |
| - | |
| - | memset(utptr, 0, sizeof(*utptr)); |
| - | utptr->ut_type = LOGIN_PROCESS; |
| - | utptr->ut_pid = pid; |
| - | strncpy(utptr->ut_line, short_tty, sizeof(utptr->ut_line)); |
| - | /* This one is only 4 chars wide. Try to fit something |
| - | * 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_tv.tv_sec = time(NULL); |
| - | } |
| - | if (!picky) /* root login */ |
| - | memset(utptr->ut_host, 0, sizeof(utptr->ut_host)); |
| + | // Why? Do we require non-root to exec login from another |
| + | // former login process (e.g. login shell)? Some login's have |
| + | // login shells as children, so it won't work... |
| + | // if (!run_by_root) |
| + | // bb_error_msg_and_die("no utmp entry found"); |
| + | |
| + | /* Otherwise create a new one. */ |
| + | memset(utptr, 0, sizeof(*utptr)); |
| + | utptr->ut_type = LOGIN_PROCESS; |
| + | utptr->ut_pid = pid; |
| + | strncpy(utptr->ut_line, short_tty, sizeof(utptr->ut_line)); |
| + | /* This one is only 4 chars wide. Try to fit something |
| + | * 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_tv.tv_sec = time(NULL); |
| } | |
| /* | |
| @@ -109,7 +112,7 @@ static void write_utent(struct utmp *utptr, const char *username) | |
| #endif | |
| } | |
| #else /* !ENABLE_FEATURE_UTMP */ | |
| - | #define read_or_build_utent(utptr, picky) ((void)0) |
| + | #define read_or_build_utent(utptr, run_by_root) ((void)0) |
| #define write_utent(utptr, username) ((void)0) | |
| #endif /* !ENABLE_FEATURE_UTMP */ | |
| @@ -225,7 +228,7 @@ int login_main(int argc UNUSED_PARAM, char **argv) | |
| char *fromhost; | |
| char username[USERNAME_SIZE]; | |
| const char *tmp; | |
| - | int amroot; |
| + | int run_by_root; |
| unsigned opt; | |
| int count = 0; | |
| struct passwd *pw; | |
| @@ -248,8 +251,9 @@ int login_main(int argc UNUSED_PARAM, char **argv) | |
| 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 */ |
| + | /* More of suid paranoia if called by non-root: */ |
| + | /* Clear dangerous stuff, set PATH */ |
| + | run_by_root = !sanitize_env_if_suid(); |
| /* Mandatory paranoia for suid applet: | |
| * ensure that fd# 0,1,2 are opened (at least to /dev/null) | |
| @@ -259,7 +263,7 @@ int login_main(int argc UNUSED_PARAM, char **argv) | |
| opt = getopt32(argv, "f:h:p", &opt_user, &opt_host); | |
| if (opt & LOGIN_OPT_f) { | |
| - | if (!amroot) |
| + | if (!run_by_root) |
| bb_error_msg_and_die("-f is for root only"); | |
| safe_strncpy(username, opt_user, sizeof(username)); | |
| } | |
| @@ -278,7 +282,7 @@ int login_main(int argc UNUSED_PARAM, char **argv) | |
| short_tty = full_tty + 5; | |
| } | |
| - | read_or_build_utent(&utent, !amroot); |
| + | read_or_build_utent(&utent, run_by_root); |
| if (opt & LOGIN_OPT_h) { | |
| USE_FEATURE_UTMP( | |
| @@ -396,10 +400,12 @@ int login_main(int argc UNUSED_PARAM, char **argv) | |
| return EXIT_FAILURE; | |
| } | |
| username[0] = '\0'; | |
| - | } |
| + | } /* while (1) */ |
| alarm(0); | |
| - | if (!amroot) |
| + | /* We can ignore /etc/nologin if we are logging in as root, |
| + | * it doesn't matter whether we are run by root or not */ |
| + | if (pw->pw_uid != 0) |
| die_if_nologin(); | |
| write_utent(&utent, username); | |
| @@ -433,7 +439,7 @@ int login_main(int argc UNUSED_PARAM, char **argv) | |
| fchmod(0, 0600); | |
| /* We trust environment only if we run by root */ | |
| - | if (ENABLE_LOGIN_SCRIPTS && amroot) { |
| + | if (ENABLE_LOGIN_SCRIPTS && run_by_root) { |
| char *t_argv[2]; | |
| t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT"); | |
| @@ -479,14 +485,16 @@ int login_main(int argc UNUSED_PARAM, char **argv) | |
| // bb_setpgrp(); | |
| // If this stuff is really needed, add it and explain why! | |
| - | /* set signals to defaults */ |
| - | signal(SIGALRM, SIG_DFL); |
| + | /* Set signals to defaults */ |
| + | /*signal(SIGALRM, SIG_DFL); - not needed, we already set it |
| + | * to non-SIG_IGN, and on exec such signals are reset to SIG_DFL */ |
| + | |
| /* Is this correct? This way user can ctrl-c out of /etc/profile, | |
| * potentially creating security breach (tested with bash 3.0). | |
| * But without this, bash 3.0 will not enable ctrl-c either. | |
| * Maybe bash is buggy? | |
| * Need to find out what standards say about /bin/login - | |
| - | * should it leave SIGINT etc enabled or disabled? */ |
| + | * should we leave SIGINT etc enabled or disabled? */ |
| signal(SIGINT, SIG_DFL); | |
| /* Exec login shell with no additional parameters */ | |