]> granicus.if.org Git - shadow/blobdiff - src/chage.c
* src/chfn.c, man/chfn.1.xml: Add support for long options.
[shadow] / src / chage.c
index 2810194a90b454baffba206c14c4a6d01a801bed..40e465f429c844c0667a744201903cb90d43fe79 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 1989 - 1994, Julianne Frances Haugh
  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
  * Copyright (c) 2000 - 2006, Tomasz Kłoczko
- * Copyright (c) 2007 - 2008, Nicolas François
+ * Copyright (c) 2007 - 2011, Nicolas François
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include <stdlib.h>
 #include <sys/types.h>
 #include <time.h>
+#ifdef ACCT_TOOLS_SETUID
 #ifdef USE_PAM
 #include "pam_defs.h"
 #endif                         /* USE_PAM */
+#endif                         /* ACCT_TOOLS_SETUID */
 #include <pwd.h>
 #ifdef WITH_SELINUX
 #include <selinux/selinux.h>
 #include <selinux/av_permissions.h>
 #endif
-#include "exitcodes.h"
 #include "prototypes.h"
 #include "defines.h"
 #include "pwio.h"
 #include "shadowio.h"
+#ifdef WITH_TCB
+#include "tcbfuncs.h"
+#endif
+/*@-exitarg@*/
+#include "exitcodes.h"
+
 /*
  * Global variables
  */
-static char *Prog;
+const char *Prog;
 
 static bool
     dflg = false,              /* set last password change date */
@@ -78,20 +85,13 @@ static uid_t user_uid = -1;
 
 static long mindays;
 static long maxdays;
-static long lastday;
+static long lstchgdate;
 static long warndays;
 static long inactdays;
-static long expdays;
-
-#ifdef USE_PAM
-static pam_handle_t *pamh = NULL;
-#endif
-
-#define        EPOCH           "1969-12-31"
+static long expdate;
 
 /* local function prototypes */
-static bool isnum (const char *s);
-static void usage (void);
+static /*@noreturn@*/void usage (int status);
 static void date_to_str (char *buf, size_t maxsize, time_t date);
 static int new_fields (void);
 static void print_date (time_t date);
@@ -101,18 +101,26 @@ static void check_flags (int argc, int opt_index);
 static void check_perms (void);
 static void open_files (bool readonly);
 static void close_files (void);
-static void fail_exit (int code);
+static /*@noreturn@*/void fail_exit (int code);
 
 /*
  * fail_exit - do some cleanup and exit with the given error code
  */
-static void fail_exit (int code)
+static /*@noreturn@*/void fail_exit (int code)
 {
        if (spw_locked) {
-               spw_unlock ();
+               if (spw_unlock () == 0) {
+                       fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
+                       SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+                       /* continue */
+               }
        }
        if (pw_locked) {
-               pw_unlock ();
+               if (pw_unlock () == 0) {
+                       fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
+                       SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+                       /* continue */
+               }
        }
        closelog ();
 
@@ -124,54 +132,34 @@ static void fail_exit (int code)
        }
 #endif
 
-#ifdef USE_PAM
-       if (NULL != pamh) {
-               /* If there is a PAM error, pam_end will be called by the
-                * caller.
-                * We always end the pam transaction with PAM_SUCCESS here.
-                */
-               (void) pam_end (pamh, PAM_SUCCESS);
-       }
-#endif
-
        exit (code);
 }
 
-/*
- * isnum - determine whether or not a string is a number
- */
-static bool isnum (const char *s)
-{
-       while ('\0' != *s) {
-               if (!isdigit (*s)) {
-                       return false;
-               }
-               s++;
-       }
-       return true;
-}
-
 /*
  * usage - print command line syntax and exit
  */
