Logo Search packages:      
Sourcecode: shadow version File versions

passwd.c

/*
 * Copyright 1989 - 1994, Julianne Frances Haugh
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 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.
 *
 * 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.
 */

#include <config.h>

#include "rcsid.h"
RCSID (PKG_VER "$Id: passwd.c 6 2005-03-20 15:34:28Z bubulle $")
#include "prototypes.h"
#include "defines.h"
#include <sys/types.h>
#include <time.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#ifndef GPASSWD_PROGRAM
#define GPASSWD_PROGRAM "gpasswd"
#endif
#ifndef CHFN_PROGRAM
#define CHFN_PROGRAM "chfn"
#endif
#ifndef CHSH_PROGRAM
#define CHSH_PROGRAM "chsh"
#endif
#include <pwd.h>
#include "pwauth.h"
#ifdef HAVE_TCFS
#include <tcfslib.h>
#include "tcfsio.h"
#endif
#ifdef SHADOWPWD
#include "shadowio.h"
#endif
#include "pwio.h"
#include "getdef.h"
/*
 * exit status values
 */
#define E_SUCCESS 0     /* success */
#define E_NOPERM  1     /* permission denied */
#define E_USAGE         2     /* invalid combination of options */
#define E_FAILURE 3     /* unexpected failure, nothing done */
#define E_MISSING 4     /* unexpected failure, passwd file missing */
#define E_PWDBUSY 5     /* passwd file busy, try again later */
#define E_BAD_ARG 6     /* invalid argument to option */
/*
 * Global variables
 */
static char *name;            /* The name of user whose password is being changed */
static char *myname;          /* The current user's name */
static char *Prog;            /* Program name */
static int amroot;            /* The real UID was 0 */

static int
#ifdef SHADOWPWD
 eflg = 0,              /* -e - force password change */
 iflg = 0,              /* -i - set inactive days */
 kflg = 0,              /* -k - change only if expired */
 nflg = 0,              /* -n - set minimum days */
 wflg = 0,              /* -w - set warning days */
 xflg = 0,              /* -x - set maximum days */
#endif
 aflg = 0,              /* -a - show status for all users */
 dflg = 0,              /* -d - delete password */
 lflg = 0,              /* -l - lock account */
 qflg = 0,              /* -q - quiet mode */
 Sflg = 0,              /* -S - show password status */
 uflg = 0;              /* -u - unlock account */

/*
 * set to 1 if there are any flags which require root privileges,
 * and require username to be specified
 */
static int anyflag = 0;

#ifdef SHADOWPWD
static long age_min = 0;      /* Minimum days before change   */
static long age_max = 0;      /* Maximum days until change     */
static long warn = 0;         /* Warning days before change   */
static long inact = 0;        /* Days without change before locked */
#endif

static int do_update_age = 0;

#ifndef USE_PAM
static char crypt_passwd[128];      /* The "old-style" password, if present */
static int do_update_pwd = 0;
#endif

#ifdef HAVE_TCFS
static struct tcfspwd *tcfspword;
static int tcfs_force = 0;
#endif

/*
 * External identifiers
 */

#ifdef ATT_AGE
extern char *l64a ();
#endif

#ifdef      NDBM
extern int sp_dbm_mode;
extern int pw_dbm_mode;
#endif

/* local function prototypes */
static void usage (int);

#ifndef USE_PAM
#ifdef AUTH_METHODS
static char *get_password (const char *);
static int uses_default_method (const char *);
#endif                        /* AUTH_METHODS */
static int reuse (const char *, const struct passwd *);
static int new_password (const struct passwd *);

#ifdef SHADOWPWD
static void check_password (const struct passwd *, const struct spwd *);
#else                   /* !SHADOWPWD */
static void check_password (const struct passwd *);
#endif                        /* !SHADOWPWD */
static char *insert_crypt_passwd (const char *, const char *);
#endif                        /* !USE_PAM */
static char *date_to_str (time_t);
static const char *pw_status (const char *);
static void print_status (const struct passwd *);
static void fail_exit (int);
static void oom (void);
static char *update_crypt_pw (char *);
static void update_noshadow (void);

#ifdef SHADOWPWD
static void update_shadow (void);
#endif
#ifdef HAVE_TCFS
static void update_tcfs (void);
#endif
static long getnumber (const char *);

/*
 * usage - print command usage and exit
 */

