f5461ff01e
* NEWS, src/passwd.c: Make sure that no more than one username argument was provided.
961 lines
24 KiB
C
961 lines
24 KiB
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>
|
|
|
|
#ident "$Id$"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <pwd.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#ifdef WITH_SELINUX
|
|
#include <selinux/selinux.h>
|
|
#include <selinux/av_permissions.h>
|
|
#endif
|
|
#include <time.h>
|
|
#include "defines.h"
|
|
#include "getdef.h"
|
|
#include "nscd.h"
|
|
#include "prototypes.h"
|
|
#include "pwauth.h"
|
|
#include "pwio.h"
|
|
#include "shadowio.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
|
|
aflg = 0, /* -a - show status for all users */
|
|
dflg = 0, /* -d - delete password */
|
|
eflg = 0, /* -e - force password change */
|
|
iflg = 0, /* -i - set inactive days */
|
|
kflg = 0, /* -k - change only if expired */
|
|
lflg = 0, /* -l - lock account */
|
|
nflg = 0, /* -n - set minimum days */
|
|
qflg = 0, /* -q - quiet mode */
|
|
Sflg = 0, /* -S - show password status */
|
|
uflg = 0, /* -u - unlock account */
|
|
wflg = 0, /* -w - set warning days */
|
|
xflg = 0; /* -x - set maximum days */
|
|
|
|
/*
|
|
* set to 1 if there are any flags which require root privileges,
|
|
* and require username to be specified
|
|
*/
|
|
static int anyflag = 0;
|
|
|
|
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 */
|
|
|
|
static int do_update_age = 0;
|
|
|
|
#ifndef USE_PAM
|
|
/*
|
|
* Size of the biggest passwd:
|
|
* $6$ 3
|
|
* rounds= 7
|
|
* 999999999 9
|
|
* $ 1
|
|
* salt 16
|
|
* $ 1
|
|
* SHA512 123
|
|
* nul 1
|
|
*
|
|
* total 161
|
|
*/
|
|
static char crypt_passwd[256];
|
|
static int do_update_pwd = 0;
|
|
#endif
|
|
|
|
/*
|
|
* External identifiers
|
|
*/
|
|
|
|
/* local function prototypes */
|
|
static void usage (int);
|
|
|
|
#ifndef USE_PAM
|
|
static int reuse (const char *, const struct passwd *);
|
|
static int new_password (const struct passwd *);
|
|
|
|
static void check_password (const struct passwd *, const struct spwd *);
|
|
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);
|
|
|
|
static void update_shadow (void);
|
|
static long getnumber (const char *);
|
|
|
|
/*
|
|
* usage - print command usage and exit
|
|
*/
|
|
static void usage (int status)
|
|
{
|
|
fprintf (stderr, _("Usage: passwd [options] [LOGIN]\n"
|
|
"\n"
|
|
"Options:\n"
|
|
" -a, --all report password status on all accounts\n"
|
|
" -d, --delete delete the password for the named account\n"
|
|
" -e, --expire force expire the password for the named account\n"
|
|
" -h, --help display this help message and exit\n"
|
|
" -k, --keep-tokens change password only if expired\n"
|
|
" -i, --inactive INACTIVE set password inactive after expiration\n"
|
|
" to INACTIVE\n"
|
|
" -l, --lock lock the named account\n"
|
|
" -n, --mindays MIN_DAYS set minimum number of days before password\n"
|
|
" change to MIN_DAYS\n"
|
|
" -q, --quiet quiet mode\n"
|
|
" -r, --repository REPOSITORY change password in REPOSITORY repository\n"
|
|
" -S, --status report password status on the named account\n"
|
|
" -u, --unlock unlock the named account\n"
|
|
" -w, --warndays WARN_DAYS set expiration warning days to WARN_DAYS\n"
|
|
" -x, --maxdays MAX_DAYS set maximim number of days before password\n"
|
|
" change to MAX_DAYS\n"
|
|
"\n"));
|
|
exit (status);
|
|
}
|
|
|
|
#ifndef USE_PAM
|
|
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]")
|
|
*/
|
|
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 = -1;
|
|
char *method;
|
|
|
|
#ifdef HAVE_LIBCRACK_HIST
|
|
int HistUpdate (const char *, const char *);
|
|
#endif
|
|
|
|
/*
|
|
* Authenticate the user. The user will be prompted for their own
|
|
* password.
|
|
*/
|
|
|
|
if (!amroot && crypt_passwd[0]) {
|
|
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);
|
|
strzero (clear);
|
|
strzero (cipher);
|
|
} else {
|
|
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 ((method = getdef_str ("ENCRYPT_METHOD")) == NULL) {
|
|
if (!getdef_bool ("MD5_CRYPT_ENAB"))
|
|
pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
|
|
} else {
|
|
if ( !strcmp (method, "MD5")
|
|
#ifdef USE_SHA_CRYPT
|
|
|| !strcmp (method, "SHA256")
|
|
|| !strcmp (method, "SHA512")
|
|
#endif
|
|
)
|
|
pass_max_len = -1;
|
|
else
|
|
pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
|
|
}
|
|
if (!qflg) {
|
|
if (pass_max_len == -1) {
|
|
printf (_(
|
|
"Enter the new password (minimum of %d characters)\n"
|
|
"Please use a combination of upper and lower case letters and numbers.\n"),
|
|
getdef_num ("PASS_MIN_LEN", 5));
|
|
} else {
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Encrypt the password, then wipe the cleartext password.
|
|
*/
|
|
cp = pw_encrypt (pass, crypt_make_salt (NULL, NULL));
|
|
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.
|
|
*/
|
|
static void check_password (const struct passwd *pw, const struct spwd *sp)
|
|
{
|
|
time_t now, last, ok;
|
|
int exp_status;
|
|
|
|
exp_status = isexpired (pw, sp);
|
|
|
|
/*
|
|
* 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);
|
|
|
|
/*
|
|
* 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);
|
|
|
|
if (now < ok) {
|
|
fprintf (stderr,
|
|
_
|
|
("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)
|
|
{
|
|
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)
|
|
{
|
|
struct spwd *sp;
|
|
|
|
sp = getspnam (pw->pw_name); /* local, no need for xgetspnam */
|
|
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 {
|
|
printf ("%s %s\n", pw->pw_name, pw_status (pw->pw_passwd));
|
|
}
|
|
}
|
|
|
|
|
|
static void fail_exit (int status)
|
|
{
|
|
pw_unlock ();
|
|
spw_unlock ();
|
|
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 == '!') {
|
|
if (cp[1] == '\0') {
|
|
fprintf (stderr,
|
|
_("%s: unlocking the user would result in a passwordless account.\n"
|
|
"You should set a password with usermod -p to unlock this user account.\n"),
|
|
Prog);
|
|
} else {
|
|
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;
|
|
|
|
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);
|
|
if (!pw_update (npw)) {
|
|
fprintf (stderr, _("Error updating the password entry.\n"));
|
|
SYSLOG ((LOG_ERR, "error updating password entry"));
|
|
fail_exit (E_FAILURE);
|
|
}
|
|
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 ();
|
|
}
|
|
|
|
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;
|
|
if (lflg) {
|
|
/* Set the account expiry field to 1.
|
|
* Some PAM implementation consider zero as a non expired
|
|
* account.
|
|
*/
|
|
nsp->sp_expire = 1;
|
|
}
|
|
if (uflg)
|
|
nsp->sp_expire = -1;
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
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 ();
|
|
}
|
|
|
|
static long getnumber (const char *numstr)
|
|
{
|
|
long val;
|
|
char *errptr;
|
|
|
|
val = strtol (numstr, &errptr, 10);
|
|
if (*errptr || errno == ERANGE) {
|
|
fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog,
|
|
numstr);
|
|
exit (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
|
|
*
|
|
* -d delete the password for the named account (*)
|
|
* -e expire the password for the named account (*)
|
|
* -f execute chfn command to interpret flags
|
|
* -g execute gpasswd command to interpret flags
|
|
* -i # set sp_inact to # days (*)
|
|
* -k change password only if expired
|
|
* -l lock the named account (*)
|
|
* -n # set sp_min to # days (*)
|
|
* -r # change password in # repository
|
|
* -s execute chsh command to interpret flags
|
|
* -S show password status of named account
|
|
* -u unlock the named account (*)
|
|
* -w # set sp_warn to # days (*)
|
|
* -x # set sp_max to # days (*)
|
|
*
|
|
* (*) 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)
|
|
{
|
|
const struct passwd *pw; /* Password file entry for user */
|
|
|
|
#ifndef USE_PAM
|
|
char *cp; /* Miscellaneous character pointing */
|
|
|
|
const struct spwd *sp; /* Shadow file entry for user */
|
|
#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");
|
|
|
|
{
|
|
/*
|
|
* Parse the command line options.
|
|
*/
|
|
int option_index = 0;
|
|
int c;
|
|
static struct option long_options[] = {
|
|
{"all", no_argument, NULL, 'a'},
|
|
{"delete", no_argument, NULL, 'd'},
|
|
{"expire", no_argument, NULL, 'e'},
|
|
{"help", no_argument, NULL, 'h'},
|
|
{"inactive", required_argument, NULL, 'i'},
|
|
{"keep-tokens", no_argument, NULL, 'k'},
|
|
{"lock", no_argument, NULL, 'l'},
|
|
{"mindays", required_argument, NULL, 'n'},
|
|
{"quiet", no_argument, NULL, 'q'},
|
|
{"repository", required_argument, NULL, 'r'},
|
|
{"status", no_argument, NULL, 'S'},
|
|
{"unlock", no_argument, NULL, 'u'},
|
|
{"warndays", required_argument, NULL, 'w'},
|
|
{"maxdays", required_argument, NULL, 'x'},
|
|
{NULL, 0, NULL, '\0'}
|
|
};
|
|
|
|
while ((c =
|
|
getopt_long (argc, argv, "adei:kln:qr:Suw:x:",
|
|
long_options, &option_index)) != -1) {
|
|
switch (c) {
|
|
case 'a':
|
|
aflg++;
|
|
break;
|
|
case 'd':
|
|
dflg++;
|
|
anyflag = 1;
|
|
break;
|
|
case 'e':
|
|
eflg++;
|
|
anyflag = 1;
|
|
break;
|
|
case 'i':
|
|
inact = getnumber (optarg);
|
|
if (inact >= -1)
|
|
iflg++;
|
|
anyflag = 1;
|
|
break;
|
|
case 'k':
|
|
/* change only if expired, like Linux-PAM passwd -k. */
|
|
kflg++; /* ok for users */
|
|
break;
|
|
case 'l':
|
|
lflg++;
|
|
anyflag = 1;
|
|
break;
|
|
case 'n':
|
|
age_min = getnumber (optarg);
|
|
nflg++;
|
|
anyflag = 1;
|
|
break;
|
|
case 'q':
|
|
qflg++; /* ok for users */
|
|
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;
|
|
case 'S':
|
|
Sflg++; /* ok for users */
|
|
break;
|
|
case 'u':
|
|
uflg++;
|
|
anyflag = 1;
|
|
break;
|
|
case 'w':
|
|
warn = getnumber (optarg);
|
|
if (warn >= -1)
|
|
wflg++;
|
|
anyflag = 1;
|
|
break;
|
|
case 'x':
|
|
age_max = getnumber (optarg);
|
|
xflg++;
|
|
anyflag = 1;
|
|
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;
|
|
|
|
/*
|
|
* Make sure that at most one username was specified.
|
|
*/
|
|
if (argc > optind+1)
|
|
usage (E_USAGE);
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
pw = xgetpwnam (name);
|
|
if (!pw) {
|
|
fprintf (stderr, _("%s: unknown user %s\n"), Prog, name);
|
|
exit (E_NOPERM);
|
|
}
|
|
#ifdef WITH_SELINUX
|
|
/*
|
|
* If the UID of the user does not match the current real UID,
|
|
* check if the change is allowed by SELinux policy.
|
|
*/
|
|
if ((pw->pw_uid != getuid ())
|
|
&& (is_selinux_enabled () > 0 ?
|
|
(selinux_check_passwd_access (PASSWD__PASSWD) != 0) :
|
|
!amroot)) {
|
|
#else
|
|
/*
|
|
* If the UID of the user does not match the current real UID,
|
|
* check if I'm root.
|
|
*/
|
|
if (!amroot && pw->pw_uid != getuid ()) {
|
|
#endif
|
|
fprintf (stderr,
|
|
_
|
|
("%s: You may not view or modify password information for %s.\n"),
|
|
Prog, name);
|
|
SYSLOG ((LOG_WARN,
|
|
"%s: can't view or modify password information for %s",
|
|
Prog, name));
|
|
closelog ();
|
|
exit (E_NOPERM);
|
|
}
|
|
|
|
if (Sflg) {
|
|
print_status (pw);
|
|
exit (E_SUCCESS);
|
|
}
|
|
#ifndef USE_PAM
|
|
/*
|
|
* The user name is valid, so let's get the shadow file entry.
|
|
*/
|
|
sp = getspnam (name); /* !USE_PAM, no need for xgetspnam */
|
|
if (!sp)
|
|
sp = pwd_to_spwd (pw);
|
|
|
|
cp = sp->sp_pwdp;
|
|
|
|
/*
|
|
* If there are no other flags, just change the password.
|
|
*/
|
|
if (!anyflag) {
|
|
STRFCPY (crypt_passwd, cp);
|
|
|
|
/*
|
|
* See if the user is permitted to change the password.
|
|
* Otherwise, go ahead and set a new password.
|
|
*/
|
|
check_password (pw, sp);
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
#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 ();
|
|
|
|
#ifdef USE_PAM
|
|
/*
|
|
* Don't set the real UID for PAM...
|
|
*/
|
|
if (!anyflag) {
|
|
do_pam_passwd (name, qflg, kflg);
|
|
exit (E_SUCCESS);
|
|
}
|
|
#endif /* USE_PAM */
|
|
if (setuid (0)) {
|
|
fprintf (stderr, _("Cannot change ID to root.\n"));
|
|
SYSLOG ((LOG_ERR, "can't setuid(0)"));
|
|
closelog ();
|
|
exit (E_NOPERM);
|
|
}
|
|
if (spw_file_present ())
|
|
update_shadow ();
|
|
else
|
|
update_noshadow ();
|
|
|
|
nscd_flush_cache ("passwd");
|
|
nscd_flush_cache ("group");
|
|
|
|
SYSLOG ((LOG_INFO, "password for `%s' changed by `%s'", name, myname));
|
|
closelog ();
|
|
if (!qflg) {
|
|
if (!eflg)
|
|
printf (_("Password changed.\n"));
|
|
else
|
|
printf (_("Password set to expire.\n"));
|
|
}
|
|
exit (E_SUCCESS);
|
|
/* NOT REACHED */
|
|
}
|