shadow/src/passwd.c

955 lines
24 KiB
C
Raw Normal View History

/*
* 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.
*/
2007-11-20 15:21:36 +05:30
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,
_
2007-10-27 18:16:30 +05:30
("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;
* lib/prototypes.h, configure.in, libmisc/Makefile.am, libmisc/xgetXXbyYY.c, libmisc/xgetpwnam.c, libmisc/xgetpwuid.c, libmisc/xgetgrnam.c, libmisc/xgetgrgid.c, libmisc/xgetspnam.c: Added functions xgetpwnam(), xgetpwuid(), xgetgrnam(), xgetgrgid(), and xgetspnam(). They allocate memory for the returned structure and are more robust to successive calls. They are implemented with the libc's getxxyyy_r() functions if available. * libmisc/limits.c, libmisc/entry.c, libmisc/chowntty.c, libmisc/addgrps.c, libmisc/myname.c, libmisc/rlogin.c, libmisc/pwdcheck.c, src/newgrp.c, src/login_nopam.c, src/userdel.c, src/lastlog.c, src/grpck.c, src/gpasswd.c, src/newusers.c, src/chpasswd.c, src/chfn.c, src/groupmems.c, src/usermod.c, src/expiry.c, src/groupdel.c, src/chgpasswd.c, src/su.c, src/useradd.c, src/groupmod.c, src/passwd.c, src/pwck.c, src/groupadd.c, src/chage.c, src/login.c, src/suauth.c, src/faillog.c, src/groups.c, src/chsh.c, src/id.c: Review all the usage of one of the getpwnam(), getpwuid(), getgrnam(), getgrgid(), and getspnam() functions. It was noticed on http://bugs.debian.org/341230 that chfn and chsh use a passwd structure after calling a pam function, which result in using information from the passwd structure requested by pam, not the original one. It is much easier to use the new xget... functions to avoid these issues. I've checked which call to the original get... functions could be left (reducing the scope of the structure if possible), and I've left comments to ease future reviews (e.g. /* local, no need for xgetpwnam */). Note: the getpwent/getgrent calls should probably be checked also. * src/groupdel.c, src/expiry.c: Fix typos in comments. * src/groupmod.c: Re-indent. * libmisc/Makefile.am, lib/groupmem.c, lib/groupio.c, lib/pwmem.c, lib/pwio.c, lib/shadowmem.c, lib/shadowio.c: Move the __<xx>_dup functions (used by the xget... functions) from the <xx>io.c files to the new <xx>mem.c files. This avoid linking some utils against the SELinux library.
2007-11-19 04:45:26 +05:30
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;
/*
* 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);
}
* lib/prototypes.h, configure.in, libmisc/Makefile.am, libmisc/xgetXXbyYY.c, libmisc/xgetpwnam.c, libmisc/xgetpwuid.c, libmisc/xgetgrnam.c, libmisc/xgetgrgid.c, libmisc/xgetspnam.c: Added functions xgetpwnam(), xgetpwuid(), xgetgrnam(), xgetgrgid(), and xgetspnam(). They allocate memory for the returned structure and are more robust to successive calls. They are implemented with the libc's getxxyyy_r() functions if available. * libmisc/limits.c, libmisc/entry.c, libmisc/chowntty.c, libmisc/addgrps.c, libmisc/myname.c, libmisc/rlogin.c, libmisc/pwdcheck.c, src/newgrp.c, src/login_nopam.c, src/userdel.c, src/lastlog.c, src/grpck.c, src/gpasswd.c, src/newusers.c, src/chpasswd.c, src/chfn.c, src/groupmems.c, src/usermod.c, src/expiry.c, src/groupdel.c, src/chgpasswd.c, src/su.c, src/useradd.c, src/groupmod.c, src/passwd.c, src/pwck.c, src/groupadd.c, src/chage.c, src/login.c, src/suauth.c, src/faillog.c, src/groups.c, src/chsh.c, src/id.c: Review all the usage of one of the getpwnam(), getpwuid(), getgrnam(), getgrgid(), and getspnam() functions. It was noticed on http://bugs.debian.org/341230 that chfn and chsh use a passwd structure after calling a pam function, which result in using information from the passwd structure requested by pam, not the original one. It is much easier to use the new xget... functions to avoid these issues. I've checked which call to the original get... functions could be left (reducing the scope of the structure if possible), and I've left comments to ease future reviews (e.g. /* local, no need for xgetpwnam */). Note: the getpwent/getgrent calls should probably be checked also. * src/groupdel.c, src/expiry.c: Fix typos in comments. * src/groupmod.c: Re-indent. * libmisc/Makefile.am, lib/groupmem.c, lib/groupio.c, lib/pwmem.c, lib/pwio.c, lib/shadowmem.c, lib/shadowio.c: Move the __<xx>_dup functions (used by the xget... functions) from the <xx>io.c files to the new <xx>mem.c files. This avoid linking some utils against the SELinux library.
2007-11-19 04:45:26 +05:30
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.
*/
* lib/prototypes.h, configure.in, libmisc/Makefile.am, libmisc/xgetXXbyYY.c, libmisc/xgetpwnam.c, libmisc/xgetpwuid.c, libmisc/xgetgrnam.c, libmisc/xgetgrgid.c, libmisc/xgetspnam.c: Added functions xgetpwnam(), xgetpwuid(), xgetgrnam(), xgetgrgid(), and xgetspnam(). They allocate memory for the returned structure and are more robust to successive calls. They are implemented with the libc's getxxyyy_r() functions if available. * libmisc/limits.c, libmisc/entry.c, libmisc/chowntty.c, libmisc/addgrps.c, libmisc/myname.c, libmisc/rlogin.c, libmisc/pwdcheck.c, src/newgrp.c, src/login_nopam.c, src/userdel.c, src/lastlog.c, src/grpck.c, src/gpasswd.c, src/newusers.c, src/chpasswd.c, src/chfn.c, src/groupmems.c, src/usermod.c, src/expiry.c, src/groupdel.c, src/chgpasswd.c, src/su.c, src/useradd.c, src/groupmod.c, src/passwd.c, src/pwck.c, src/groupadd.c, src/chage.c, src/login.c, src/suauth.c, src/faillog.c, src/groups.c, src/chsh.c, src/id.c: Review all the usage of one of the getpwnam(), getpwuid(), getgrnam(), getgrgid(), and getspnam() functions. It was noticed on http://bugs.debian.org/341230 that chfn and chsh use a passwd structure after calling a pam function, which result in using information from the passwd structure requested by pam, not the original one. It is much easier to use the new xget... functions to avoid these issues. I've checked which call to the original get... functions could be left (reducing the scope of the structure if possible), and I've left comments to ease future reviews (e.g. /* local, no need for xgetpwnam */). Note: the getpwent/getgrent calls should probably be checked also. * src/groupdel.c, src/expiry.c: Fix typos in comments. * src/groupmod.c: Re-indent. * libmisc/Makefile.am, lib/groupmem.c, lib/groupio.c, lib/pwmem.c, lib/pwio.c, lib/shadowmem.c, lib/shadowio.c: Move the __<xx>_dup functions (used by the xget... functions) from the <xx>io.c files to the new <xx>mem.c files. This avoid linking some utils against the SELinux library.
2007-11-19 04:45:26 +05:30
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 */
}