static void usage (int status)
{
      fprintf (stderr, _("usage: %s [-f|-s] [name]\n"), Prog);
      if (amroot) {
            fprintf (stderr,
                   _
                   ("       %s [-x max] [-n min] [-w warn] [-i inact] name\n"),
                   Prog);
            fprintf (stderr, _("       %s {-l|-u|-d|-S|-e} name\n"),
                   Prog);
      }
      exit (status);
}

#ifndef USE_PAM
#ifdef AUTH_METHODS
/*
 * get_password - locate encrypted password in authentication list
 */

static char *get_password (const char *list)
{
      char *cp, *end;
      static char buf[257];

      STRFCPY (buf, list);
      for (cp = buf; cp; cp = end) {
            if ((end = strchr (cp, ';')))
                  *end++ = 0;

            if (cp[0] == '@')
                  continue;

            return cp;
      }
      return (char *) 0;
}

/*
 * uses_default_method - determine if "old-style" password present
 *
 *    uses_default_method determines if a "old-style" password is present
 *    in the authentication string, and if one is present it extracts it.
 */

static int uses_default_method (const char *methods)
{
      char *cp;

      if ((cp = get_password (methods))) {
            STRFCPY (crypt_passwd, cp);
            return 1;
      }
      return 0;
}
#endif                        /* AUTH_METHODS */

static int reuse (const char *pass, const struct passwd *pw)
{
#ifdef HAVE_LIBCRACK_HIST
      const char *reason;

#ifdef HAVE_LIBCRACK_PW
      const char *FascistHistoryPw (const char *, const struct passwd *);

      reason = FascistHistory (pass, pw);
#else
      const char *FascistHistory (const char *, int);

      reason = FascistHistory (pass, pw->pw_uid);
#endif
      if (reason) {
            printf (_("Bad password: %s.  "), reason);
            return 1;
      }
#endif
      return 0;
}

