]> granicus.if.org Git - shadow/blobdiff - src/login.c
* src/su.c: Extract export of environment from main().
[shadow] / src / login.c
index aa9e55603e0cbce35f84d1209ea091dfa6c0abf3..c24fc860d7cfb4f75b790acefed37c4eeab9d93b 100644 (file)
@@ -1,5 +1,8 @@
 /*
- * Copyright 1989 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1989 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2001, Marek Michałkiewicz
+ * Copyright (c) 2001 - 2006, Tomasz Kłoczko
+ * Copyright (c) 2007 - 2010, Nicolas François
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
+ * 3. The name of the copyright holders or contributors may not be used to
+ *    endorse or promote products derived from this software without
+ *    specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include <config.h>
 
 #include <errno.h>
 #include <grp.h>
+#ifndef USE_PAM
 #include <lastlog.h>
-#ifdef UT_ADDR
-#include <netdb.h>
-#endif
+#endif                         /* !USE_PAM */
 #include <pwd.h>
 #include <signal.h>
 #include <stdio.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
+#include <assert.h>
 #include "defines.h"
 #include "faillog.h"
 #include "failure.h"
 #include "getdef.h"
 #include "prototypes.h"
 #include "pwauth.h"
+/*@-exitarg@*/
 #include "exitcodes.h"
+
 #ifdef USE_PAM
 #include "pam_defs.h"
 
@@ -57,54 +62,48 @@ static pam_handle_t *pamh = NULL;
 #define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \
        fprintf(stderr,"\n%s\n",pam_strerror(pamh, retcode)); \
        SYSLOG((LOG_ERR,"%s",pam_strerror(pamh, retcode))); \
-       pam_end(pamh, retcode); exit(1); \
+       (void) pam_end(pamh, retcode); \
+       exit(1); \
    }
 #define PAM_END { retcode = pam_close_session(pamh,0); \
-               pam_end(pamh,retcode); }
+               (void) pam_end(pamh,retcode); }
 
 #endif                         /* USE_PAM */
 
+#ifndef USE_PAM
 /*
  * Needed for MkLinux DR1/2/2.1 - J.
  */
 #ifndef LASTLOG_FILE
 #define LASTLOG_FILE "/var/log/lastlog"
 #endif
+#endif                         /* !USE_PAM */
 
 /*
  * Global variables
  */
-const char *hostname = "";
+const char *Prog;
 
-static struct passwd pwent;
+static const char *hostname = "";
+static /*@null@*/ /*@only@*/char *username = NULL;
+static int reason = PW_LOGIN;
 
-#if HAVE_UTMPX_H
-extern struct utmpx utxent;
-struct utmpx failent;
-#else
-struct utmp failent;
-#endif
-extern struct utmp utent;
-
-struct lastlog lastlog;
-static int pflg = 0;
-static int fflg = 0;
+#ifndef USE_PAM
+static struct lastlog ll;
+#endif                         /* !USE_PAM */
+static bool pflg = false;
+static bool fflg = false;
 
 #ifdef RLOGIN
-static int rflg = 0;
-#else
-#define rflg 0
-#endif
-static int hflg = 0;
-static int preauth_flag = 0;
-
-/*
- * Global variables.
- */
+static bool rflg = false;
+#else                          /* RLOGIN */
+#define rflg false
+#endif                         /* !RLOGIN */
+static bool hflg = false;
+static bool preauth_flag = false;
 
-static char *Prog;
-static int amroot;
-static int timeout;
+static bool amroot;
+static unsigned int timeout;
 
 /*
  * External identifiers.
@@ -125,13 +124,20 @@ extern char **environ;
 /* local function prototypes */
 static void usage (void);
 static void setup_tty (void);
-static void check_flags (int, char *const *);
+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 *host,
+                         /*@null@*/const struct utmp *utent);
 
 #ifndef USE_PAM
 static struct faillog faillog;
 
 static void bad_time_notify (void);
-static void check_nologin (void);
+static void check_nologin (bool login_to_root);
+#else
+static void get_pam_user (char **ptr_pam_user);
 #endif
 
 static void init_env (void);
@@ -148,12 +154,13 @@ static RETSIGTYPE alarm_handler (int);
 static void usage (void)
 {
        fprintf (stderr, _("Usage: %s [-p] [name]\n"), Prog);
-       if (!amroot)
+       if (!amroot) {
                exit (1);
+       }
        fprintf (stderr, _("       %s [-p] [-h host] [-f name]\n"), Prog);
 #ifdef RLOGIN
        fprintf (stderr, _("       %s [-p] -r host\n"), Prog);
-#endif
+#endif                         /* RLOGIN */
        exit (1);
 }
 
