gzip: add support for compression levels 4-9
[oweals/busybox.git] / loginutils / login.c
index f2563dc090a2319fbf579fd81e670e1fdde25c1b..b9d910331e0a97bae3e91cc77f9cc1006730ee79 100644 (file)
@@ -7,7 +7,6 @@
 //usage:       "[-p] [-h HOST] [[-f] USER]"
 //usage:#define login_full_usage "\n\n"
 //usage:       "Begin a new session on the system\n"
-//usage:     "\nOptions:"
 //usage:     "\n       -f      Don't authenticate (user already authenticated)"
 //usage:     "\n       -h      Name of the remote host"
 //usage:     "\n       -p      Preserve environment"
@@ -38,11 +37,17 @@ static const struct pam_conv conv = {
 enum {
        TIMEOUT = 60,
        EMPTY_USERNAME_COUNT = 10,
-       USERNAME_SIZE = 32,
+       /* Some users found 32 chars limit to be too low: */
+       USERNAME_SIZE = 64,
        TTYNAME_SIZE = 32,
 };
 
-static char* short_tty;
+struct globals {
+       struct termios tty_attrs;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { } while (0)
+
 
 #if ENABLE_FEATURE_NOLOGIN
 static void die_if_nologin(void)
@@ -75,7 +80,7 @@ static void die_if_nologin(void)
 #endif
 
 #if ENABLE_FEATURE_SECURETTY && !ENABLE_PAM
-static int check_securetty(void)
+static int check_securetty(const char *short_tty)
 {
        char *buf = (char*)"/etc/securetty"; /* any non-NULL is ok */
        parser_t *parser = config_open2("/etc/securetty", fopen_for_read);
@@ -90,7 +95,7 @@ static int check_securetty(void)
        return buf != NULL;
 }
 #else
-static ALWAYS_INLINE int check_securetty(void) { return 1; }
+static ALWAYS_INLINE int check_securetty(const char *short_tty UNUSED_PARAM) { return 1; }
 #endif
 
 #if ENABLE_SELINUX
@@ -143,6 +148,29 @@ static void run_login_script(struct passwd *pw, char *full_tty)
 void run_login_script(struct passwd *pw, char *full_tty);
 #endif
 
+#if ENABLE_LOGIN_SESSION_AS_CHILD && ENABLE_PAM
+static void login_pam_end(pam_handle_t *pamh)
+{
+       int pamret;
+
+       pamret = pam_setcred(pamh, PAM_DELETE_CRED);
+       if (pamret != PAM_SUCCESS) {
+               bb_error_msg("pam_%s failed: %s (%d)", "setcred",
+                       pam_strerror(pamh, pamret), pamret);
+       }
+       pamret = pam_close_session(pamh, 0);
+       if (pamret != PAM_SUCCESS) {
+               bb_error_msg("pam_%s failed: %s (%d)", "close_session",
+                       pam_strerror(pamh, pamret), pamret);
+       }
+       pamret = pam_end(pamh, pamret);
+       if (pamret != PAM_SUCCESS) {
+               bb_error_msg("pam_%s failed: %s (%d)", "end",
+                       pam_strerror(pamh, pamret), pamret);
+       }
+}
+#endif /* ENABLE_PAM */
+
 static void get_username_or_die(char *buf, int size_buf)
 {
        int c, cntdown;
@@ -186,15 +214,21 @@ static void motd(void)
 
 static void alarm_handler(int sig UNUSED_PARAM)
 {
-       /* This is the escape hatch!  Poor serial line users and the like
+       /* 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);
-       printf("\r\nLogin timed out after %d seconds\r\n", TIMEOUT);
+       ndelay_on(STDOUT_FILENO);
+       /* Test for correct attr restoring:
+        * run "getty 0 -" from a shell, enter bogus username, stop at
+        * password prompt, let it time out. Without the tcsetattr below,
+        * when you are back at shell prompt, echo will be still off.
+        */
+       tcsetattr_stdin_TCSANOW(&G.tty_attrs);
+       printf("\r\nLogin timed out after %u seconds\r\n", TIMEOUT);
        fflush_all();
        /* unix API is brain damaged regarding O_NONBLOCK,
         * we should undo it, or else we can affect other processes */
-       ndelay_off(1);
+       ndelay_off(STDOUT_FILENO);
        _exit(EXIT_SUCCESS);
 }
 
@@ -215,6 +249,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
        char *opt_host = NULL;
        char *opt_user = opt_user; /* for compiler */
        char *full_tty;
+       char *short_tty;
        IF_SELINUX(security_context_t user_sid = NULL;)
 #if ENABLE_PAM
        int pamret;
@@ -225,10 +260,11 @@ int login_main(int argc UNUSED_PARAM, char **argv)
        char pwdbuf[256];
        char **pamenv;
 #endif
+#if ENABLE_LOGIN_SESSION_AS_CHILD
+       pid_t child_pid;
+#endif
 
-       username[0] = '\0';
-       signal(SIGALRM, alarm_handler);
-       alarm(TIMEOUT);
+       INIT_G();
 
        /* More of suid paranoia if called by non-root: */
        /* Clear dangerous stuff, set PATH */
@@ -240,6 +276,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
         * (The name of the function is misleading. Not daemonizing here.) */
        bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE | DAEMON_CLOSE_EXTRA_FDS, NULL);
 
+       username[0] = '\0';
        opt = getopt32(argv, "f:h:p", &opt_user, &opt_host);
        if (opt & LOGIN_OPT_f) {
                if (!run_by_root)
@@ -250,9 +287,19 @@ int login_main(int argc UNUSED_PARAM, char **argv)
        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(STDIN_FILENO) || !isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO))
+       /* Save tty attributes - and by doing it, check that it's indeed a tty */
+       if (tcgetattr(STDIN_FILENO, &G.tty_attrs) < 0
+        || !isatty(STDOUT_FILENO)
+        /*|| !isatty(STDERR_FILENO) - no, guess some people might want to redirect this */
+       ) {
                return EXIT_FAILURE;  /* Must be a terminal */
+       }
+
+       /* We install timeout handler only _after_ we saved G.tty_attrs */
+       signal(SIGALRM, alarm_handler);
+       alarm(TIMEOUT);
+
+       /* Find out and memorize our tty name */
        full_tty = xmalloc_ttyname(STDIN_FILENO);
        if (!full_tty)
                full_tty = xstrdup("UNKNOWN");
@@ -296,14 +343,16 @@ int login_main(int argc UNUSED_PARAM, char **argv)
                                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)