/*
 * new_password - validate old password and replace with new (both old and
 * new in global "char crypt_passwd[128]")
 */

 /*ARGSUSED*/ static int new_password (const struct passwd *pw)
{
      char *clear;            /* Pointer to clear text */
      char *cipher;           /* Pointer to cipher text */
      char *cp;         /* Pointer to getpass() response */
      char orig[200];         /* Original password */
      char pass[200];         /* New password */
      int i;                  /* Counter for retries */
      int warned;
      int pass_max_len;

#ifdef HAVE_LIBCRACK_HIST
      int HistUpdate (const char *, const char *);
#endif

      /*
       * Authenticate the user. The user will be prompted for their own
       * password.
       */

#ifdef HAVE_TCFS
      tcfs_force = tcfs_force && amroot;

      if ((tcfs_locate (name) && !tcfs_force)
          || (!amroot && crypt_passwd[0])) {
            if (amroot) {
                  printf (_
                        ("User %s has a TCFS key, his old password is required.\n"),
                        name);
                  printf (_
                        ("You can use -t option to force the change.\n"));
            }
#else
      if (!amroot && crypt_passwd[0]) {
#endif

            if (!(clear = getpass (_("Old password: "))))
                  return -1;

            cipher = pw_encrypt (clear, crypt_passwd);
            if (strcmp (cipher, crypt_passwd) != 0) {
                  SYSLOG ((LOG_WARN, "incorrect password for `%s'",
                         pw->pw_name));
                  sleep (1);
                  fprintf (stderr,
                         _("Incorrect password for `%s'\n"),
                         pw->pw_name);
                  return -1;
            }
            STRFCPY (orig, clear);
#ifdef HAVE_TCFS
            STRFCPY (tcfspword->tcfsorig, clear);
#endif
            strzero (clear);
            strzero (cipher);
      } else {
#ifdef HAVE_TCFS
            if (tcfs_locate (name))
                  printf (_("Warning: user %s has a TCFS key.\n"),
                        name);
#endif
            orig[0] = '\0';
      }

      /*
       * Get the new password. The user is prompted for the new password
       * and has five tries to get it right. The password will be tested
       * for strength, unless it is the root user. This provides an escape
       * for initial login passwords.
       */

      if (getdef_bool ("MD5_CRYPT_ENAB"))
            pass_max_len = 127;
      else
            pass_max_len = getdef_num ("PASS_MAX_LEN", 8);

      if (!qflg)
            printf (_("\
Enter the new password (minimum of %d, maximum of %d characters)\n\
Please use a combination of upper and lower case letters and numbers.\n"), getdef_num ("PASS_MIN_LEN", 5), pass_max_len);

      warned = 0;
      for (i = getdef_num ("PASS_CHANGE_TRIES", 5); i > 0; i--) {
            if (!(cp = getpass (_("New password: ")))) {
                  memzero (orig, sizeof orig);
                  return -1;
            }
            if (warned && strcmp (pass, cp) != 0)
                  warned = 0;
            STRFCPY (pass, cp);
            strzero (cp);

            if (!amroot
                && (!obscure (orig, pass, pw) || reuse (pass, pw))) {
                  printf (_("Try again.\n"));
                  continue;
            }
            /*
             * If enabled, warn about weak passwords even if you are
             * root (enter this password again to use it anyway). 
             * --marekm
             */
            if (amroot && !warned && getdef_bool ("PASS_ALWAYS_WARN")
                && (!obscure (orig, pass, pw) || reuse (pass, pw))) {
                  printf (_
                        ("\nWarning: weak password (enter it again to use it anyway).\n"));
                  warned++;
                  continue;
            }
            if (!(cp = getpass (_("Re-enter new password: ")))) {
                  memzero (orig, sizeof orig);
                  return -1;
            }
            if (strcmp (cp, pass))
                  fprintf (stderr,
                         _("They don't match; try again.\n"));
            else {
                  strzero (cp);
                  break;
            }
      }
      memzero (orig, sizeof orig);

      if (i == 0) {
            memzero (pass, sizeof pass);
            return -1;
      }
#ifdef HAVE_TCFS
      STRFCPY (tcfspword->tcfspass, pass);
#endif

      /*
       * Encrypt the password, then wipe the cleartext password.
       */

      cp = pw_encrypt (pass, crypt_make_salt ());
      memzero (pass, sizeof pass);

#ifdef HAVE_LIBCRACK_HIST
      HistUpdate (pw->pw_name, crypt_passwd);
#endif
      STRFCPY (crypt_passwd, cp);
      return 0;
}

/*
 * check_password - test a password to see if it can be changed
 *
 *    check_password() sees if the invoker has permission to change the
 *    password for the given user.
 */

#ifdef SHADOWPWD
static void check_password (const struct passwd *pw, const struct spwd *sp)
{
#else
static void check_password (const struct passwd *pw)
{
#endif
      time_t now, last, ok;
      int exp_status;

#ifdef SHADOWPWD
      exp_status = isexpired (pw, sp);
#else
      exp_status = isexpired (pw);
#endif

      /*
       * If not expired and the "change only if expired" option (idea from
       * PAM) was specified, do nothing. --marekm
       */
      if (kflg && exp_status == 0)
            exit (E_SUCCESS);

      /*
       * Root can change any password any time.
       */

      if (amroot)
            return;

      time (&now);

#ifdef SHADOWPWD
      /*
       * Expired accounts cannot be changed ever. Passwords which are
       * locked may not be changed. Passwords where min > max may not be
       * changed. Passwords which have been inactive too long cannot be
       * changed.
       */

      if (sp->sp_pwdp[0] == '!' || exp_status > 1 ||
          (sp->sp_max >= 0 && sp->sp_min > sp->sp_max)) {
            fprintf (stderr,
                   _("The password for %s cannot be changed.\n"),
                   sp->sp_namp);
            SYSLOG ((LOG_WARN, "password locked for `%s'",
                   sp->sp_namp));
            closelog ();
            exit (E_NOPERM);
      }

      /*
       * Passwords may only be changed after sp_min time is up.
       */

      last = sp->sp_lstchg * SCALE;
      ok = last + (sp->sp_min > 0 ? sp->sp_min * SCALE : 0);

#else                   /* !SHADOWPWD */
      if (pw->pw_passwd[0] == '!' || exp_status > 1) {
            fprintf (stderr,
                   _("The password for %s cannot be changed.\n"),
                   pw->pw_name);
            SYSLOG ((LOG_WARN, "password locked for `%s'",
                   pw->pw_name));
            closelog ();
            exit (E_NOPERM);
      }
#ifdef ATT_AGE
      /*
       * Can always be changed if there is no age info
       */

      if (!pw->pw_age[0])
            return;

      last = a64l (pw->pw_age + 2) * WEEK;
      ok = last + c64i (pw->pw_age[1]) * WEEK;
#else                   /* !ATT_AGE */
      last = 0;
      ok = 0;
#endif                        /* !ATT_AGE */
#endif                        /* !SHADOWPWD */
      if (now < ok) {
            fprintf (stderr,
                   _
                   ("Sorry, the password for %s cannot be changed yet.\n"),
                   pw->pw_name);
            SYSLOG ((LOG_WARN, "now < minimum age for `%s'",
                   pw->pw_name));
            closelog ();
            exit (E_NOPERM);
      }
}

/*
 * insert_crypt_passwd - add an "old-style" password to authentication
 * string result now malloced to avoid overflow, just in case.  --marekm
 */
static char *insert_crypt_passwd (const char *string, const char *passwd)
{
#ifdef AUTH_METHODS
      if (string && *string) {
            char *cp, *result;

            result = xmalloc (strlen (string) + strlen (passwd) + 1);
            cp = result;
            while (*string) {
                  if (string[0] == ';') {
                        *cp++ = *string++;
                  } else if (string[0] == '@') {
                        while (*string && *string != ';')
                              *cp++ = *string++;
                  } else {
                        while (*passwd)
                              *cp++ = *passwd++;

                        while (*string && *string != ';')
                              string++;
                  }
            }
            *cp = '\0';
            return result;
      }
#endif
      return xstrdup (passwd);
}
#endif                        /* !USE_PAM */

static char *date_to_str (time_t t)
{
      static char buf[80];
      struct tm *tm;

      tm = gmtime (&t);
#ifdef HAVE_STRFTIME
      strftime (buf, sizeof buf, "%m/%d/%Y", tm);
#else
      snprintf (buf, sizeof buf, "%02d/%02d/%04d",
              tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900);
#endif
      return buf;
}

static const char *pw_status (const char *pass)
{
      if (*pass == '*' || *pass == '!')
            return "L";
      if (*pass == '\0')
            return "NP";
      return "P";
}

/*
 * print_status - print current password status
 */

static void print_status (const struct passwd *pw)
{
#ifdef SHADOWPWD
      struct spwd *sp;
#endif

#ifdef SHADOWPWD
      sp = getspnam (pw->pw_name);
      if (sp) {
            printf ("%s %s %s %ld %ld %ld %ld\n",
                  pw->pw_name,
                  pw_status (sp->sp_pwdp),
                  date_to_str (sp->sp_lstchg * SCALE),
                  (sp->sp_min * SCALE) / DAY,
                  (sp->sp_max * SCALE) / DAY,
                  (sp->sp_warn * SCALE) / DAY,
                  (sp->sp_inact * SCALE) / DAY);
      } else
#endif
      {
#ifdef ATT_AGE
            printf ("%s %s %s %d %d\n",
                  pw->pw_name,
                  pw_status (pw->pw_passwd),
                  date_to_str (strlen (pw->pw_age) > 2 ?
                             a64l (pw->pw_age + 2) * WEEK : 0L),
                  pw->pw_age[0] ? c64i (pw->pw_age[1]) * 7 : 0,
                  pw->pw_age[0] ? c64i (pw->pw_age[0]) * 7 : 10000);
#else
            printf ("%s %s\n", pw->pw_name, pw_status (pw->pw_passwd));
#endif
      }
}


static void fail_exit (int status)
{
      pw_unlock ();
#ifdef SHADOWPWD
      spw_unlock ();
#endif
#ifdef HAVE_TCFS
      tcfs_unlock ();
#endif
      exit (status);
}

static void oom (void)
{
      fprintf (stderr, _("%s: out of memory\n"), Prog);
      fail_exit (E_FAILURE);
}

static char *update_crypt_pw (char *cp)
{
#ifndef USE_PAM
      if (do_update_pwd)
            cp = insert_crypt_passwd (cp, crypt_passwd);
#endif

      if (dflg)
            cp = "";    /* XXX warning: const */

      if (uflg && *cp == '!')
            cp++;

      if (lflg && *cp != '!') {
            char *newpw = xmalloc (strlen (cp) + 2);

            strcpy (newpw, "!");
            strcat (newpw, cp);
            cp = newpw;
      }
      return cp;
}


static void update_noshadow (void)
{
      const struct passwd *pw;
      struct passwd *npw;

#ifdef ATT_AGE
      char age[5];
      long week = time ((time_t *) 0) / WEEK;
      char *cp;
#endif

      if (!pw_lock ()) {
            fprintf (stderr,
                   _
                   ("Cannot lock the password file; try again later.\n"));
            SYSLOG ((LOG_WARN, "can't lock password file"));
            exit (E_PWDBUSY);
      }
      if (!pw_open (O_RDWR)) {
            fprintf (stderr, _("Cannot open the password file.\n"));
            SYSLOG ((LOG_ERR, "can't open password file"));
            fail_exit (E_MISSING);
      }
      pw = pw_locate (name);
      if (!pw) {
            fprintf (stderr, _("%s: %s not found in /etc/passwd\n"),
                   Prog, name);
            fail_exit (E_NOPERM);
      }
      npw = __pw_dup (pw);
      if (!npw)
            oom ();
      npw->pw_passwd = update_crypt_pw (npw->pw_passwd);
#ifdef ATT_AGE
      memzero (age, sizeof (age));
      STRFCPY (age, npw->pw_age);

      /*
       * Just changing the password - update the last change date if there
       * is one, otherwise the age just disappears.
       */
      if (do_update_age) {
            if (strlen (age) > 2) {
                  cp = l64a (week);
                  age[2] = cp[0];
                  age[3] = cp[1];
            } else {
                  age[0] = '\0';
            }
      }

      if (xflg) {
            if (age_max > 0)
                  age[0] = i64c ((age_max + 6) / 7);
            else
                  age[0] = '.';

            if (age[1] == '\0')
                  age[1] = '.';
      }
      if (nflg) {
            if (age[0] == '\0')
                  age[0] = 'z';

            if (age_min > 0)
                  age[1] = i64c ((age_min + 6) / 7);
            else
                  age[1] = '.';
      }
      /*
       * The last change date is added by -n or -x if it's not already
       * there.
       */
      if ((nflg || xflg) && strlen (age) <= 2) {
            cp = l64a (week);
            age[2] = cp[0];
            age[3] = cp[1];
      }

      /*
       * Force password change - if last change date is present, it will
       * be set to (today - max - 1 week). Otherwise, just set min = max
       * = 0 (will disappear when password is changed).
       */
      if (eflg) {
            if (strlen (age) > 2) {
                  cp = l64a (week - c64i (age[0]) - 1);
                  age[2] = cp[0];
                  age[3] = cp[1];
            } else {
                  strcpy (age, "..");
            }
      }

      npw->pw_age = age;
#endif
      if (!pw_update (npw)) {
            fprintf (stderr,
                   _("Error updating the password entry.\n"));
            SYSLOG ((LOG_ERR, "error updating password entry"));
            fail_exit (E_FAILURE);
      }
#ifdef NDBM
      if (pw_dbm_present () && !pw_dbm_update (npw)) {
            fprintf (stderr,
                   _("Error updating the DBM password entry.\n"));
            SYSLOG ((LOG_ERR, "error updaring dbm password entry"));
            fail_exit (E_FAILURE);
      }
      endpwent ();
#endif
      if (!pw_close ()) {
            fprintf (stderr,
                   _("Cannot commit password file changes.\n"));
            SYSLOG ((LOG_ERR, "can't rewrite password file"));
            fail_exit (E_FAILURE);
      }
      pw_unlock ();
}

#ifdef HAVE_TCFS
static void update_tcfs (void)
{
      if (!tcfs_force) {
            if (!tcfs_lock ()) {
                  fprintf (stderr,
                         _
                         ("Cannot lock the TCFS key database; try again later\n"));
                  SYSLOG ((LOG_WARN,
                         "can't lock TCFS key database"));
                  exit (E_PWDBUSY);
            }
            if (!tcfs_open (O_RDWR)) {
                  fprintf (stderr,
                         _
                         ("Cannot open the TCFS key database.\n"));
                  SYSLOG ((LOG_WARN,
                         "can't open TCFS key database"));
                  fail_exit (E_MISSING);
            }
            if (!tcfs_update (name, tcfspword)) {
                  fprintf (stderr,
                         _
                         ("Error updating the TCFS key database.\n"));
                  SYSLOG ((LOG_ERR,
                         "error updating TCFS key database"));
                  fail_exit (E_FAILURE);
            }
            if (!tcfs_close ()) {
                  fprintf (stderr,
                         _("Cannot commit TCFS changes.\n"));
                  SYSLOG ((LOG_ERR,
                         "can't rewrite TCFS key database"));
                  fail_exit (E_FAILURE);
            }
            tcfs_unlock ();
      }
}
#endif                        /* HAVE_TCFS */

#ifdef SHADOWPWD
static void update_shadow (void)
{
      const struct spwd *sp;
      struct spwd *nsp;

      if (!spw_lock ()) {
            fprintf (stderr,
                   _
                   ("Cannot lock the password file; try again later.\n"));
            SYSLOG ((LOG_WARN, "can't lock password file"));
            exit (E_PWDBUSY);
      }
      if (!spw_open (O_RDWR)) {
            fprintf (stderr, _("Cannot open the password file.\n"));
            SYSLOG ((LOG_ERR, "can't open password file"));
            fail_exit (E_FAILURE);
      }
      sp = spw_locate (name);
      if (!sp) {
            /* Try to update the password in /etc/passwd instead. */
            spw_close ();
            update_noshadow ();
            spw_unlock ();
            return;
      }
      nsp = __spw_dup (sp);
      if (!nsp)
            oom ();
      nsp->sp_pwdp = update_crypt_pw (nsp->sp_pwdp);
      if (xflg)
            nsp->sp_max = (age_max * DAY) / SCALE;
      if (nflg)
            nsp->sp_min = (age_min * DAY) / SCALE;
      if (wflg)
            nsp->sp_warn = (warn * DAY) / SCALE;
      if (iflg)
            nsp->sp_inact = (inact * DAY) / SCALE;
      if (do_update_age)
            nsp->sp_lstchg = time ((time_t *) 0) / SCALE;
      /*
       * Force change on next login, like SunOS 4.x passwd -e or Solaris
       * 2.x passwd -f. Solaris 2.x seems to do the same thing (set
       * sp_lstchg to 0).
       */
      if (eflg)
            nsp->sp_lstchg = 0;

      if (!spw_update (nsp)) {
            fprintf (stderr,
                   _("Error updating the password entry.\n"));
            SYSLOG ((LOG_ERR, "error updating password entry"));
            fail_exit (E_FAILURE);
      }
#ifdef NDBM
      if (sp_dbm_present () && !sp_dbm_update (nsp)) {
            fprintf (stderr,
                   _("Error updating the DBM password entry.\n"));
            SYSLOG ((LOG_ERR, "error updaring dbm password entry"));
            fail_exit (E_FAILURE);
      }
      endspent ();
#endif
      if (!spw_close ()) {
            fprintf (stderr,
                   _("Cannot commit password file changes.\n"));
            SYSLOG ((LOG_ERR, "can't rewrite password file"));
            fail_exit (E_FAILURE);
      }
      spw_unlock ();
}
#endif                        /* SHADOWPWD */

static long getnumber (const char *str)
{
      long val;
      char *cp;

      val = strtol (str, &cp, 10);
      if (*cp)
            usage (E_BAD_ARG);
      return val;
}

/*
 * passwd - change a user's password file information
 *
 *    This command controls the password file and commands which are used
 *    to modify it.
 *
 *    The valid options are
 *
 *    -l    lock the named account (*)
 *    -u    unlock the named account (*)
 *    -d    delete the password for the named account (*)
 *    -e    expire the password for the named account (*)
 *    -x #  set sp_max to # days (*)
 *    -n #  set sp_min to # days (*)
 *    -w #  set sp_warn to # days (*)
 *    -i #  set sp_inact to # days (*)
 *    -S    show password status of named account
 *    -g    execute gpasswd command to interpret flags
 *    -f    execute chfn command to interpret flags
 *    -s    execute chsh command to interpret flags
 *    -k    change password only if expired
 *    -t    force 'passwd' to change the password regardless of TCFS
 *
 *    (*) requires root permission to execute.
 *
 *    All of the time fields are entered in days and converted to the
 *    appropriate internal format. For finer resolute the chage
 *    command must be used.
 */

int main (int argc, char **argv)
{
      int flag;         /* Current option to process     */
      const struct passwd *pw;      /* Password file entry for user      */

#ifndef USE_PAM
      char *cp;         /* Miscellaneous character pointing  */

#ifdef SHADOWPWD
      const struct spwd *sp;  /* Shadow file entry for user   */
#endif
#endif

      setlocale (LC_ALL, "");
      bindtextdomain (PACKAGE, LOCALEDIR);
      textdomain (PACKAGE);

      /*
       * The program behaves differently when executed by root than when
       * executed by a normal user.
       */
      amroot = (getuid () == 0);

      /*
       * Get the program name. The program name is used as a prefix to
       * most error messages.
       */
      Prog = Basename (argv[0]);

      sanitize_env ();

      OPENLOG ("passwd");

      /*
       * Start with the flags which cause another command to be executed.
       * The effective UID will be set back to the real UID and the new
       * command executed with the flags
       *
       * These flags are deprecated, may change in a future release.
       * Please run these programs directly.  --marekm
       */

      if (argc > 1 && argv[1][0] == '-' && strchr ("gfs", argv[1][1])) {
            char buf[200];

                uid_t uid = getuid();
                setuid (uid);
                if (getuid() != uid) {
                        perror("cannot set user id");
                        SYSLOG ((LOG_ERR, "setuid to %i failed", uid));
                        exit(E_FAILURE);
                }
            switch (argv[1][1]) {
            case 'g':
                  argv[1] = GPASSWD_PROGRAM;    /* XXX warning: const */
                  break;
            case 'f':
                  argv[1] = CHFN_PROGRAM; /* XXX warning: const */
                  break;
            case 's':
                  argv[1] = CHSH_PROGRAM; /* XXX warning: const */
                  break;
            default:
                  usage (E_BAD_ARG);
            }
            snprintf (buf, sizeof buf, _("%s: Cannot execute %s"),
                    Prog, argv[1]);
            execvp (argv[1], &argv[1]);
            perror (buf);
            SYSLOG ((LOG_ERR, "cannot execute %s", argv[1]));
            closelog ();
            exit (E_FAILURE);
      }

      /* 
       * The remaining arguments will be processed one by one and executed
       * by this command. The name is the last argument if it does not
       * begin with a "-", otherwise the name is determined from the
       * environment and must agree with the real UID. Also, the UID will
       * be checked for any commands which are restricted to root only.
       */

#ifdef SHADOWPWD

#define FLAGS "adlqr:uSekn:x:i:w:"
#ifdef HAVE_TCFS
#undef FLAGS
#define FLAGS "adlqr:uSekn:x:i:w:t"
#endif

#else

#define FLAGS "adlqr:uS"
#ifdef HAVE_TCFS
#undef FLAGS
#define FLAGS "adlqr:uSt"
#endif

#endif

      while ((flag = getopt (argc, argv, FLAGS)) != EOF) {
#undef FLAGS
            switch (flag) {
#ifdef      HAVE_TCFS
            case 't':
                  tcfs_force = 1;
                  break;
#endif
#ifdef      SHADOWPWD
            case 'x':
                  age_max = getnumber (optarg);
                  xflg++;
                  anyflag = 1;
                  break;
            case 'n':
                  age_min = getnumber (optarg);
                  nflg++;
                  anyflag = 1;
                  break;
            case 'w':
                  warn = getnumber (optarg);
                  if (warn >= -1)
                        wflg++;
                  anyflag = 1;
                  break;
            case 'i':
                  inact = getnumber (optarg);
                  if (inact >= -1)
                        iflg++;
                  anyflag = 1;
                  break;
            case 'e':
                  eflg++;
                  anyflag = 1;
                  break;
            case 'k':
                  /* change only if expired, like Linux-PAM passwd -k. */
                  kflg++;     /* ok for users */
                  break;
#endif                        /* SHADOWPWD */
            case 'a':
                  aflg++;
                  break;
            case 'q':
                  qflg++;     /* ok for users */
                  break;
            case 'S':
                  Sflg++;     /* ok for users */
                  break;
            case 'd':
                  dflg++;
                  anyflag = 1;
                  break;
            case 'l':
                  lflg++;
                  anyflag = 1;
                  break;
            case 'u':
                  uflg++;
                  anyflag = 1;
                  break;
            case 'r':
                  /* -r repository (files|nis|nisplus) */
                  /* only "files" supported for now */
                  if (strcmp (optarg, "files") != 0) {
                        fprintf (stderr,
                               _
                               ("%s: repository %s not supported\n"),
                               Prog, optarg);
                        exit (E_BAD_ARG);
                  }
                  break;
            default:
                  usage (E_BAD_ARG);
            }
      }

      /*
       * Now I have to get the user name. The name will be gotten from the
       * command line if possible. Otherwise it is figured out from the
       * environment.
       */

      pw = get_my_pwent ();
      if (!pw) {
            fprintf (stderr,
                   _("%s: Cannot determine your user name.\n"),
                   Prog);
            exit (E_NOPERM);
      }
      myname = xstrdup (pw->pw_name);
      if (optind < argc)
            name = argv[optind];
      else
            name = myname;

      /*
       * The -a flag requires -S, no other flags, no username, and
       * you must be root.  --marekm
       */

      if (aflg) {
            if (anyflag || !Sflg || (optind < argc))
                  usage (E_USAGE);
            if (!amroot) {
                  fprintf (stderr, _("%s: Permission denied.\n"),
                         Prog);
                  exit (E_NOPERM);
            }
            setpwent ();
            while ((pw = getpwent ()))
                  print_status (pw);
            exit (E_SUCCESS);
      }
#if 0
      /*
       * Allow certain users (administrators) to change passwords of
       * certain users. Not implemented yet. --marekm
       */
      if (may_change_passwd (myname, name))
            amroot = 1;
#endif

      /*
       * If any of the flags were given, a user name must be supplied on
       * the command line. Only an unadorned command line doesn't require
       * the user's name be given. Also, -x, -n, -w, -i, -e, -d,
       * -l, -u may appear with each other. -S, -k must appear alone.
       */

      /*
       * -S now ok for normal users (check status of my own account), and
       * doesn't require username.  --marekm
       */

      if (anyflag && optind >= argc)
            usage (E_USAGE);

      if (anyflag + Sflg + kflg > 1)
            usage (E_USAGE);

      if (anyflag && !amroot) {
            fprintf (stderr, _("%s: Permission denied\n"), Prog);
            exit (E_NOPERM);
      }
#ifdef NDBM
      endpwent ();
      pw_dbm_mode = O_RDWR;
#ifdef SHADOWPWD
      sp_dbm_mode = O_RDWR;
#endif
#endif

      pw = getpwnam (name);
      if (!pw) {
            fprintf (stderr, _("%s: Unknown user %s\n"), Prog, name);
            exit (E_NOPERM);
      }

      /*
       * Now I have a name, let's see if the UID for the name matches the
       * current real UID.
       */

      if (!amroot && pw->pw_uid != getuid ()) {
            fprintf (stderr,
                   _("%s: You may not view or modify password information for %s.\n"),
                   Prog, name);
            SYSLOG ((LOG_WARN, "can't change pwd for `%s'", name));
            closelog ();
            exit (E_NOPERM);
      }

      if (Sflg) {
            print_status (pw);
            exit (E_SUCCESS);
      }
#ifndef USE_PAM
#ifdef SHADOWPWD
      /*
       * The user name is valid, so let's get the shadow file entry.
       */

      sp = getspnam (name);
      if (!sp)
            sp = pwd_to_spwd (pw);

      cp = sp->sp_pwdp;
#else
      cp = pw->pw_passwd;
#endif

      /*
       * If there are no other flags, just change the password.
       */

      if (!anyflag) {
#ifdef AUTH_METHODS
            if (strchr (cp, '@')) {
                  if (pw_auth (cp, name, PW_CHANGE, (char *) 0)) {
                        SYSLOG ((LOG_INFO,
                               "password for `%s' changed by `%s'",
                               name));
                        closelog ();
                        exit (E_NOPERM);
                  } else if (!uses_default_method (cp)) {
                        do_update_age = 1;
                        goto done;
                  }
            } else
#endif
                  STRFCPY (crypt_passwd, cp);

            /*
             * See if the user is permitted to change the password. 
             * Otherwise, go ahead and set a new password.
             */

#ifdef SHADOWPWD
            check_password (pw, sp);
#else
            check_password (pw);
#endif

#ifdef HAVE_TCFS
            tcfspword =
                (struct tcfspwd *) calloc (1, sizeof (struct tcfspwd));
#endif
            /*
             * Let the user know whose password is being changed.
             */
            if (!qflg)
                  printf (_("Changing password for %s\n"), name);

            if (new_password (pw)) {
                  fprintf (stderr,
                         _("The password for %s is unchanged.\n"),
                         name);
                  closelog ();
                  exit (E_NOPERM);
            }
            do_update_pwd = 1;
            do_update_age = 1;
      }
#ifdef AUTH_METHODS
      done:
#endif
#endif                        /* !USE_PAM */
      /*
       * Before going any further, raise the ulimit to prevent colliding
       * into a lowered ulimit, and set the real UID to root to protect
       * against unexpected signals. Any keyboard signals are set to be
       * ignored.
       */

      pwd_init ();

      /*
       * Don't set the real UID for PAM...
       */
#ifdef USE_PAM
      if (!anyflag) {
            do_pam_passwd (name, qflg, kflg);
            exit (E_SUCCESS);
      }
#endif                        /* SHADOWPWD */
      if (setuid (0)) {
            fprintf (stderr, _("Cannot change ID to root.\n"));
            SYSLOG ((LOG_ERR, "can't setuid(0)"));
            closelog ();
            exit (E_NOPERM);
      }
#ifdef SHADOWPWD
      if (spw_file_present ())
            update_shadow ();
      else
#endif
            update_noshadow ();

#ifdef HAVE_TCFS
      if (tcfs_locate (name) && tcfs_file_present ())
            update_tcfs ();
#endif
      SYSLOG ((LOG_INFO, "password for `%s' changed by `%s'", name,
             myname));
      closelog ();
      if (!qflg)
            printf (_("Password changed.\n"));
      exit (E_SUCCESS);
 /*NOTREACHED*/}

Generated by  Doxygen 1.6.0   Back to index