@@ -161,23 +168,53 @@ static void setup_tty (void)
 {
        TERMIO termio;
 
-       GTTY (0, &termio);      /* get terminal characteristics */
+       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;
 
-       /* leave these values unchanged if not specified in login.defs */
-       termio.c_cc[VERASE] = getdef_num ("ERASECHAR", termio.c_cc[VERASE]);
-       termio.c_cc[VKILL] = getdef_num ("KILLCHAR", termio.c_cc[VKILL]);
+#if defined(ECHOKE) && defined(ECHOCTL)
+               termio.c_lflag |= ECHOKE | ECHOCTL;
+#endif
+#if defined(ECHOPRT) && defined(NOFLSH) && defined(TOSTOP)
+               termio.c_lflag &= ~(ECHOPRT | NOFLSH | TOSTOP);
+#endif
+#ifdef ONLCR
+               termio.c_oflag |= ONLCR;
+#endif
 
-       /*
-        * ttymon invocation prefers this, but these settings won't come into
-        * effect after the first username login 
-        */
-       STTY (0, &termio);
+               /* 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);
+       }
 }
 
 
@@ -187,11 +224,11 @@ static void setup_tty (void)
  */
 static void bad_time_notify (void)
 {
-       puts (_("Invalid login time"));
-       fflush (stdout);
+       (void) puts (_("Invalid login time"));
+       (void) fflush (stdout);
 }
 
-static void check_nologin (void)
+static void check_nologin (bool login_to_root)
 {
        char *fname;
 
@@ -203,42 +240,46 @@ static void check_nologin (void)
         * forgotten about it ...
         */
        fname = getdef_str ("NOLOGINS_FILE");
-       if (fname != NULL && access (fname, F_OK) == 0) {
+       if ((NULL != fname) && (access (fname, F_OK) == 0)) {
                FILE *nlfp;
-               int c;
 
                /*
                 * Cat the file if it can be opened, otherwise just
                 * print a default message
                 */
-               if ((nlfp = fopen (fname, "r"))) {
+               nlfp = fopen (fname, "r");
+               if (NULL != nlfp) {
+                       int c;
                        while ((c = getc (nlfp)) != EOF) {
-                               if (c == '\n')
-                                       putchar ('\r');
+                               if (c == '\n') {
+                                       (void) putchar ('\r');
+                               }
 
-                               putchar (c);
+                               (void) putchar (c);
                        }
-                       fflush (stdout);
-                       fclose (nlfp);
-               } else
-                       puts (_("\nSystem closed for routine maintenance"));
+                       (void) fflush (stdout);
+                       (void) fclose (nlfp);
+               } else {
+                       (void) puts (_("\nSystem closed for routine maintenance"));
+               }
                /*
                 * Non-root users must exit. Root gets the message, but
                 * gets to login.
                 */
 
-               if (pwent.pw_uid != 0) {
+               if (!login_to_root) {
                        closelog ();
                        exit (0);
                }
-               puts (_("\n[Disconnect bypassed -- root login allowed.]"));
+               (void) puts (_("\n[Disconnect bypassed -- root login allowed.]"));
        }
 }
 #endif                         /* !USE_PAM */
 
-static void check_flags (int argc, char *const *argv)
+static void process_flags (int argc, char *const *argv)
 {
        int arg;
+       int flag;
 
        /*
         * Check the flags for proper form. Every argument starting with
@@ -246,9 +287,83 @@ static void check_flags (int argc, char *const *argv)
         * clever rlogin, telnet, and getty holes.
         */
        for (arg = 1; arg < argc; arg++) {
-               if (argv[arg][0] == '-' && strlen (argv[arg]) > 2)
+               if (argv[arg][0] == '-' && strlen (argv[arg]) > 2) {
+                       usage ();
+               }
+               if (strcmp(argv[arg], "--") == 0) {
+                       break; /* stop checking on a "--" */
+               }
+       }
+
+       /*
+        * Process options.
+        */
+       while ((flag = getopt (argc, argv, "d:fh:pr:")) != EOF) {
+               switch (flag) {
+               case 'd':
+                       /* "-d device" ignored for compatibility */
+                       break;
+               case 'f':
+                       fflg = true;
+                       break;
+               case 'h':
+                       hflg = true;
+                       hostname = optarg;
+                       reason = PW_TELNET;
+                       break;
+#ifdef RLOGIN
+               case 'r':
+                       rflg = true;
+                       hostname = optarg;
+                       reason = PW_RLOGIN;
+                       break;
+#endif                         /* RLOGIN */
+               case 'p':
+                       pflg = true;
+                       break;
+               default:
                        usage ();
+               }
+       }
+
+#ifdef RLOGIN
+       /*
+        * Neither -h nor -f should be combined with -r.
+        */
+
+       if (rflg && (hflg || fflg)) {
+               usage ();
+       }
+#endif                         /* RLOGIN */
+
+       /*
+        * Allow authentication bypass only if real UID is zero.
+        */
+
+       if ((rflg || fflg || hflg) && !amroot) {
+               fprintf (stderr, _("%s: Permission denied.\n"), Prog);
+               exit (1);
+       }
+
+       /*
+        *  Get the user name.
+        */
+       if (optind < argc) {
+               assert (NULL == username);
+               username = xstrdup (argv[optind]);
+               strzero (argv[optind]);
+               ++optind;
        }
+
+#ifdef RLOGIN
+       if (rflg && (NULL != username)) {
+               usage ();
+       }
+#endif                         /* RLOGIN */
+       if (fflg && (NULL == username)) {
+               usage ();
+       }
+
 }
 
 
@@ -259,7 +374,8 @@ static void init_env (void)
 #endif
        char *tmp;
 
-       if ((tmp = getenv ("LANG"))) {
+       tmp = getenv ("LANG");
+       if (NULL != tmp) {
                addenv ("LANG", tmp);
        }
 
@@ -267,33 +383,119 @@ static void init_env (void)
         * Add the timezone environmental variable so that time functions
         * work correctly.
         */
-       if ((tmp = getenv ("TZ"))) {
+       tmp = getenv ("TZ");
+       if (NULL != tmp) {
                addenv ("TZ", tmp);
        }
 #ifndef USE_PAM
-       else if ((cp = getdef_str ("ENV_TZ")))
-               addenv (*cp == '/' ? tz (cp) : cp, NULL);
+       else {
+               cp = getdef_str ("ENV_TZ");
+               if (NULL != cp) {
+                       addenv (('/' == *cp) ? tz (cp) : cp, NULL);
+               }
+       }
 #endif                         /* !USE_PAM */
        /* 
         * Add the clock frequency so that profiling commands work
         * correctly.
         */
-       if ((tmp = getenv ("HZ"))) {
+       tmp = getenv ("HZ");
+       if (NULL != tmp) {
                addenv ("HZ", tmp);
        }
 #ifndef USE_PAM
-       else if ((cp = getdef_str ("ENV_HZ")))
-               addenv (cp, NULL);
+       else {
+               cp = getdef_str ("ENV_HZ");
+               if (NULL != cp) {
+                       addenv (cp, NULL);
+               }
+       }
 #endif                         /* !USE_PAM */
 }
 
 
 static RETSIGTYPE alarm_handler (unused int sig)
 {
-       fprintf (stderr, _("\nLogin timed out after %d seconds.\n"), timeout);
+       fprintf (stderr, _("\nLogin timed out after %u seconds.\n"), timeout);
        exit (0);
 }
 
+#ifdef USE_PAM
+/*
+ * get_pam_user - Get the username according to PAM
+ *
+ * ptr_pam_user shall point to a malloc'ed string (or NULL).
+ */
+static void get_pam_user (char **ptr_pam_user)
+{
+       int retcode;
+       void *ptr_user;
+
+       assert (NULL != ptr_pam_user);
+
+       retcode = pam_get_item (pamh, PAM_USER, (const void **)&ptr_user);
+       PAM_FAIL_CHECK;
+
+       if (NULL != *ptr_pam_user) {
+               free (*ptr_pam_user);
+       }
+       if (NULL != ptr_user) {
+               *ptr_pam_user = xstrdup ((const char *)ptr_user);
+       } else {
+               *ptr_pam_user = NULL;
+       }
+}
+#endif
+
+/*
+ * get_failent_user - Return a string that can be used to log failure
+ *                    from an user.
+ *
+ * This will be either the user argument, or "UNKNOWN".
+ *
+ * It is quite common to mistyped the password for username, and passwords
+ * should not be logged.
+ */
+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");
+
+       if ((NULL != user) && ('\0' != user[0])) {
+               if (   log_unkfail_enab
+                   || (getpwnam (user) != NULL)) {
+                       failent_user = user;
+               }
+       }
+
+       return failent_user;
+}
+
+/*
+ * update_utmp - Update or create an utmp entry in utmp, wtmp, utmpw, and
+ *               wtmpx
+ *
+ *     utent should be the utmp entry returned by get_current_utmp (or
+ *     NULL).
+ */
+static void update_utmp (const char *user,
+                         const char *tty,
+                         const char *host,
+                         /*@null@*/const struct utmp *utent)
+{
+       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 USE_UTMPX
+       (void) setutmpx (utx);  /* make entry in the utmpx & wtmpx files */
+       free (utx);
+#endif                         /* USE_UTMPX */
+}
 
 /*
  * login - create a new login session for a user
@@ -314,38 +516,34 @@ static RETSIGTYPE alarm_handler (unused int sig)
  */
 int main (int argc, char **argv)
 {
-       char username[32];
+       const char *tmptty;
        char tty[BUFSIZ];
 
 #ifdef RLOGIN
        char term[128] = "";
-#endif
+#endif                         /* RLOGIN */
 #if defined(HAVE_STRFTIME) && !defined(USE_PAM)
        char ptime[80];
 #endif
-       int reason = PW_LOGIN;
-       int delay;
-       int retries;
-       int failed;
-       int flag;
-       int subroot = 0;
+       unsigned int delay;
+       unsigned int retries;
+       bool subroot = false;
 #ifndef USE_PAM
-       int is_console;
+       bool is_console;
 #endif
        int err;
        const char *cp;
-       char *tmp;
+       const char *tmp;
        char fromhost[512];
-       struct passwd *pwd;
+       struct passwd *pwd = NULL;
        char **envp = environ;
-       static char temp_pw[2];
-       static char temp_shell[] = "/bin/sh";
+       const char *failent_user;
+       /*@null@*/struct utmp *utent;
 
 #ifdef USE_PAM
        int retcode;
        pid_t child;
-       char *pam_user;
-       char **ptr_pam_user = &pam_user;
+       char *pam_user = NULL;
 #else
        struct spwd *spwd = NULL;
 #endif
@@ -355,79 +553,27 @@ int main (int argc, char **argv)
 
        sanitize_env ();
 
-       setlocale (LC_ALL, "");
-       bindtextdomain (PACKAGE, LOCALEDIR);
-       textdomain (PACKAGE);
+       (void) setlocale (LC_ALL, "");
+       (void) bindtextdomain (PACKAGE, LOCALEDIR);
+       (void) textdomain (PACKAGE);
 
        initenv ();
 
-       username[0] = '\0';
        amroot = (getuid () == 0);
        Prog = Basename (argv[0]);
 
-       check_flags (argc, argv);
-
-       while ((flag = getopt (argc, argv, "d:f::h:pr:")) != EOF) {
-               switch (flag) {
-               case 'd':
-                       /* "-d device" ignored for compatibility */
-                       break;
-               case 'f':
-                       /*
-                        * username must be a separate token
-                        * (-f root, *not* -froot).  --marekm
-                        *
-                        * if -f has an arg, use that, else use the
-                        * normal user name passed after all options
-                        * --benc
-                        */
-                       if (optarg != NULL && optarg != argv[optind - 1])
-                               usage ();
-                       fflg++;
-                       if (optarg)
-                               STRFCPY (username, optarg);
-                       break;
-               case 'h':
-                       hflg++;
-                       hostname = optarg;
-                       reason = PW_TELNET;
-                       break;
-#ifdef RLOGIN
-               case 'r':
-                       rflg++;
-                       hostname = optarg;
-                       reason = PW_RLOGIN;
-                       break;
-#endif
-               case 'p':
-                       pflg++;
-                       break;
-               default:
-                       usage ();
-               }
-       }
-
-#ifdef RLOGIN
-       /*
-        * Neither -h nor -f should be combined with -r.
-        */
-
-       if (rflg && (hflg || fflg))
-               usage ();
-#endif
-
-       /*
-        * Allow authentication bypass only if real UID is zero.
-        */
-
-       if ((rflg || fflg || hflg) && !amroot) {
-               fprintf (stderr, _("%s: Permission denied.\n"), Prog);
+       if (geteuid() != 0) {
+               fprintf (stderr, _("%s: Cannot possibly work without effective root\n"), Prog);
                exit (1);
        }
 
-       if (!isatty (0) || !isatty (1) || !isatty (2))
+       process_flags (argc, argv);
+
+       if ((isatty (0) == 0) || (isatty (1) == 0) || (isatty (2) == 0)) {
                exit (1);       /* must be a terminal */
+       }
 
+       utent = get_current_utmp ();
        /*
         * Be picky if run by normal users (possible if installed setuid
         * root), but not if run by root. This way it still allows logins
@@ -435,408 +581,414 @@ int main (int argc, char **argv)
         * but users must "exec login" which will use the existing utmp
         * entry (will not overwrite remote hostname).  --marekm
         */
-       checkutmp (!amroot);
-       STRFCPY (tty, utent.ut_line);
+       if (!amroot && (NULL == utent)) {
+               (void) puts (_("No utmp entry.  You must exec \"login\" from the lowest level \"sh\""));
+               exit (1);
+       }
+       /* NOTE: utent might be NULL afterwards */
+
+       tmptty = ttyname (0);
+       if (NULL == tmptty) {
+               tmptty = "UNKNOWN";
+       }
+       STRFCPY (tty, tmptty);
+
 #ifndef USE_PAM
        is_console = console (tty);
 #endif
 
        if (rflg || hflg) {
-#ifdef UT_ADDR
-               struct hostent *he;
-
-               /*
-                * Fill in the ut_addr field (remote login IP address). XXX
-                * - login from util-linux does it, but this is not the
-                * right place to do it. The program that starts login
-                * (telnetd, rlogind) knows the IP address, so it should
-                * create the utmp entry and fill in ut_addr. 
-                * gethostbyname() is not 100% reliable (the remote host may
-                * be unknown, etc.).  --marekm
-                */
-               if ((he = gethostbyname (hostname))) {
-                       utent.ut_addr = *((int32_t *) (he->h_addr_list[0]));
-#endif
-#ifdef UT_HOST
-                       strncpy (utent.ut_host, hostname,
-                                sizeof (utent.ut_host));
-#endif
-#if HAVE_UTMPX_H
-                       strncpy (utxent.ut_host, hostname,
-                                sizeof (utxent.ut_host));
-#endif
-                       /*
-                        * Add remote hostname to the environment. I think
-                        * (not sure) I saw it once on Irix.  --marekm
-                        */
-                       addenv ("REMOTEHOST", hostname);
-               }
-#ifdef __linux__
                /*
-                * workaround for init/getty leaving junk in ut_host at least in
-                * some version of RedHat.  --marekm
+                * Add remote hostname to the environment. I think
+                * (not sure) I saw it once on Irix.  --marekm
                 */
-               else if (amroot)
-                       memzero (utent.ut_host, sizeof utent.ut_host);
-#endif
-               if (fflg)
-                       preauth_flag++;
-               if (hflg)
-                       reason = PW_RLOGIN;
+               addenv ("REMOTEHOST", hostname);
+       }
+       if (fflg) {
+               preauth_flag = true;
+       }
+       if (hflg) {
+               reason = PW_RLOGIN;
+       }
 #ifdef RLOGIN
-               if (rflg
-                   && do_rlogin (hostname, username, sizeof username,
-                                 term, sizeof term))
-                       preauth_flag++;
-#endif
+       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)) {
+                       preauth_flag = true;
+               } else {
+                       free (username);
+                       username = NULL;
+               }
+       }
+#endif                         /* RLOGIN */
 
-               OPENLOG ("login");
+       OPENLOG ("login");
 
-               setup_tty ();
+       setup_tty ();
 
 #ifndef USE_PAM
-               umask (getdef_num ("UMASK", GETDEF_DEFAULT_UMASK));
-
-               {
-                       /* 
-                        * Use the ULIMIT in the login.defs file, and if
-                        * there isn't one, use the default value. The
-                        * user may have one for themselves, but otherwise,
-                        * just take what you get.
-                        */
-                       long limit = getdef_long ("ULIMIT", -1L);
+       (void) umask (getdef_num ("UMASK", GETDEF_DEFAULT_UMASK));
 
-                       if (limit != -1)
-                               set_filesize_limit (limit);
+       {
+               /* 
+                * Use the ULIMIT in the login.defs file, and if
+                * there isn't one, use the default value. The
+                * user may have one for themselves, but otherwise,
+                * just take what you get.
+                */
+               long limit = getdef_long ("ULIMIT", -1L);
+
+               if (limit != -1) {
+                       set_filesize_limit (limit);
                }
+       }
 
 #endif
-               /*
-                * The entire environment will be preserved if the -p flag
-                * is used.
-                */
-               if (pflg)
-                       while (*envp)   /* add inherited environment, */
-                               addenv (*envp++, NULL); /* some variables change later */
+       /*
+        * The entire environment will be preserved if the -p flag
+        * is used.
+        */
+       if (pflg) {
+               while (NULL != *envp) { /* add inherited environment, */
+                       addenv (*envp, NULL); /* some variables change later */
+                       envp++;
+               }
+       }
 
 #ifdef RLOGIN
-               if (term[0] != '\0')
-                       addenv ("TERM", term);
-               else
-#endif
-                       /* preserve TERM from getty */
-               if (!pflg && (tmp = getenv ("TERM")))
-                       addenv ("TERM", tmp);
+       if (term[0] != '\0') {
+               addenv ("TERM", term);
+       } else
+#endif                         /* RLOGIN */
+       {
+               /* preserve TERM from getty */
+               if (!pflg) {
+                       tmp = getenv ("TERM");
+                       if (NULL != tmp) {
+                               addenv ("TERM", tmp);
+                       }
+               }
+       }
 
-               init_env ();
+       init_env ();
 
-               if (optind < argc) {    /* get the user name */
-                       if (rflg || (fflg && username[0]))
-                               usage ();
+       if (optind < argc) {    /* now set command line variables */
+               set_env (argc - optind, &argv[optind]);
+       }
 
-                       STRFCPY (username, argv[optind]);
-                       strzero (argv[optind]);
-                       ++optind;
-               }
-               if (optind < argc)      /* now set command line variables */
-                       set_env (argc - optind, &argv[optind]);
-
-               if (rflg || hflg)
-                       cp = hostname;
-               else
-#ifdef UT_HOST
-               if (utent.ut_host[0])
-                       cp = utent.ut_host;
-               else
-#endif
-#if HAVE_UTMPX_H
-               if (utxent.ut_host[0])
-                       cp = utxent.ut_host;
-               else
-#endif
-                       cp = "";
-
-               if (*cp)
-                       snprintf (fromhost, sizeof fromhost,
-                                 " on '%.100s' from '%.200s'", tty, cp);
-               else
-                       snprintf (fromhost, sizeof fromhost,
-                                 " on '%.100s'", tty);
-
-             top:
-               /* only allow ALARM sec. for login */
-               signal (SIGALRM, alarm_handler);
-               timeout = getdef_num ("LOGIN_TIMEOUT", ALARM);
-               if (timeout > 0)
-                       alarm (timeout);
-
-               environ = newenvp;      /* make new environment active */
-               delay = getdef_num ("FAIL_DELAY", 1);
-               retries = getdef_num ("LOGIN_RETRIES", RETRIES);
+       if (rflg || hflg) {
+               cp = hostname;
+#ifdef HAVE_STRUCT_UTMP_UT_HOST
+       } else if ((NULL != utent) && ('\0' != utent->ut_host[0])) {
+               cp = utent->ut_host;
+#endif                         /* HAVE_STRUCT_UTMP_UT_HOST */
+       } else {
+               cp = "";
+       }
+
+       if ('\0' != *cp) {
+               snprintf (fromhost, sizeof fromhost,
+                         " on '%.100s' from '%.200s'", tty, cp);
+       } else {
+               snprintf (fromhost, sizeof fromhost,
+                         " on '%.100s'", tty);
+       }
+
+      top:
+       /* only allow ALARM sec. for login */
+       (void) signal (SIGALRM, alarm_handler);
+       timeout = getdef_unum ("LOGIN_TIMEOUT", ALARM);
+       if (timeout > 0) {
+               (void) alarm (timeout);
+       }
+
+       environ = newenvp;      /* make new environment active */
+       delay   = getdef_unum ("FAIL_DELAY", 1);
+       retries = getdef_unum ("LOGIN_RETRIES", RETRIES);
 
 #ifdef USE_PAM
-               retcode = pam_start ("login", username, &conv, &pamh);
-               if (retcode != PAM_SUCCESS) {
-                       fprintf (stderr,
-                                _("login: PAM Failure, aborting: %s\n"),
-                                pam_strerror (pamh, retcode));
-                       SYSLOG ((LOG_ERR, "Couldn't initialize PAM: %s",
-                                pam_strerror (pamh, retcode)));
-                       exit (99);
+       retcode = pam_start ("login", username, &conv, &pamh);
+       if (retcode != PAM_SUCCESS) {
+               fprintf (stderr,
+                        _("login: PAM Failure, aborting: %s\n"),
+                        pam_strerror (pamh, retcode));
+               SYSLOG ((LOG_ERR, "Couldn't initialize PAM: %s",
+                        pam_strerror (pamh, retcode)));
+               exit (99);
+       }
+
+       /*
+        * hostname & tty are either set to NULL or their correct values,
+        * depending on how much we know. We also set PAM's fail delay to
+        * ours.
+        *
+        * PAM_RHOST and PAM_TTY are used for authentication, only use
+        * information coming from login or from the caller (e.g. no utmp)
+        */
+       retcode = pam_set_item (pamh, PAM_RHOST, hostname);
+       PAM_FAIL_CHECK;
+       retcode = pam_set_item (pamh, PAM_TTY, tty);
+       PAM_FAIL_CHECK;
+#ifdef HAS_PAM_FAIL_DELAY
+       retcode = pam_fail_delay (pamh, 1000000 * delay);
+       PAM_FAIL_CHECK;
+#endif
+       /* if fflg, then the user has already been authenticated */
+       if (!fflg) {
+               unsigned int failcount = 0;
+               char hostn[256];
+               char loginprompt[256];  /* That's one hell of a prompt :) */
+
+               /* Make the login prompt look like we want it */
+               if (gethostname (hostn, sizeof (hostn)) == 0) {
+                       snprintf (loginprompt,
+                                 sizeof (loginprompt),
+                                 _("%s login: "), hostn);
+               } else {
+                       strncpy (loginprompt, _("login: "),
+                                sizeof (loginprompt));
+               }
+
+               retcode = pam_set_item (pamh, PAM_USER_PROMPT, loginprompt);
+               PAM_FAIL_CHECK;
+
+               /* if we didn't get a user on the command line,
+                  set it to NULL */
+               get_pam_user (&pam_user);
+               if ((NULL != pam_user) && ('\0' == pam_user[0])) {
+                       retcode = pam_set_item (pamh, PAM_USER, NULL);
+                       PAM_FAIL_CHECK;
                }
 
                /*
-                * hostname & tty are either set to NULL or their correct values,
-                * depending on how much we know. We also set PAM's fail delay to
-                * ours.
+                * There may be better ways to deal with some of
+                * these conditions, but at least this way I don't
+                * think we'll be giving away information. Perhaps
+                * someday we can trust that all PAM modules will
+                * pay attention to failure count and get rid of
+                * MAX_LOGIN_TRIES?
                 */
-               retcode = pam_set_item (pamh, PAM_RHOST, hostname);
-               PAM_FAIL_CHECK;
-               retcode = pam_set_item (pamh, PAM_TTY, tty);
-               PAM_FAIL_CHECK;
-#ifdef HAVE_PAM_FAIL_DELAY
-               retcode = pam_fail_delay (pamh, 1000000 * delay);
-               PAM_FAIL_CHECK;
+               failcount = 0;
+               while (true) {
+                       bool failed = false;
+
+                       failcount++;
+#ifdef HAS_PAM_FAIL_DELAY
+                       if (delay > 0) {
+                               retcode = pam_fail_delay(pamh, 1000000*delay);
+                               PAM_FAIL_CHECK;
+                       }
 #endif
-               /* if fflg == 1, then the user has already been authenticated */
-               if (!fflg || (getuid () != 0)) {
-                       int failcount = 0;
-                       char hostn[256];
-                       char loginprompt[256];  /* That's one hell of a prompt :) */
-
-                       /* Make the login prompt look like we want it */
-                       if (!gethostname (hostn, sizeof (hostn)))
-                               snprintf (loginprompt,
-                                         sizeof (loginprompt),
-                                         _("%s login: "), hostn);
-                       else
-                               snprintf (loginprompt,
-                                         sizeof (loginprompt), _("login: "));
-
-                       retcode =
-                           pam_set_item (pamh, PAM_USER_PROMPT, loginprompt);
-                       PAM_FAIL_CHECK;
 
-                       /* if we didn't get a user on the command line,
-                          set it to NULL */
-                       pam_get_item (pamh, PAM_USER,
-                                     (const void **)ptr_pam_user);
-                       if (pam_user[0] == '\0')
-                               pam_set_item (pamh, PAM_USER, NULL);
+                       retcode = pam_authenticate (pamh, 0);
+
+                       get_pam_user (&pam_user);
+                       failent_user = get_failent_user (pam_user);
+
+                       if (retcode == PAM_MAXTRIES) {
+                               SYSLOG ((LOG_NOTICE,
+                                        "TOO MANY LOGIN TRIES (%u)%s FOR '%s'",
+                                        failcount, fromhost, failent_user));
+                               fprintf(stderr,
+                                       _("Maximum number of tries exceeded (%u)\n"),
+                                       failcount);
+                               PAM_END;
+                               exit(0);
+                       } else if (retcode == PAM_ABORT) {
+                               /* Serious problems, quit now */
+                               (void) fputs (_("login: abort requested by PAM\n"), stderr);
+                               SYSLOG ((LOG_ERR,"PAM_ABORT returned from pam_authenticate()"));
+                               PAM_END;
+                               exit(99);
+                       } else if (retcode != PAM_SUCCESS) {
+                               SYSLOG ((LOG_NOTICE,"FAILED LOGIN (%u)%s FOR '%s', %s",
+                                        failcount, fromhost, failent_user,
+                                        pam_strerror (pamh, retcode)));
+                               failed = true;
+                       }
 
-                       /*
-                        * There may be better ways to deal with some of
-                        * these conditions, but at least this way I don't
-                        * think we'll be giving away information. Perhaps
-                        * someday we can trust that all PAM modules will
-                        * pay attention to failure count and get rid of
-                        * MAX_LOGIN_TRIES?
-                        */
-                       failcount = 0;
-                       while (1) {
-                         const char *failent_user;
-                         failed = 0;
-
-                         failcount++;
-                         if (delay > 0)
-                           retcode = pam_fail_delay(pamh, 1000000*delay);
-
-                         retcode = pam_authenticate (pamh, 0);
-
-                         pam_get_item (pamh, PAM_USER,
-                                       (const void **) ptr_pam_user);
-
-                         if (pam_user && pam_user[0]) {
-                           pwd = xgetpwnam(pam_user);
-                           if (pwd) {
-                             pwent = *pwd;
-                             failent_user = pwent.pw_name;
-                           } else {
-                             if (getdef_bool("LOG_UNKFAIL_ENAB") && pam_user)
-                               failent_user = pam_user;
-                             else
-                               failent_user = "UNKNOWN";
-                           }
-                         } else {
-                           pwd = NULL;
-                           failent_user = "UNKNOWN";
-                         }
-
-                         if (retcode == PAM_MAXTRIES || failcount >= retries) {
-                           SYSLOG ((LOG_NOTICE,
-                                   "TOO MANY LOGIN TRIES (%d)%s FOR `%s'",
-                                   failcount, fromhost, failent_user));
-                           fprintf(stderr,
-                                   _("Maximum number of tries exceeded (%d)\n"),
-                                   failcount);
-                           PAM_END;
-                           exit(0);
-                         } else if (retcode == PAM_ABORT) {
-                           /* Serious problems, quit now */
-                           fputs (_("login: abort requested by PAM\n"),stderr);
-                           SYSLOG ((LOG_ERR,"PAM_ABORT returned from pam_authenticate()"));
-                           PAM_END;
-                           exit(99);
-                         } else if (retcode != PAM_SUCCESS) {
-                           SYSLOG ((LOG_NOTICE,"FAILED LOGIN (%d)%s FOR `%s', %s",
-                                  failcount, fromhost, failent_user,
-                                  pam_strerror (pamh, retcode)));
-                           failed = 1;
-                         }
-
-                         if (!failed)
-                           break;
+                       if (!failed) {
+                               break;
+                       }
 
 #ifdef WITH_AUDIT
-                               {
-                                       struct passwd *pw;
-                                       char buf[64];
-
-                                       audit_fd = audit_open ();
-                                       /* local, no need for xgetpwnam */
-                                       pw = getpwnam (username);
-                                       if (pw) {
-                                               snprintf (buf, sizeof (buf),
-                                                 "uid=%d", pw->pw_uid);
-                                               audit_log_user_message
-                                                   (audit_fd, AUDIT_USER_LOGIN,
-                                                    buf, hostname, NULL,
-                                                    tty, 0);
-                                       } else {
-                                               snprintf (buf, sizeof (buf),
-                                                         "acct=%s", username);
-                                               audit_log_user_message
-                                                   (audit_fd, AUDIT_USER_LOGIN,
-                                                    buf, hostname, NULL,
-                                                    tty, 0);
-                                       }
-                                       close (audit_fd);
-                               }
+                       audit_fd = audit_open ();
+                       audit_log_acct_message (audit_fd,
+                                               AUDIT_USER_LOGIN,
+                                               NULL,    /* Prog. name */
+                                               "login",
+                                               failent_user,
+                                               AUDIT_NO_ID,
+                                               hostname,
+                                               NULL,    /* addr */
+                                               tty,
+                                               0);      /* result */
+                       close (audit_fd);
 #endif                         /* WITH_AUDIT */
 
-                         fprintf(stderr,"\nLogin incorrect\n");
-
-                         /* Let's give it another go around */
-                         pam_set_item(pamh,PAM_USER,NULL);
-                       }
-
-                       /* We don't get here unless they were authenticated above */
-                       alarm (0);
-                       retcode = pam_acct_mgmt (pamh, 0);
-
-                       if (retcode == PAM_NEW_AUTHTOK_REQD) {
-                               retcode =
-                                   pam_chauthtok (pamh,
-                                                  PAM_CHANGE_EXPIRED_AUTHTOK);
+                       (void) puts ("");
+                       (void) puts (_("Login incorrect"));
+
+                       if (failcount >= retries) {
+                               SYSLOG ((LOG_NOTICE,
+                                        "TOO MANY LOGIN TRIES (%u)%s FOR '%s'",
+                                        failcount, fromhost, failent_user));
+                               fprintf(stderr,
+                                       _("Maximum number of tries exceeded (%u)\n"),
+                                       failcount);
+                               PAM_END;
+                               exit(0);
                        }
 
+                       /*
+                        * Let's give it another go around.
+                        * Even if a username was given on the command
+                        * line, prompt again for the username.
+                        */
+                       retcode = pam_set_item (pamh, PAM_USER, NULL);
                        PAM_FAIL_CHECK;
                }
 
-               /* Grab the user information out of the password file for future usage
-                  First get the username that we are actually using, though.
-                */
-               retcode =
-                   pam_get_item (pamh, PAM_USER, (const void **)ptr_pam_user);
-               setpwent ();
-               pwd = xgetpwnam (pam_user);
-               if (!pwd) {
-                       SYSLOG ((LOG_ERR, "xgetpwnam(%s) failed",
-                                getdef_bool ("LOG_UNKFAIL_ENAB") ?
-                                pam_user : "UNKNOWN"));
-                       exit (1);
-               }
-
-               if (fflg) {
-                       retcode = pam_acct_mgmt (pamh, 0);
-                       PAM_FAIL_CHECK;
-               }
+               /* We don't get here unless they were authenticated above */
+               (void) alarm (0);
+       }
 
-               if (setup_groups (pwd))
-                       exit (1);
+       /* Check the account validity */
+       retcode = pam_acct_mgmt (pamh, 0);
+       if (retcode == PAM_NEW_AUTHTOK_REQD) {
+               retcode = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
+       }
+       PAM_FAIL_CHECK;
+
+       /* Open the PAM session */
+       get_pam_user (&pam_user);
+       retcode = pam_open_session (pamh, hushed (pam_user) ? PAM_SILENT : 0);
+       PAM_FAIL_CHECK;
+
+       /* Grab the user information out of the password file for future usage
+        * First get the username that we are actually using, though.
+        *
+        * From now on, we will discard changes of the user (PAM_USER) by
+        * PAM APIs.
+        */
+       get_pam_user (&pam_user);
+       if (NULL != username) {
+               free (username);
+       }
+       username = pam_user;
+       failent_user = get_failent_user (username);
 
-               pwent = *pwd;
+       pwd = xgetpwnam (username);
+       if (NULL == pwd) {
+               SYSLOG ((LOG_ERR, "cannot find user %s", failent_user));
+               exit (1);
+       }
 
-               retcode = pam_setcred (pamh, PAM_ESTABLISH_CRED);
-               PAM_FAIL_CHECK;
+       /* This set up the process credential (group) and initialize the
+        * supplementary group access list.
+        * This has to be done before pam_setcred
+        */
+       if (setup_groups (pwd) != 0) {
+               exit (1);
+       }
 
-               retcode = pam_open_session (pamh,
-                                           hushed (&pwent) ? PAM_SILENT : 0);
-               PAM_FAIL_CHECK;
+       retcode = pam_setcred (pamh, PAM_ESTABLISH_CRED);
+       PAM_FAIL_CHECK;
+       /* NOTE: If pam_setcred changes PAM_USER, this will not be taken
+        * into account.
+        */
 
 #else                          /* ! USE_PAM */
-               while (1) {     /* repeatedly get login/password pairs */
-                       failed = 0;     /* haven't failed authentication yet */
-                       if (!username[0]) {     /* need to get a login id */
-                               if (subroot) {
-                                       closelog ();
-                                       exit (1);
-                               }
-                               preauth_flag = 0;
-                               login_prompt (_("\n%s login: "), username,
-                                             sizeof username);
+       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.
+                * Do not free() user_passwd. */
+               const char *user_passwd = "!";
+
+               /* Do some cleanup to avoid keeping entries we do not need
+                * anymore. */
+               if (NULL != pwd) {
+                       pw_free (pwd);
+                       pwd = NULL;
+               }
+               if (NULL != spwd) {
+                       spw_free (spwd);
+                       spwd = NULL;
+               }
+
+               failed = false; /* haven't failed authentication yet */
+               if (NULL == username) { /* need to get a login id */
+                       if (subroot) {
+                               closelog ();
+                               exit (1);
+                       }
+                       preauth_flag = false;
+                       username = xmalloc (USER_NAME_MAX_LENGTH + 1);
+                       username[USER_NAME_MAX_LENGTH] = '\0';
+                       login_prompt (_("\n%s login: "), username, USER_NAME_MAX_LENGTH);
+
+                       if ('\0' == username[0]) {
+                               /* Prompt for a new login */
+                               free (username);
+                               username = NULL;
                                continue;
                        }
-#endif                         /* ! USE_PAM */
-
-#ifdef USE_PAM
-               if (!(pwd = xgetpwnam (pam_user))) {
-                       pwent.pw_name = pam_user;
-#else
-               if (!(pwd = xgetpwnam (username))) {
-                       pwent.pw_name = username;
-#endif
-                       strcpy (temp_pw, "!");
-                       pwent.pw_passwd = temp_pw;
-                       pwent.pw_shell = temp_shell;
+               }
+               /* Get the username to be used to log failures */
+               failent_user = get_failent_user (username);
 
-                       preauth_flag = 0;
-                       failed = 1;
+               pwd = xgetpwnam (username);
+               if (NULL == pwd) {
+                       preauth_flag = false;
+                       failed = true;
                } else {
-                       pwent = *pwd;
+                       user_passwd = pwd->pw_passwd;
+                       /*
+                        * If the encrypted password begins with a "!",
+                        * the account is locked and the user cannot
+                        * login, even if they have been
+                        * "pre-authenticated."
+                        */
+                       if (   ('!' == user_passwd[0])
+                           || ('*' == user_passwd[0])) {
+                               failed = true;
+                       }
                }
-#ifndef USE_PAM
-               spwd = NULL;
-               if (pwd && strcmp (pwd->pw_passwd, SHADOW_PASSWD_STRING) == 0) {
-                       /* !USE_PAM, no need for xgetspnam */
-                       spwd = getspnam (username);
-                       if (spwd)
-                               pwent.pw_passwd = spwd->sp_pwdp;
-                       else
+
+               if (strcmp (user_passwd, SHADOW_PASSWD_STRING) == 0) {
+                       spwd = xgetspnam (username);
+                       if (NULL != spwd) {
+                               user_passwd = spwd->sp_pwdp;
+                       } else {
+                               /* The user exists in passwd, but not in
+                                * shadow. SHADOW_PASSWD_STRING indicates
+                                * that the password shall be in shadow.
+                                */
                                SYSLOG ((LOG_WARN,
-                                        "no shadow password for `%s'%s",
-                                        username, fromhost));
+                                        "no shadow password for '%s'%s",
+                                        username, fromhost));
+                       }
                }
 
-               /*
-                * If the encrypted password begins with a "!", the account
-                * is locked and the user cannot login, even if they have
-                * been "pre-authenticated."
-                */
-               if (pwent.pw_passwd[0] == '!' || pwent.pw_passwd[0] == '*')
-                       failed = 1;
-
                /*
                 * The -r and -f flags provide a name which has already
                 * been authenticated by some server.
                 */
-               if (preauth_flag)
+               if (preauth_flag) {
                        goto auth_ok;
+               }
 
-               if (pw_auth
-                   (pwent.pw_passwd, username, reason, (char *) 0) == 0)
+               if (pw_auth (user_passwd, username, reason, (char *) 0) == 0) {
                        goto auth_ok;
+               }
 
-               /*
-                * Don't log unknown usernames - I mistyped the password for
-                * username at least once. Should probably use LOG_AUTHPRIV
-                * for those who really want to log them.  --marekm
-                */
-               SYSLOG ((LOG_WARN, "invalid password for `%s' %s",
-                        (pwd
-                         || getdef_bool ("LOG_UNKFAIL_ENAB")) ?
-                        username : "UNKNOWN", fromhost));
-               failed = 1;
+               SYSLOG ((LOG_WARN, "invalid password for '%s' %s",
+                        failent_user, fromhost));
+               failed = true;
 
              auth_ok:
                /*
@@ -844,67 +996,59 @@ int main (int argc, char **argv)
                 * If you reach this far, your password has been
                 * authenticated and so on.
                 */
-               if (!failed && pwent.pw_name && pwent.pw_uid == 0
+               if (   !failed
+                   && (NULL != pwd)
+                   && (0 == pwd->pw_uid)
                    && !is_console) {
                        SYSLOG ((LOG_CRIT, "ILLEGAL ROOT LOGIN %s", fromhost));
-                       failed = 1;
+                       failed = true;
                }
-               if (!failed
-                   && !login_access (username, *hostname ? hostname : tty)) {
-                       SYSLOG ((LOG_WARN, "LOGIN `%s' REFUSED %s",
-                                username, fromhost));
-                       failed = 1;
+               if (   !failed
+                   && !login_access (username, ('\0' != *hostname) ? hostname : tty)) {
+                       SYSLOG ((LOG_WARN, "LOGIN '%s' REFUSED %s",
+                                username, fromhost));
+                       failed = true;
                }
-               if (pwd && getdef_bool ("FAILLOG_ENAB") &&
-                   !failcheck (pwent.pw_uid, &faillog, failed)) {
+               if (   (NULL != pwd)
+                   && getdef_bool ("FAILLOG_ENAB")
+                   && !failcheck (pwd->pw_uid, &faillog, failed)) {
                        SYSLOG ((LOG_CRIT,
-                                "exceeded failure limit for `%s' %s",
-                                username, fromhost));
-                       failed = 1;
+                                "exceeded failure limit for '%s' %s",
+                                username, fromhost));
+                       failed = true;
                }
-               if (!failed)
+               if (!failed) {
                        break;
+               }
 
                /* don't log non-existent users */
-               if (pwd && getdef_bool ("FAILLOG_ENAB"))
-                       failure (pwent.pw_uid, tty, &faillog);
+               if ((NULL != pwd) && getdef_bool ("FAILLOG_ENAB")) {
+                       failure (pwd->pw_uid, tty, &faillog);
+               }
                if (getdef_str ("FTMP_FILE") != NULL) {
-                       const char *failent_user;
-
-#if HAVE_UTMPX_H
-                       failent = utxent;
-                       if (sizeof (failent.ut_tv) == sizeof (struct timeval))
-                               gettimeofday ((struct timeval *)
-                                             &failent.ut_tv, NULL);
-                       else {
-                               struct timeval tv;
-
-                               gettimeofday (&tv, NULL);
-                               failent.ut_tv.tv_sec = tv.tv_sec;
-                               failent.ut_tv.tv_usec = tv.tv_usec;
-                       }
-#else
-                       failent = utent;
-                       failent.ut_time = time (NULL);
-#endif
-                       if (pwd) {
-                               failent_user = pwent.pw_name;
-                       } else {
-                               if (getdef_bool ("LOG_UNKFAIL_ENAB"))
-                                       failent_user = username;
-                               else
-                                       failent_user = "UNKNOWN";
-                       }
-                       strncpy (failent.ut_user, failent_user,
-                                sizeof (failent.ut_user));
-                       failent.ut_type = USER_PROCESS;
-                       failtmp (&failent);
+#ifdef USE_UTMPX
+                       struct utmpx *failent =
+                               prepare_utmpx (failent_user,
+                                              tty,
+                       /* FIXME: or fromhost? */hostname,
+                                              utent);
+#else                          /* !USE_UTMPX */
+                       struct utmp *failent =
+                               prepare_utmp (failent_user,
+                                             tty,
+                                             hostname,
+                                             utent);
+#endif                         /* !USE_UTMPX */
+                       failtmp (failent_user, failent);
+                       free (failent);
                }
-               memzero (username, sizeof username);
 
-               if (--retries <= 0)
+               retries--;
+               if (retries <= 0) {
                        SYSLOG ((LOG_CRIT, "REPEATED login failures%s",
-                                fromhost));
+                                fromhost));
+               }
+
                /*
                 * If this was a passwordless account and we get here, login
                 * was denied (securetty, faillog, etc.). There was no
@@ -912,57 +1056,66 @@ int main (int argc, char **argv)
                 * guys won't see that the passwordless account exists at
                 * all).  --marekm
                 */
-               if (pwent.pw_passwd[0] == '\0')
+               if (user_passwd[0] == '\0') {
                        pw_auth ("!", username, reason, (char *) 0);
+               }
+
+               /*
+                * Authentication of this user failed.
+                * The username must be confirmed in the next try.
+                */
+               free (username);
+               username = NULL;
 
                /*
                 * Wait a while (a la SVR4 /usr/bin/login) before attempting
                 * to login the user again. If the earlier alarm occurs
                 * before the sleep() below completes, login will exit.
                 */
-               if (delay > 0)
-                       sleep (delay);
+               if (delay > 0) {
+                       (void) sleep (delay);
+               }
 
-               puts (_("Login incorrect"));
+               (void) puts (_("Login incorrect"));
 
                /* allow only one attempt with -r or -f */
-               if (rflg || fflg || retries <= 0) {
+               if (rflg || fflg || (retries <= 0)) {
                        closelog ();
                        exit (1);
                }
-       }                       /* while (1) */
+       }                       /* while (true) */
 #endif                         /* ! USE_PAM */
-       alarm (0);              /* turn off alarm clock */
+       assert (NULL != username);
+       assert (NULL != pwd);
+
+       (void) alarm (0);               /* turn off alarm clock */
+
 #ifndef USE_PAM                        /* PAM does this */
        /*
         * porttime checks moved here, after the user has been
         * authenticated. now prints a message, as suggested
         * by Ivan Nejgebauer <ian@unsux.ns.ac.yu>.  --marekm
         */
-       if (getdef_bool ("PORTTIME_CHECKS_ENAB") &&
-           !isttytime (pwent.pw_name, tty, time ((time_t *) 0))) {
-               SYSLOG ((LOG_WARN, "invalid login time for `%s'%s",
-                        username, fromhost));
+       if (   getdef_bool ("PORTTIME_CHECKS_ENAB")
+           && !isttytime (username, tty, time ((time_t *) 0))) {
+               SYSLOG ((LOG_WARN, "invalid login time for '%s'%s",
+                        username, fromhost));
                closelog ();
                bad_time_notify ();
                exit (1);
        }
 
-       check_nologin ();
+       check_nologin (pwd->pw_uid == 0);
 #endif
 
-       if (getenv ("IFS"))     /* don't export user IFS ... */
+       if (getenv ("IFS")) {   /* don't export user IFS ... */
                addenv ("IFS= \t\n", NULL);     /* ... instead, set a safe IFS */
+       }
 
-#ifdef USE_PAM
-       setutmp (pam_user, tty, hostname);      /* make entry in utmp & wtmp files */
-#else
-       setutmp (username, tty, hostname);      /* make entry in utmp & wtmp files */
-#endif
-       if (pwent.pw_shell[0] == '*') { /* subsystem root */
-               pwent.pw_shell++;       /* skip the '*' */
-               subsystem (&pwent);     /* figure out what to execute */
-               subroot++;      /* say i was here again */
+       if (pwd->pw_shell[0] == '*') {  /* subsystem root */
+               pwd->pw_shell++;        /* skip the '*' */
+               subsystem (pwd);        /* figure out what to execute */
+               subroot = true; /* say I was here again */
                endpwent ();    /* close all of the file which were */
                endgrent ();    /* open in the original rooted file */
                endspent ();    /* system. they will be re-opened */
@@ -973,57 +1126,69 @@ int main (int argc, char **argv)
        }
 
 #ifdef WITH_AUDIT
-       {
-               char buf[32];
-
-               audit_fd = audit_open ();
-               snprintf (buf, sizeof (buf), "uid=%d", pwd->pw_uid);
-               audit_log_user_message (audit_fd, AUDIT_USER_LOGIN,
-                                       buf, hostname, NULL, tty, 1);
-               close (audit_fd);
-       }
+       audit_fd = audit_open ();
+       audit_log_acct_message (audit_fd,
+                               AUDIT_USER_LOGIN,
+                               NULL,    /* Prog. name */
+                               "login",
+                               username,
+                               AUDIT_NO_ID,
+                               hostname,
+                               NULL,    /* addr */
+                               tty,
+                               1);      /* result */
+       close (audit_fd);
 #endif                         /* WITH_AUDIT */
 
 #ifndef USE_PAM                        /* pam_lastlog handles this */
-       if (getdef_bool ("LASTLOG_ENAB"))       /* give last login and log this one */
-               dolastlog (&lastlog, &pwent, utent.ut_line, hostname);
+       if (getdef_bool ("LASTLOG_ENAB")) {     /* give last login and log this one */
+               dolastlog (&ll, pwd, tty, hostname);
+       }
 #endif
 
 #ifndef USE_PAM                        /* PAM handles this as well */
        /*
         * Have to do this while we still have root privileges, otherwise we
-        * don't have access to /etc/shadow. expire() closes password files,
-        * and changes to the user in the child before executing the passwd
-        * program.  --marekm
+        * don't have access to /etc/shadow.
         */
-       if (spwd) {             /* check for age of password */
-               if (expire (&pwent, spwd)) {
-                       /* !USE_PAM, no need for xgetpwnam */
-                       pwd = getpwnam (username);
-                       /* !USE_PAM, no need for xgetspnam */
-                       spwd = getspnam (username);
-                       if (pwd)
-                               pwent = *pwd;
+       if (NULL != spwd) {             /* check for age of password */
+               if (expire (pwd, spwd)) {
+                       /* The user updated her password, get the new
+                        * entries.
+                        * Use the x variants because we need to keep the
+                        * entry for a long time, and there might be other
+                        * getxxyy in between.
+                        */
+                       pw_free (pwd);
+                       pwd = xgetpwnam (username);
+                       if (NULL == pwd) {
+                               SYSLOG ((LOG_ERR,
+                                        "cannot find user %s after update of expired password",
+                                        username));
+                               exit (1);
+                       }
+                       spw_free (spwd);
+                       spwd = xgetspnam (username);
                }
        }
-       setup_limits (&pwent);  /* nice, ulimit etc. */
+       setup_limits (pwd);     /* nice, ulimit etc. */
 #endif                         /* ! USE_PAM */
-       chown_tty (tty, &pwent);
+       chown_tty (pwd);
 
 #ifdef USE_PAM
        /*
         * We must fork before setuid() because we need to call
         * pam_close_session() as root.
         */
-       signal (SIGINT, SIG_IGN);
+       (void) signal (SIGINT, SIG_IGN);
        child = fork ();
        if (child < 0) {
                /* error in fork() */
                fprintf (stderr, _("%s: failure forking: %s"),
-                        Prog, strerror (errno));
+                        Prog, strerror (errno));
                PAM_END;
                exit (0);
-       } else if (child) {
+       } else if (child != 0) {
                /*
                 * parent - wait for child to finish, then cleanup
                 * session
@@ -1034,40 +1199,64 @@ int main (int argc, char **argv)
        }
        /* child */
 #endif
+
        /* If we were init, we need to start a new session */
        if (getppid() == 1) {
                setsid();
-               if (ioctl(0, TIOCSCTTY, 1))
-                       fprintf(stderr,_("TIOCSCTTY failed on %s"),tty);
+               if (ioctl(0, TIOCSCTTY, 1) != 0) {
+                       fprintf (stderr, _("TIOCSCTTY failed on %s"), tty);
+               }
        }
 
-       /* We call set_groups() above because this clobbers pam_groups.so */
+       /*
+        * 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.
+        *
+        * Close all the files so that unauthorized access won't occur.
+        */
+       endpwent ();            /* stop access to password file */
+       endgrent ();            /* stop access to group file */
+       endspent ();            /* stop access to shadow passwd file */
+#ifdef SHADOWGRP
+       endsgent ();            /* stop access to shadow group file */
+#endif
+
+       /* Drop root privileges */
 #ifndef USE_PAM
-       if (setup_uid_gid (&pwent, is_console))
+       if (setup_uid_gid (pwd, is_console))
 #else
-       if (change_uid (&pwent))
+       /* The group privileges were already dropped.
+        * See setup_groups() above.
+        */
+       if (change_uid (pwd))
 #endif
+       {
                exit (1);
+       }
 
-       setup_env (&pwent);     /* set env vars, cd to the home dir */
+       setup_env (pwd);        /* set env vars, cd to the home dir */
 
 #ifdef USE_PAM
        {
                const char *const *env;
 
                env = (const char *const *) pam_getenvlist (pamh);
-               while (env && *env) {
+               while ((NULL != env) && (NULL != *env)) {
                        addenv (*env, NULL);
                        env++;
                }
        }
 #endif
 
-       setlocale (LC_ALL, "");
-       bindtextdomain (PACKAGE, LOCALEDIR);
-       textdomain (PACKAGE);
+       (void) setlocale (LC_ALL, "");
+       (void) bindtextdomain (PACKAGE, LOCALEDIR);
+       (void) textdomain (PACKAGE);
 
-       if (!hushed (&pwent)) {
+       if (!hushed (username)) {
                addenv ("HUSHLOGIN=FALSE", NULL);
                /*
                 * pam_unix, pam_mail and pam_lastlog should take care of
@@ -1075,78 +1264,70 @@ int main (int argc, char **argv)
                 */
 #ifndef USE_PAM
                motd ();        /* print the message of the day */
-               if (getdef_bool ("FAILLOG_ENAB")
-                   && faillog.fail_cnt != 0) {
+               if (   getdef_bool ("FAILLOG_ENAB")
+                   && (0 != faillog.fail_cnt)) {
                        failprint (&faillog);
                        /* Reset the lockout times if logged in */
-                       if (faillog.fail_max &&
-                           faillog.fail_cnt >= faillog.fail_max) {
-                               puts (_
-                                     ("Warning: login re-enabled after temporary lockout."));
+                       if (   (0 != faillog.fail_max)
+                           && (faillog.fail_cnt >= faillog.fail_max)) {
+                               (void) puts (_("Warning: login re-enabled after temporary lockout."));
                                SYSLOG ((LOG_WARN,
-                                        "login `%s' re-enabled after temporary lockout (%d failures)",
-                                        username, (int) faillog.fail_cnt));
+                                        "login '%s' re-enabled after temporary lockout (%d failures)",
+                                        username, (int) faillog.fail_cnt));
                        }
                }
-               if (getdef_bool ("LASTLOG_ENAB")
-                   && lastlog.ll_time != 0) {
-                       time_t ll_time = lastlog.ll_time;
+               if (   getdef_bool ("LASTLOG_ENAB")
+                   && (ll.ll_time != 0)) {
+                       time_t ll_time = ll.ll_time;
 
 #ifdef HAVE_STRFTIME
-                       strftime (ptime, sizeof (ptime),
-                                 "%a %b %e %H:%M:%S %z %Y",
-                                 localtime (&ll_time));
+                       (void) strftime (ptime, sizeof (ptime),
+                                        "%a %b %e %H:%M:%S %z %Y",
+                                        localtime (&ll_time));
                        printf (_("Last login: %s on %s"),
-                               ptime, lastlog.ll_line);
+                               ptime, ll.ll_line);
 #else
                        printf (_("Last login: %.19s on %s"),
-                               ctime (&ll_time), lastlog.ll_line);
+                               ctime (&ll_time), ll.ll_line);
 #endif
 #ifdef HAVE_LL_HOST            /* __linux__ || SUN4 */
-                       if (lastlog.ll_host[0])
+                       if ('\0' != ll.ll_host[0]) {
                                printf (_(" from %.*s"),
-                                       (int) sizeof lastlog.
-                                       ll_host, lastlog.ll_host);
+                                       (int) sizeof ll.ll_host, ll.ll_host);
+                       }
 #endif
                        printf (".\n");
                }
-               agecheck (&pwent, spwd);
+               agecheck (spwd);
 
                mailcheck ();   /* report on the status of mail */
 #endif                         /* !USE_PAM */
-       } else
+       } else {
                addenv ("HUSHLOGIN=TRUE", NULL);
+       }
 
-       if (getdef_str ("TTYTYPE_FILE") != NULL && getenv ("TERM") == NULL)
-               ttytype (tty);
+       ttytype (tty);
 
-       signal (SIGQUIT, SIG_DFL);      /* default quit signal */
-       signal (SIGTERM, SIG_DFL);      /* default terminate signal */
-       signal (SIGALRM, SIG_DFL);      /* default alarm signal */
-       signal (SIGHUP, SIG_DFL);       /* added this.  --marekm */
-       signal (SIGINT, SIG_DFL);       /* default interrupt signal */
+       (void) signal (SIGQUIT, SIG_DFL);       /* default quit signal */
+       (void) signal (SIGTERM, SIG_DFL);       /* default terminate signal */
+       (void) signal (SIGALRM, SIG_DFL);       /* default alarm signal */
+       (void) signal (SIGHUP, SIG_DFL);        /* added this.  --marekm */
+       (void) signal (SIGINT, SIG_DFL);        /* default interrupt signal */
 
-       endpwent ();            /* stop access to password file */
-       endgrent ();            /* stop access to group file */
-       endspent ();            /* stop access to shadow passwd file */
-#ifdef SHADOWGRP
-       endsgent ();            /* stop access to shadow group file */
-#endif
-       if (pwent.pw_uid == 0)
+       if (0 == pwd->pw_uid) {
                SYSLOG ((LOG_NOTICE, "ROOT LOGIN %s", fromhost));
-       else if (getdef_bool ("LOG_OK_LOGINS"))
-#ifdef USE_PAM
-               SYSLOG ((LOG_INFO, "`%s' logged in %s", pam_user, fromhost));
-#else
-               SYSLOG ((LOG_INFO, "`%s' logged in %s", username, fromhost));
-#endif
+       } else if (getdef_bool ("LOG_OK_LOGINS")) {
+               SYSLOG ((LOG_INFO, "'%s' logged in %s", username, fromhost));
+       }
        closelog ();
-       if ((tmp = getdef_str ("FAKE_SHELL")) != NULL)
-               err = shell (tmp, pwent.pw_shell, newenvp); /* fake shell */
-       else
+       tmp = getdef_str ("FAKE_SHELL");
+       if (NULL != tmp) {
+               err = shell (tmp, pwd->pw_shell, newenvp); /* fake shell */
+       } else {
                /* exec the shell finally */
-               err = shell (pwent.pw_shell, (char *) 0, newenvp);
-       exit (err == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
-       /* NOT REACHED */
-       return 0;
+               err = shell (pwd->pw_shell, (char *) 0, newenvp);
+       }
+
+       return ((err == ENOENT) ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
 }
+