-                        */
+               if (!(opt & LOGIN_OPT_f)) {
+                       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);
@@ -360,15 +409,18 @@ int login_main(int argc UNUSED_PARAM, char **argv)
                if (opt & LOGIN_OPT_f)
                        break; /* -f USER: success without asking passwd */
 
-               if (pw->pw_uid == 0 && !check_securetty())
+               if (pw->pw_uid == 0 && !check_securetty(short_tty))
                        goto auth_failed;
 
                /* Don't check the password if password entry is empty (!) */
                if (!pw->pw_passwd[0])
                        break;
  fake_it:
-               /* authorization takes place here */
-               if (correct_password(pw))
+               /* Password reading and authorization takes place here.
+                * Note that reads (in no-echo mode) trash tty attributes.
+                * If we get interrupted by SIGALRM, we need to restore attrs.
+                */
+               if (ask_and_check_password(pw) > 0)
                        break;
 #endif /* ENABLE_PAM */
  auth_failed:
@@ -394,7 +446,22 @@ int login_main(int argc UNUSED_PARAM, char **argv)
        if (pw->pw_uid != 0)
                die_if_nologin();
 
-       IF_SELINUX(initselinux(username, full_tty, &user_sid));
+#if ENABLE_LOGIN_SESSION_AS_CHILD
+       child_pid = vfork();
+       if (child_pid != 0) {
+               if (child_pid < 0)
+                       bb_perror_msg("vfork");
+               else {
+                       if (safe_waitpid(child_pid, NULL, 0) == -1)
+                               bb_perror_msg("waitpid");
+                       update_utmp_DEAD_PROCESS(child_pid);
+               }
+               IF_PAM(login_pam_end(pamh);)
+               return 0;
+       }
+#endif
+
+       IF_SELINUX(initselinux(username, full_tty, &user_sid);)
 
        /* Try these, but don't complain if they fail.
         * _f_chown is safe wrt race t=ttyname(0);...;chown(t); */