]> granicus.if.org Git - shadow/blobdiff - src/login.c
* src/su.c: Extract export of environment from main().
[shadow] / src / login.c
index cb08db0f964d01b6583033e232ddbd33f07eba98..c24fc860d7cfb4f75b790acefed37c4eeab9d93b 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 1989 - 1994, Julianne Frances Haugh
  * Copyright (c) 1996 - 2001, Marek Michałkiewicz
  * Copyright (c) 2001 - 2006, Tomasz Kłoczko
- * Copyright (c) 2007 - 2009, Nicolas François
+ * Copyright (c) 2007 - 2010, Nicolas François
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -51,7 +51,9 @@
 #include "getdef.h"
 #include "prototypes.h"
 #include "pwauth.h"
+/*@-exitarg@*/
 #include "exitcodes.h"
+
 #ifdef USE_PAM
 #include "pam_defs.h"
 
@@ -80,10 +82,10 @@ static pam_handle_t *pamh = NULL;
 /*
  * Global variables
  */
-char *Prog;
+const char *Prog;
 
 static const char *hostname = "";
-static char *username = NULL;
+static /*@null@*/ /*@only@*/char *username = NULL;
 static int reason = PW_LOGIN;
 
 #ifndef USE_PAM
@@ -122,12 +124,12 @@ extern char **environ;
 /* local function prototypes */
 static void usage (void);
 static void setup_tty (void);
-static void process_flags (int, char *const *);
-static const char *get_failent_user (const char *user);
-static void update_utmp (const char *username,
+static void process_flags (int argc, char *const *argv);
+static /*@observer@*/const char *get_failent_user (/*@returned@*/const char *user);
+static void update_utmp (const char *user,
                          const char *tty,
-                         const char *hostname,
-                         const struct utmp *utent);
+                         const char *host,
+                         /*@null@*/const struct utmp *utent);
 
 #ifndef USE_PAM
 static struct faillog faillog;
@@ -165,53 +167,53 @@ static void usage (void)
 static void setup_tty (void)
 {
        TERMIO termio;
-       int erasechar;
-       int killchar;
 
        if (GTTY (0, &termio) == 0) {   /* get terminal characteristics */
+               int erasechar;
+               int killchar;
 
-       /*
-        * Add your favorite terminal modes here ...
-        */
-       termio.c_lflag |= ISIG | ICANON | ECHO | ECHOE;
-       termio.c_iflag |= ICRNL;
+               /*
+                * Add your favorite terminal modes here ...
+                */
+               termio.c_lflag |= ISIG | ICANON | ECHO | ECHOE;
+               termio.c_iflag |= ICRNL;
 
 #if defined(ECHOKE) && defined(ECHOCTL)
-       termio.c_lflag |= ECHOKE | ECHOCTL;
+               termio.c_lflag |= ECHOKE | ECHOCTL;
 #endif
 #if defined(ECHOPRT) && defined(NOFLSH) && defined(TOSTOP)
-       termio.c_lflag &= ~(ECHOPRT | NOFLSH | TOSTOP);
+               termio.c_lflag &= ~(ECHOPRT | NOFLSH | TOSTOP);
 #endif
 #ifdef ONLCR
-       termio.c_oflag |= ONLCR;
+               termio.c_oflag |= ONLCR;
 #endif
 
-       /* leave these values unchanged if not specified in login.defs */
-       erasechar = getdef_num ("ERASECHAR", (int) termio.c_cc[VERASE]);
-       killchar = getdef_num ("KILLCHAR", (int) termio.c_cc[VKILL]);
-       termio.c_cc[VERASE] = (cc_t) erasechar;
-       termio.c_cc[VKILL] = (cc_t) killchar;
-       /* Make sure the values were valid.
-        * getdef_num cannot validate this.
-        */
-       if (erasechar != termio.c_cc[VERASE]) {
-               fprintf (stderr,
-                        _("configuration error - cannot parse %s value: '%d'"),
-                        "ERASECHAR", erasechar);
-               exit (1);
-       }
-       if (killchar != termio.c_cc[VKILL]) {
-               fprintf (stderr,
-                        _("configuration error - cannot parse %s value: '%d'"),
-                        "KILLCHAR", killchar);
-               exit (1);
-       }
+               /* leave these values unchanged if not specified in login.defs */
+               erasechar = getdef_num ("ERASECHAR", (int) termio.c_cc[VERASE]);
+               killchar = getdef_num ("KILLCHAR", (int) termio.c_cc[VKILL]);
+               termio.c_cc[VERASE] = (cc_t) erasechar;
+               termio.c_cc[VKILL] = (cc_t) killchar;
+               /* Make sure the values were valid.
+                * getdef_num cannot validate this.
+                */
+               if (erasechar != (int) termio.c_cc[VERASE]) {
+                       fprintf (stderr,
+                                _("configuration error - cannot parse %s value: '%d'"),
+                                "ERASECHAR", erasechar);
+                       exit (1);
+               }
+               if (killchar != (int) termio.c_cc[VKILL]) {
+                       fprintf (stderr,
+                                _("configuration error - cannot parse %s value: '%d'"),
+                                "KILLCHAR", killchar);
+                       exit (1);
+               }
 
-       /*
-        * ttymon invocation prefers this, but these settings won't come into
-        * effect after the first username login 
-        */
-       (void) STTY (0, &termio);
+               /*
+                * ttymon invocation prefers this, but these settings
+                * won't come into effect after the first username login 
+                */
+               (void) STTY (0, &termio);
        }
 }
 