-static void usage (void)
+static /*@noreturn@*/void usage (int status)
 {
-       fputs (_("Usage: chage [options] [LOGIN]\n"
-                "\n"
-                "Options:\n"
-                "  -d, --lastday LAST_DAY        set last password change to LAST_DAY\n"
-                "  -E, --expiredate EXPIRE_DATE  set account expiration date to EXPIRE_DATE\n"
-                "  -h, --help                    display this help message and exit\n"
-                "  -I, --inactive INACTIVE       set password inactive after expiration\n"
-                "                                to INACTIVE\n"
-                "  -l, --list                    show account aging information\n"
-                "  -m, --mindays MIN_DAYS        set minimum number of days before password\n"
-                "                                change to MIN_DAYS\n"
-                "  -M, --maxdays MAX_DAYS        set maximim number of days before password\n"
-                "                                change to MAX_DAYS\n"
-                "  -W, --warndays WARN_DAYS      set expiration warning days to WARN_DAYS\n"
-                "\n"), stderr);
-       exit (E_USAGE);
+       FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
+       (void) fprintf (usageout,
+                       _("Usage: %s [options] LOGIN\n"
+                         "\n"
+                         "Options:\n"),
+                       Prog);
+       (void) fputs (_("  -d, --lastday LAST_DAY        set date of last password change to LAST_DAY\n"), usageout);
+       (void) fputs (_("  -E, --expiredate EXPIRE_DATE  set account expiration date to EXPIRE_DATE\n"), usageout);
+       (void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
+       (void) fputs (_("  -I, --inactive INACTIVE       set password inactive after expiration\n"
+                       "                                to INACTIVE\n"), usageout);
+       (void) fputs (_("  -l, --list                    show account aging information\n"), usageout);
+       (void) fputs (_("  -m, --mindays MIN_DAYS        set minimum number of days before password\n"
+                       "                                change to MIN_DAYS\n"), usageout);
+       (void) fputs (_("  -M, --maxdays MAX_DAYS        set maximim number of days before password\n"
+                       "                                change to MAX_DAYS\n"), usageout);
+       (void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
+       (void) fputs (_("  -W, --warndays WARN_DAYS      set expiration warning days to WARN_DAYS\n"), usageout);
+       (void) fputs ("\n", usageout);
+       exit (status);
 }
 
 static void date_to_str (char *buf, size_t maxsize, time_t date)
@@ -199,66 +187,69 @@ static void date_to_str (char *buf, size_t maxsize, time_t date)
 static int new_fields (void)
 {
        char buf[200];
-       char *cp;
 
        (void) puts (_("Enter the new value, or press ENTER for the default"));
        (void) puts ("");
 
-       snprintf (buf, sizeof buf, "%ld", mindays);
+       (void) snprintf (buf, sizeof buf, "%ld", mindays);
        change_field (buf, sizeof buf, _("Minimum Password Age"));
-       mindays = strtol (buf, &cp, 10);
-       if (   ((0 == mindays) && ('\0' != *cp))
+       if (   (getlong (buf, &mindays) == 0)
            || (mindays < -1)) {
                return 0;
        }
 
-       snprintf (buf, sizeof buf, "%ld", maxdays);
+       (void) snprintf (buf, sizeof buf, "%ld", maxdays);
        change_field (buf, sizeof buf, _("Maximum Password Age"));
-       maxdays = strtol (buf, &cp, 10);
-       if (   ((0 == maxdays) && ('\0' != *cp))
+       if (   (getlong (buf, &maxdays) == 0)
            || (maxdays < -1)) {
                return 0;
        }
 
-       date_to_str (buf, sizeof buf, lastday * SCALE);
+       if (-1 == lstchgdate) {
+               strcpy (buf, "-1");
+       } else {
+               date_to_str (buf, sizeof buf, lstchgdate * SCALE);
+       }
 
        change_field (buf, sizeof buf, _("Last Password Change (YYYY-MM-DD)"));
 
-       if (strcmp (buf, EPOCH) == 0) {
-               lastday = -1;
+       if (strcmp (buf, "-1") == 0) {
+               lstchgdate = -1;
        } else {
-               lastday = strtoday (buf);
-               if (lastday == -1) {
+               lstchgdate = strtoday (buf);
+               if (lstchgdate <= -1) {
                        return 0;
                }
        }
 
-       snprintf (buf, sizeof buf, "%ld", warndays);
+       (void) snprintf (buf, sizeof buf, "%ld", warndays);
        change_field (buf, sizeof buf, _("Password Expiration Warning"));
-       warndays = strtol (buf, &cp, 10);
-       if (   ((warndays == 0) && ('\0' != *cp))
+       if (   (getlong (buf, &warndays) == 0)
            || (warndays < -1)) {
                return 0;
        }
 
-       snprintf (buf, sizeof buf, "%ld", inactdays);
+       (void) snprintf (buf, sizeof buf, "%ld", inactdays);
        change_field (buf, sizeof buf, _("Password Inactive"));
-       inactdays = strtol (buf, &cp, 10);
-       if (   ((inactdays == 0) && ('\0' != *cp))
+       if (   (getlong (buf, &inactdays) == 0)
            || (inactdays < -1)) {
                return 0;
        }
 
-       date_to_str (buf, sizeof buf, expdays * SCALE);
+       if (-1 == expdate) {
+               strcpy (buf, "-1");
+       } else {
+               date_to_str (buf, sizeof buf, expdate * SCALE);
+       }
 
        change_field (buf, sizeof buf,
                      _("Account Expiration Date (YYYY-MM-DD)"));
 
-       if (strcmp (buf, EPOCH) == 0) {
-               expdays = -1;
+       if (strcmp (buf, "-1") == 0) {
+               expdate = -1;
        } else {
-               expdays = strtoday (buf);
-               if (expdays == -1) {
+               expdate = strtoday (buf);
+               if (expdate <= -1) {
                        return 0;
                }
        }
@@ -274,7 +265,7 @@ static void print_date (time_t date)
 
        tp = gmtime (&date);
        if (NULL == tp) {
-               (void) printf ("time_t: %ul\n", date);
+               (void) printf ("time_t: %lu\n", (unsigned long)date);
        } else {
                (void) strftime (buf, sizeof buf, "%b %d, %Y", tp);
                (void) puts (buf);
@@ -289,8 +280,8 @@ static void print_date (time_t date)
        }
        if (NULL != cp) {
                (void) printf ("%6.6s, %4.4s\n", cp + 4, cp + 20);
-       } else {
-               (void) printf ("time_t: %ul\n", date);
+       } else {
+               (void) printf ("time_t: %lu\n", date);
        }
 #endif
 }
@@ -312,12 +303,12 @@ static void list_fields (void)
         * was last modified. The date is the number of days since 1/1/1970.
         */
        (void) fputs (_("Last password change\t\t\t\t\t: "), stdout);
-       if (lastday < 0) {
+       if (lstchgdate < 0) {
                (void) puts (_("never"));
-       } else if (lastday == 0) {
+       } else if (lstchgdate == 0) {
                (void) puts (_("password must be changed"));
        } else {
-               changed = lastday * SCALE;
+               changed = lstchgdate * SCALE;
                print_date ((time_t) changed);
        }
 
@@ -326,8 +317,11 @@ static void list_fields (void)
         * date plus the number of days the password is valid for.
         */
        (void) fputs (_("Password expires\t\t\t\t\t: "), stdout);
-       if ((lastday <= 0) || (maxdays >= (10000 * (DAY / SCALE)))
-           || (maxdays < 0)) {
+       if (lstchgdate == 0) {
+               (void) puts (_("password must be changed"));
+       } else if (   (lstchgdate < 0)
+                  || (maxdays >= (10000 * (DAY / SCALE)))
+                  || (maxdays < 0)) {
                (void) puts (_("never"));
        } else {
                expires = changed + maxdays * SCALE;
@@ -341,8 +335,12 @@ static void list_fields (void)
         * active will be disabled.
         */
        (void) fputs (_("Password inactive\t\t\t\t\t: "), stdout);
-       if ((lastday <= 0) || (inactdays < 0) ||
-           (maxdays >= (10000 * (DAY / SCALE))) || (maxdays < 0)) {
+       if (lstchgdate == 0) {
+               (void) puts (_("password must be changed"));
+       } else if (   (lstchgdate < 0)
+                  || (inactdays < 0)
+                  || (maxdays >= (10000 * (DAY / SCALE)))
+                  || (maxdays < 0)) {
                (void) puts (_("never"));
        } else {
                expires = changed + (maxdays + inactdays) * SCALE;
@@ -354,10 +352,10 @@ static void list_fields (void)
         * password expiring or not.
         */
        (void) fputs (_("Account expires\t\t\t\t\t\t: "), stdout);
-       if (expdays < 0) {
+       if (expdate < 0) {
                (void) puts (_("never"));
        } else {
-               expires = expdays * SCALE;
+               expires = expdate * SCALE;
                print_date ((time_t) expires);
        }
 
@@ -386,7 +384,6 @@ static void process_flags (int argc, char **argv)
        /*
         * Parse the command line options.
         */
-       int option_index = 0;
        int c;
        static struct option long_options[] = {
                {"lastday", required_argument, NULL, 'd'},
@@ -396,54 +393,84 @@ static void process_flags (int argc, char **argv)
                {"list", no_argument, NULL, 'l'},
                {"mindays", required_argument, NULL, 'm'},
                {"maxdays", required_argument, NULL, 'M'},
+               {"root", required_argument, NULL, 'R'},
                {"warndays", required_argument, NULL, 'W'},
                {NULL, 0, NULL, '\0'}
        };
 
-       while ((c =
-               getopt_long (argc, argv, "d:E:hI:lm:M:W:", long_options,
-                            &option_index)) != -1) {
+       while ((c = getopt_long (argc, argv, "d:E:hI:lm:M:R:W:",
+                                long_options, NULL)) != -1) {
                switch (c) {
                case 'd':
                        dflg = true;
-                       if (!isnum (optarg)) {
-                               lastday = strtoday (optarg);
-                       } else {
-                               lastday = strtol (optarg, 0, 10);
+                       lstchgdate = strtoday (optarg);
+                       if (lstchgdate < -1) {
+                               fprintf (stderr,
+                                        _("%s: invalid date '%s'\n"),
+                                        Prog, optarg);
+                               usage (E_USAGE);
                        }
                        break;
                case 'E':
                        Eflg = true;
-                       if (!isnum (optarg)) {
-                               expdays = strtoday (optarg);
-                       } else {
-                               expdays = strtol (optarg, 0, 10);
+                       expdate = strtoday (optarg);
+                       if (expdate < -1) {
+                               fprintf (stderr,
+                                        _("%s: invalid date '%s'\n"),
+                                        Prog, optarg);
+                               usage (E_USAGE);
                        }
                        break;
                case 'h':
-                       usage ();
-                       break;
+                       usage (E_SUCCESS);
+                       /*@notreached@*/break;
                case 'I':
                        Iflg = true;
-                       inactdays = strtol (optarg, 0, 10);
+                       if (   (getlong (optarg, &inactdays) == 0)
+                           || (inactdays < -1)) {
+                               fprintf (stderr,
+                                        _("%s: invalid numeric argument '%s'\n"),
+                                        Prog, optarg);
+                               usage (E_USAGE);
+                       }
                        break;
                case 'l':
                        lflg = true;
                        break;
                case 'm':
                        mflg = true;
-                       mindays = strtol (optarg, 0, 10);
+                       if (   (getlong (optarg, &mindays) == 0)
+                           || (mindays < -1)) {
+                               fprintf (stderr,
+                                        _("%s: invalid numeric argument '%s'\n"),
+                                        Prog, optarg);
+                               usage (E_USAGE);
+                       }
                        break;
                case 'M':
                        Mflg = true;
-                       maxdays = strtol (optarg, 0, 10);
+                       if (   (getlong (optarg, &maxdays) == 0)
+                           || (maxdays < -1)) {
+                               fprintf (stderr,
+                                        _("%s: invalid numeric argument '%s'\n"),
+                                        Prog, optarg);
+                               usage (E_USAGE);
+                       }
+                       break;
+               case 'R': /* no-op, handled in process_root_flag () */
                        break;
                case 'W':
                        Wflg = true;
-                       warndays = strtol (optarg, 0, 10);
+                       if (   (getlong (optarg, &warndays) == 0)
+                           || (warndays < -1)) {
+                               fprintf (stderr,
+                                        _("%s: invalid numeric argument '%s'\n"),
+                                        Prog, optarg);
+                               usage (E_USAGE);
+                       }
                        break;
                default:
-                       usage ();
+                       usage (E_USAGE);
                }
        }
 
@@ -463,14 +490,14 @@ static void check_flags (int argc, int opt_index)
         */
 
        if (argc != opt_index + 1) {
-               usage ();
+               usage (E_USAGE);
        }
 
        if (lflg && (mflg || Mflg || dflg || Wflg || Iflg || Eflg)) {
                fprintf (stderr,
                         _("%s: do not include \"l\" with other flags\n"),
                         Prog);
-               usage ();
+               usage (E_USAGE);
        }
 }
 
@@ -490,10 +517,13 @@ static void check_flags (int argc, int opt_index)
  */
 static void check_perms (void)
 {
+#ifdef ACCT_TOOLS_SETUID
 #ifdef USE_PAM
+       pam_handle_t *pamh = NULL;
        struct passwd *pampw;
        int retval;
-#endif
+#endif                         /* USE_PAM */
+#endif                         /* ACCT_TOOLS_SETUID */
 
        /*
         * An unprivileged user can ask for their own aging information, but
@@ -506,38 +536,35 @@ static void check_perms (void)
                fail_exit (E_NOPERM);
        }
 
+#ifdef ACCT_TOOLS_SETUID
 #ifdef USE_PAM
-       retval = PAM_SUCCESS;
-
        pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
        if (NULL == pampw) {
-               retval = PAM_USER_UNKNOWN;
+               fprintf (stderr,
+                        _("%s: Cannot determine your user name.\n"),
+                        Prog);
+               exit (E_NOPERM);
        }
 
-       if (PAM_SUCCESS == retval) {
-               retval = pam_start ("chage", pampw->pw_name, &conv, &pamh);
-       }
+       retval = pam_start ("chage", pampw->pw_name, &conv, &pamh);
 
        if (PAM_SUCCESS == retval) {
                retval = pam_authenticate (pamh, 0);
-               if (PAM_SUCCESS != retval) {
-                       (void) pam_end (pamh, retval);
-               }
        }
 
        if (PAM_SUCCESS == retval) {
                retval = pam_acct_mgmt (pamh, 0);
-               if (PAM_SUCCESS != retval) {
-                       (void) pam_end (pamh, retval);
-               }
        }
 
+       if (NULL != pamh) {
+               (void) pam_end (pamh, retval);
+       }
        if (PAM_SUCCESS != retval) {
                fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
-               pamh = NULL;
                fail_exit (E_NOPERM);
        }
 #endif                         /* USE_PAM */
+#endif                         /* ACCT_TOOLS_SETUID */
 }
 
 /*
@@ -553,18 +580,18 @@ static void open_files (bool readonly)
         * file entries into memory. Then we get a pointer to the password
         * file entry for the requested user.
         */
-       if (!readonly && (pw_lock () == 0)) {
-               fprintf (stderr,
-                        _("%s: can't lock password file\n"), Prog);
-               SYSLOG ((LOG_ERR, "failed locking %s", PASSWD_FILE));
-               fail_exit (E_NOPERM);
-       }
        if (!readonly) {
+               if (pw_lock () == 0) {
+                       fprintf (stderr,
+                                _("%s: cannot lock %s; try again later.\n"),
+                                Prog, pw_dbname ());
+                       fail_exit (E_NOPERM);
+               }
                pw_locked = true;
        }
        if (pw_open (readonly ? O_RDONLY: O_RDWR) == 0) {
-               fprintf (stderr, _("%s: can't open password file\n"), Prog);
-               SYSLOG ((LOG_ERR, "failed opening %s", PASSWD_FILE));
+               fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ());
+               SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ()));
                fail_exit (E_NOPERM);
        }
 
@@ -574,19 +601,19 @@ static void open_files (bool readonly)
         * does not have to exist in this case; a new entry will be created
         * for this user if one does not exist already.
         */
-       if (!readonly && (spw_lock () == 0)) {
-               fprintf (stderr,
-                        _("%s: can't lock shadow password file\n"), Prog);
-               SYSLOG ((LOG_ERR, "failed locking %s", SHADOW_FILE));
-               fail_exit (E_NOPERM);
-       }
        if (!readonly) {
+               if (spw_lock () == 0) {
+                       fprintf (stderr,
+                                _("%s: cannot lock %s; try again later.\n"),
+                                Prog, spw_dbname ());
+                       fail_exit (E_NOPERM);
+               }
                spw_locked = true;
        }
        if (spw_open (readonly ? O_RDONLY: O_RDWR) == 0) {
                fprintf (stderr,
-                        _("%s: can't open shadow password file\n"), Prog);
-               SYSLOG ((LOG_ERR, "failed opening %s", SHADOW_FILE));
+                        _("%s: cannot open %s\n"), Prog, spw_dbname ());
+               SYSLOG ((LOG_WARN, "cannot open %s", spw_dbname ()));
                fail_exit (E_NOPERM);
        }
 }
@@ -602,8 +629,8 @@ static void close_files (void)
         */
        if (spw_close () == 0) {
                fprintf (stderr,
-                        _("%s: can't rewrite shadow password file\n"), Prog);
-               SYSLOG ((LOG_ERR, "failed rewriting %s", SHADOW_FILE));
+                        _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ());
+               SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ()));
                fail_exit (E_NOPERM);
        }
 
@@ -612,13 +639,21 @@ static void close_files (void)
         * will be re-written.
         */
        if (pw_close () == 0) {
-               fprintf (stderr, _("%s: can't rewrite password file\n"), Prog);
-               SYSLOG ((LOG_ERR, "failed rewriting %s", PASSWD_FILE));
+               fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ());
+               SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
                fail_exit (E_NOPERM);
        }
-       spw_unlock ();
+       if (spw_unlock () == 0) {
+               fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
+               SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
+               /* continue */
+       }
        spw_locked = false;
-       pw_unlock ();
+       if (pw_unlock () == 0) {
+               fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
+               SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
+               /* continue */
+       }
        pw_locked = false;
 }
 
@@ -627,7 +662,8 @@ static void close_files (void)
  *
  *     It will not return in case of error
  */
-static void update_age (const struct spwd *sp, const struct passwd *pw)
+static void update_age (/*@null@*/const struct spwd *sp,
+                        /*@notnull@*/const struct passwd *pw)
 {
        struct spwd spwent;
 
@@ -647,8 +683,7 @@ static void update_age (const struct spwd *sp, const struct passwd *pw)
                pwent.pw_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */
                if (pw_update (&pwent) == 0) {
                        fprintf (stderr,
-                                _("%s: can't update password file\n"), Prog);
-                       SYSLOG ((LOG_ERR, "failed updating %s", PASSWD_FILE));
+                                _("%s: failed to prepare the new %s entry '%s'\n"), Prog, pw_dbname (), pwent.pw_name);
                        fail_exit (E_NOPERM);
                }
        } else {
@@ -664,15 +699,14 @@ static void update_age (const struct spwd *sp, const struct passwd *pw)
         */
        spwent.sp_max = maxdays;
        spwent.sp_min = mindays;
-       spwent.sp_lstchg = lastday;
+       spwent.sp_lstchg = lstchgdate;
        spwent.sp_warn = warndays;
        spwent.sp_inact = inactdays;
-       spwent.sp_expire = expdays;
+       spwent.sp_expire = expdate;
 
        if (spw_update (&spwent) == 0) {
                fprintf (stderr,
-                        _("%s: can't update shadow password file\n"), Prog);
-               SYSLOG ((LOG_ERR, "failed updating %s", SHADOW_FILE));
+                        _("%s: failed to prepare the new %s entry '%s'\n"), Prog, spw_dbname (), spwent.sp_namp);
                fail_exit (E_NOPERM);
        }
 
@@ -681,7 +715,7 @@ static void update_age (const struct spwd *sp, const struct passwd *pw)
 /*
  * get_defaults - get the value of the fields not set from the command line
  */
-static void get_defaults (const struct spwd *sp)
+static void get_defaults (/*@null@*/const struct spwd *sp)
 {
        /*
         * Set the fields that aren't being set from the command line from
@@ -695,7 +729,7 @@ static void get_defaults (const struct spwd *sp)
                        mindays = sp->sp_min;
                }
                if (!dflg) {
-                       lastday = sp->sp_lstchg;
+                       lstchgdate = sp->sp_lstchg;
                }
                if (!Wflg) {
                        warndays = sp->sp_warn;
@@ -704,7 +738,7 @@ static void get_defaults (const struct spwd *sp)
                        inactdays = sp->sp_inact;
                }
                if (!Eflg) {
-                       expdays = sp->sp_expire;
+                       expdate = sp->sp_expire;
                }
        } else {
                /*
@@ -718,7 +752,7 @@ static void get_defaults (const struct spwd *sp)
                        mindays = -1;
                }
                if (!dflg) {
-                       lastday = 0;
+                       lstchgdate = -1;
                }
                if (!Wflg) {
                        warndays = -1;
@@ -727,7 +761,7 @@ static void get_defaults (const struct spwd *sp)
                        inactdays = -1;
                }
                if (!Eflg) {
-                       expdays = -1;
+                       expdate = -1;
                }
        }
 }
@@ -760,14 +794,23 @@ int main (int argc, char **argv)
        gid_t rgid;
        const struct passwd *pw;
 
-#ifdef WITH_AUDIT
-       audit_help_open ();
-#endif
+       /*
+        * Get the program name so that error messages can use it.
+        */
+       Prog = Basename (argv[0]);
+
        sanitize_env ();
        (void) setlocale (LC_ALL, "");
        (void) bindtextdomain (PACKAGE, LOCALEDIR);
        (void) textdomain (PACKAGE);
 
+       process_root_flag ("-R", argc, argv);
+
+#ifdef WITH_AUDIT
+       audit_help_open ();
+#endif
+       OPENLOG ("chage");
+
        ruid = getuid ();
        rgid = getgid ();
        amroot = (ruid == 0);
@@ -777,22 +820,15 @@ int main (int argc, char **argv)
        }
 #endif
 
-       /*
-        * Get the program name so that error messages can use it.
-        */
-       Prog = Basename (argv[0]);
-
        process_flags (argc, argv);
 
-       OPENLOG ("chage");
-
        check_perms ();
 
        if (!spw_file_present ()) {
                fprintf (stderr,
                         _("%s: the shadow password file is not present\n"),
                         Prog);
-               SYSLOG ((LOG_ERR, "can't find the shadow password file"));
+               SYSLOG ((LOG_WARN, "can't find the shadow password file"));
                closelog ();
                exit (E_SHADOW_NOTFOUND);
        }
@@ -808,17 +844,22 @@ int main (int argc, char **argv)
 
        pw = pw_locate (argv[optind]);
        if (NULL == pw) {
-               fprintf (stderr, _("%s: unknown user %s\n"), Prog,
-                        argv[optind]);
+               fprintf (stderr, _("%s: user '%s' does not exist in %s\n"),
+                        Prog, argv[optind], pw_dbname ());
                closelog ();
-               exit (E_NOPERM);
+               fail_exit (E_NOPERM);
        }
 
        STRFCPY (user_name, pw->pw_name);
+#ifdef WITH_TCB
+       if (shadowtcb_set_user (pw->pw_name) == SHADOWTCB_FAILURE) {
+               fail_exit (E_NOPERM);
+       }
+#endif
        user_uid = pw->pw_uid;
 
        sp = spw_locate (argv[optind]);
-       get_defaults(sp);
+       get_defaults (sp);
 
        /*
         * Print out the expiration fields if the user has requested the
@@ -898,10 +939,6 @@ int main (int argc, char **argv)
 
        SYSLOG ((LOG_INFO, "changed password expiry for %s", user_name));
 
-#ifdef USE_PAM
-       (void) pam_end (pamh, PAM_SUCCESS);
-#endif                         /* USE_PAM */
-
        closelog ();
        exit (E_SUCCESS);
 }