@@ -240,7 +242,6 @@ static void check_nologin (bool login_to_root)
        fname = getdef_str ("NOLOGINS_FILE");
        if ((NULL != fname) && (access (fname, F_OK) == 0)) {
                FILE *nlfp;
-               int c;
 
                /*
                 * Cat the file if it can be opened, otherwise just
@@ -248,6 +249,7 @@ static void check_nologin (bool login_to_root)
                 */
                nlfp = fopen (fname, "r");
                if (NULL != nlfp) {
+                       int c;
                        while ((c = getc (nlfp)) != EOF) {
                                if (c == '\n') {
                                        (void) putchar ('\r');
@@ -347,6 +349,7 @@ static void process_flags (int argc, char *const *argv)
         *  Get the user name.
         */
        if (optind < argc) {
+               assert (NULL == username);
                username = xstrdup (argv[optind]);
                strzero (argv[optind]);
                ++optind;
@@ -453,7 +456,7 @@ static void get_pam_user (char **ptr_pam_user)
  * It is quite common to mistyped the password for username, and passwords
  * should not be logged.
  */
-static const char *get_failent_user (const char *user)
+static /*@observer@*/const char *get_failent_user (/*@returned@*/const char *user)
 {
        const char *failent_user = "UNKNOWN";
        bool log_unkfail_enab = getdef_bool("LOG_UNKFAIL_ENAB");
@@ -475,23 +478,23 @@ static const char *get_failent_user (const char *user)
  *     utent should be the utmp entry returned by get_current_utmp (or
  *     NULL).
  */
-static void update_utmp (const char *username,
+static void update_utmp (const char *user,
                          const char *tty,
-                         const char *hostname,
+                         const char *host,
                          /*@null@*/const struct utmp *utent)
 {
-       struct utmp  *ut  = prepare_utmp  (username, tty, hostname, utent);
-#ifdef HAVE_UTMPX_H
-       struct utmpx *utx = prepare_utmpx (username, tty, hostname, utent);
-#endif                         /* HAVE_UTMPX_H */
+       struct utmp  *ut  = prepare_utmp  (user, tty, host, utent);
+#ifdef USE_UTMPX
+       struct utmpx *utx = prepare_utmpx (user, tty, host, utent);
+#endif                         /* USE_UTMPX */
 
        (void) setutmp  (ut);   /* make entry in the utmp & wtmp files */
        free (ut);
 
-#ifdef HAVE_UTMPX_H
+#ifdef USE_UTMPX
        (void) setutmpx (utx);  /* make entry in the utmpx & wtmpx files */
        free (utx);
-#endif                         /* HAVE_UTMPX_H */
+#endif                         /* USE_UTMPX */
 }
 
 /*
@@ -524,19 +527,18 @@ int main (int argc, char **argv)
 #endif
        unsigned int delay;
        unsigned int retries;
-       bool failed;
        bool subroot = false;
 #ifndef USE_PAM
        bool is_console;
 #endif
        int err;
        const char *cp;
-       char *tmp;
+       const char *tmp;
        char fromhost[512];
        struct passwd *pwd = NULL;
        char **envp = environ;
        const char *failent_user;
-       struct utmp *utent;
+       /*@null@*/struct utmp *utent;
 
 #ifdef USE_PAM
        int retcode;
@@ -610,6 +612,7 @@ int main (int argc, char **argv)
        }
 #ifdef RLOGIN
        if (rflg) {
+               assert (NULL == username);
                username = xmalloc (USER_NAME_MAX_LENGTH + 1);
                username[USER_NAME_MAX_LENGTH] = '\0';
                if (do_rlogin (hostname, username, USER_NAME_MAX_LENGTH, term, sizeof term)) {
@@ -734,7 +737,7 @@ int main (int argc, char **argv)
 #endif
        /* if fflg, then the user has already been authenticated */
        if (!fflg) {
-               int failcount = 0;
+               unsigned int failcount = 0;
                char hostn[256];
                char loginprompt[256];  /* That's one hell of a prompt :) */
 
@@ -744,8 +747,8 @@ int main (int argc, char **argv)
                                  sizeof (loginprompt),
                                  _("%s login: "), hostn);
                } else {
-                       snprintf (loginprompt,
-                                 sizeof (loginprompt), _("login: "));
+                       strncpy (loginprompt, _("login: "),
+                                sizeof (loginprompt));
                }
 
                retcode = pam_set_item (pamh, PAM_USER_PROMPT, loginprompt);
@@ -769,7 +772,7 @@ int main (int argc, char **argv)
                 */
                failcount = 0;
                while (true) {
-                       failed = false;
+                       bool failed = false;
 
                        failcount++;
 #ifdef HAS_PAM_FAIL_DELAY
@@ -786,10 +789,10 @@ int main (int argc, char **argv)
 
                        if (retcode == PAM_MAXTRIES) {
                                SYSLOG ((LOG_NOTICE,
-                                        "TOO MANY LOGIN TRIES (%d)%s FOR '%s'",
+                                        "TOO MANY LOGIN TRIES (%u)%s FOR '%s'",
                                         failcount, fromhost, failent_user));
                                fprintf(stderr,
-                                       _("Maximum number of tries exceeded (%d)\n"),
+                                       _("Maximum number of tries exceeded (%u)\n"),
                                        failcount);
                                PAM_END;
                                exit(0);
@@ -800,7 +803,7 @@ int main (int argc, char **argv)
                                PAM_END;
                                exit(99);
                        } else if (retcode != PAM_SUCCESS) {
-                               SYSLOG ((LOG_NOTICE,"FAILED LOGIN (%d)%s FOR '%s', %s",
+                               SYSLOG ((LOG_NOTICE,"FAILED LOGIN (%u)%s FOR '%s', %s",
                                         failcount, fromhost, failent_user,
                                         pam_strerror (pamh, retcode)));
                                failed = true;
@@ -825,14 +828,15 @@ int main (int argc, char **argv)
                        close (audit_fd);
 #endif                         /* WITH_AUDIT */
 
-                       fprintf (stderr, "\nLogin incorrect\n");
+                       (void) puts ("");
+                       (void) puts (_("Login incorrect"));
 
                        if (failcount >= retries) {
                                SYSLOG ((LOG_NOTICE,
-                                        "TOO MANY LOGIN TRIES (%d)%s FOR '%s'",
+                                        "TOO MANY LOGIN TRIES (%u)%s FOR '%s'",
                                         failcount, fromhost, failent_user));
                                fprintf(stderr,
-                                       _("Maximum number of tries exceeded (%d)\n"),
+                                       _("Maximum number of tries exceeded (%u)\n"),
                                        failcount);
                                PAM_END;
                                exit(0);
@@ -898,6 +902,7 @@ int main (int argc, char **argv)
 
 #else                          /* ! USE_PAM */
        while (true) {  /* repeatedly get login/password pairs */
+               bool failed;
                /* user_passwd is always a pointer to this constant string
                 * or a passwd or shadow password that will be memzero by
                 * pw_free / spw_free.
@@ -908,6 +913,7 @@ int main (int argc, char **argv)
                 * anymore. */
                if (NULL != pwd) {
                        pw_free (pwd);
+                       pwd = NULL;
                }
                if (NULL != spwd) {
                        spw_free (spwd);
@@ -998,7 +1004,7 @@ int main (int argc, char **argv)
                        failed = true;
                }
                if (   !failed
-                   && !login_access (username, *hostname ? hostname : tty)) {
+                   && !login_access (username, ('\0' != *hostname) ? hostname : tty)) {
                        SYSLOG ((LOG_WARN, "LOGIN '%s' REFUSED %s",
                                 username, fromhost));
                        failed = true;
@@ -1020,19 +1026,19 @@ int main (int argc, char **argv)
                        failure (pwd->pw_uid, tty, &faillog);
                }
                if (getdef_str ("FTMP_FILE") != NULL) {
-#ifdef HAVE_UTMPX_H
+#ifdef USE_UTMPX
                        struct utmpx *failent =
                                prepare_utmpx (failent_user,
                                               tty,
                        /* FIXME: or fromhost? */hostname,
                                               utent);
-#else
+#else                          /* !USE_UTMPX */
                        struct utmp *failent =
                                prepare_utmp (failent_user,
                                              tty,
                                              hostname,
                                              utent);
-#endif
+#endif                         /* !USE_UTMPX */
                        failtmp (failent_user, failent);
                        free (failent);
                }
@@ -1079,6 +1085,8 @@ int main (int argc, char **argv)
                }
        }                       /* while (true) */
 #endif                         /* ! USE_PAM */
+       assert (NULL != username);
+       assert (NULL != pwd);
 
        (void) alarm (0);               /* turn off alarm clock */
 
@@ -1104,8 +1112,6 @@ int main (int argc, char **argv)
                addenv ("IFS= \t\n", NULL);     /* ... instead, set a safe IFS */
        }
 
-       update_utmp (username, tty, hostname, utent);
-
        if (pwd->pw_shell[0] == '*') {  /* subsystem root */
                pwd->pw_shell++;        /* skip the '*' */
                subsystem (pwd);        /* figure out what to execute */
@@ -1193,6 +1199,7 @@ int main (int argc, char **argv)
        }
        /* child */
 #endif
+
        /* If we were init, we need to start a new session */
        if (getppid() == 1) {
                setsid();
@@ -1201,6 +1208,11 @@ int main (int argc, char **argv)
                }
        }
 
+       /*
+        * The utmp entry needs to be updated to indicate the new status
+        * of the session, the new PID and SID.
+        */
+       update_utmp (username, tty, hostname, utent);
 
        /* The pwd and spwd entries for the user have been copied.
         *
@@ -1315,8 +1327,7 @@ int main (int argc, char **argv)
                /* exec the shell finally */
                err = shell (pwd->pw_shell, (char *) 0, newenvp);
        }
-       exit (err == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
-       /* NOT REACHED */
-       return 0;
+
+       return ((err == ENOENT) ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
 }