/
niceSOFT
/
shadow
Обзор
Документация
Войти
/
niceSOFT
/
shadow
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/passwd.c
1 131 строка
29 KB
Alejandro Colomar
lib/, src/: Use eprinte() instead of its pattern
20 июл 2026, 16:43
20 июл 2026, 16:43
10c5a20
Код
Авторство
О чём код?
/* * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh * SPDX-FileCopyrightText: 1996 - 2000, Marek Michałkiewicz * SPDX-FileCopyrightText: 2001 - 2006, Tomasz Kłoczko * SPDX-FileCopyrightText: 2007 - 2011, Nicolas François * * SPDX-License-Identifier: BSD-3-Clause */ #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> #include <time.h> #include "agetpass.h" #include "atoi/a2i.h" #include "chkname.h" #include "defines.h" #include "getdef.h" #include "io/fprintf.h" #include "nscd.h" #include "prototypes.h" #include "pwauth.h" #include "pwio.h" #include "shadowio.h" #include "shadowlog.h" #include "sssd.h" #include "string/memset/memzero.h" #include "string/sprintf/aprintf.h" #include "string/sprintf/stprintf.h" #include "string/strcmp/streq.h" #include "string/strcmp/strprefix.h" #include "string/strcpy/strtcpy.h" #include "string/strdup/strdup.h" #include "time/day_to_str.h" /* * exit status values */ /*@-exitarg@*/ #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 */ #define E_PAM_ERR 10 /* PAM returned an error */ struct option_flags { bool chroot; bool prefix; }; /* * Global variables */ static const char Prog[] = "passwd"; /* Program name */ static char *name; /* The name of user whose password is being changed */ static char *myname; /* The current user's name */ static bool amroot; /* The caller's real UID was 0 */ static const char *prefix = ""; static bool aflg = false, /* -a - show status for all users */ dflg = false, /* -d - delete password */ eflg = false, /* -e - force password change */ iflg = false, /* -i - set inactive days */ kflg = false, /* -k - change only if expired */ lflg = false, /* -l - lock the user's password */ qflg = false, /* -q - quiet mode */ Sflg = false, /* -S - show password status */ uflg = false, /* -u - unlock the user's password */ wflg = false, /* -w - set warning days */ xflg = false, /* -x - set maximum days */ sflg = false; /* -s - read passwd from stdin */ /* * set to 1 if there are any flags which require root privileges, * and require username to be specified */ static bool anyflag = false; 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 bool do_update_age = false; #ifdef USE_PAM static bool use_pam = true; #else static bool use_pam = false; #endif /* USE_PAM */ static bool pw_locked = false; static bool spw_locked = false; /* * 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 bool do_update_pwd = false; /* * External identifiers */ /* local function prototypes */ NORETURN static void usage(int); static int new_password(const struct passwd *); static void check_password(const struct passwd *, const struct spwd *, bool); static /*@observer@*/const char *pw_status(const char *); static void print_status(const struct passwd *); NORETURN static void fail_exit(int, bool); NORETURN static void oom(bool); static char *update_crypt_pw(char *, bool); static void update_noshadow(bool); static void update_shadow(bool); /* * usage - print command usage and exit */ NORETURN static void usage (int status) { FILE *usageout = (E_SUCCESS != status) ? stderr : stdout; (void) fprintf (usageout, _("Usage: %s [options] [LOGIN]\n" "\n" "Options:\n"), Prog); (void) fputs (_(" -a, --all report password status on all accounts\n"), usageout); (void) fputs (_(" -d, --delete delete the password for the named account\n"), usageout); (void) fputs (_(" -e, --expire force expire the password for the named account\n"), usageout); (void) fputs (_(" -h, --help display this help message and exit\n"), usageout); (void) fputs (_(" -k, --keep-tokens change password only if expired\n"), usageout); (void) fputs (_(" -i, --inactive INACTIVE set password inactive after expiration\n" " to INACTIVE\n"), usageout); (void) fputs (_(" -l, --lock lock the password of the named account\n"), usageout); (void) fputs (_(" -q, --quiet quiet mode\n"), usageout); (void) fputs (_(" -r, --repository REPOSITORY change password in REPOSITORY repository\n"), usageout); (void) fputs (_(" -R, --root CHROOT_DIR directory to chroot into\n"), usageout); (void) fputs (_(" -P, --prefix PREFIX_DIR directory prefix\n"), usageout); (void) fputs (_(" -S, --status report password status on the named account\n"), usageout); (void) fputs (_(" -u, --unlock unlock the password of the named account\n"), usageout); (void) fputs (_(" -w, --warndays WARN_DAYS set expiration warning days to WARN_DAYS\n"), usageout); (void) fputs (_(" -x, --maxdays MAX_DAYS set maximum number of days before password\n" " change to MAX_DAYS\n"), usageout); (void) fputs (_(" -s, --stdin read new token from stdin\n"), usageout); (void) fputs ("\n", usageout); exit (status); } /* * 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 */ const char *salt; /* Pointer to new salt */ char *cp; /* Pointer to agetpass() response */ char orig[PASS_MAX + 1]; /* Original password */ char pass[PASS_MAX + 1]; /* New password */ int i; /* Counter for retries */ int ret; bool warned; /* * Authenticate the user. The user will be prompted for their own * password. */ if (!amroot && !streq(crypt_passwd, "")) { clear = agetpass (_("Old password: ")); if (NULL == clear) { return -1; } cipher = pw_encrypt (clear, crypt_passwd); if (NULL == cipher) { erase_pass (clear); eprinte(_("%s: failed to crypt password with previous salt"), Prog); SYSLOG(LOG_INFO, "Failed to crypt password with previous salt of user '%s'", pw->pw_name); return -1; } if (!streq(cipher, crypt_passwd)) { erase_pass (clear); strzero (cipher); SYSLOG(LOG_WARN, "incorrect password for %s", pw->pw_name); (void) sleep (1); (void) eprintf(_("Incorrect password for %s.\n"), pw->pw_name); return -1; } strtcpy_a(orig, clear); erase_pass (clear); strzero (cipher); } else { strcpy(orig, ""); } /* * Get the new password. The user is prompted for the new password * and has PASS_CHANGE_TRIES tries to get it right. The password will * be optionally tested for strength. The root user can circumvent * tests. This provides an escape for initial login passwords. */ if (!qflg && !sflg) { printf(_( "Enter the new password (minimum of %zu characters)\n" "Please use a combination of upper and lower case letters and numbers.\n"), pass_min_len()); } if (sflg) { /* * root is setting the passphrase from stdin */ cp = agetpass_stdin (); if (NULL == cp) { return -1; } ret = strtcpy_a(pass, cp); erase_pass (cp); if (ret == -1) { (void) fputs (_("Password is too long.\n"), stderr); memzero_a(pass); return -1; } } else { warned = false; for (i = getdef_num ("PASS_CHANGE_TRIES", 5); i > 0; i--) { cp = agetpass (_("New password: ")); if (NULL == cp) { memzero_a(orig); memzero_a(pass); return -1; } if (warned && !streq(pass, cp)) { warned = false; } ret = strtcpy_a(pass, cp); erase_pass (cp); if (ret == -1) { (void) fputs (_("Password is too long.\n"), stderr); memzero_a(orig); memzero_a(pass); return -1; } if (!amroot && !obscure(orig, pass)) { (void) puts (_("Try again.")); 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)) { (void) puts (_("\nWarning: weak password (enter it again to use it anyway).")); warned = true; continue; } cp = agetpass (_("Re-enter new password: ")); if (NULL == cp) { memzero_a(orig); memzero_a(pass); return -1; } if (!streq(cp, pass)) { erase_pass (cp); (void) fputs (_("They don't match; try again.\n"), stderr); } else { erase_pass (cp); break; } } memzero_a(orig); if (i == 0) { memzero_a(pass); return -1; } } /* * Encrypt the password, then wipe the cleartext password. */ salt = crypt_make_salt (NULL, NULL); cp = pw_encrypt (pass, salt); memzero_a(pass); if (NULL == cp) { eprinte(_("%s: failed to crypt password with salt '%s'"), Prog, salt); return -1; } strtcpy_a(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, bool process_selinux) { 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 && (0 == exp_status)) { fail_exit(E_SUCCESS, process_selinux); } /* * Root can change any password any time. */ if (amroot) { return; } /* * Expired accounts cannot be changed ever. Passwords which are * locked may not be changed. * Passwords which have been inactive too long cannot be changed. */ if ( strprefix(sp->sp_pwdp, "!") || (exp_status > 1)) { eprintf(_("The password for %s cannot be changed.\n"), sp->sp_namp); SYSLOG(LOG_WARN, "password locked for '%s'", sp->sp_namp); closelog (); fail_exit(E_NOPERM, process_selinux); } } static /*@observer@*/const char *pw_status (const char *pass) { if (strprefix(pass, "*") || strprefix(pass, "!")) { return "L"; } if (streq(pass, "")) { return "NP"; } return "P"; } /* * print_status - print current password status */ static void print_status (const struct passwd *pw) { char date[80]; struct spwd *sp; sp = prefix_getspnam (pw->pw_name); /* local, no need for xprefix_getspnam */ if (NULL != sp) { day_to_str_a(date, sp->sp_lstchg); (void) printf ("%s %s %s -1 %ld %ld %ld\n", pw->pw_name, pw_status (sp->sp_pwdp), date, sp->sp_max, sp->sp_warn, sp->sp_inact); } else if (NULL != pw->pw_passwd) { (void) printf ("%s %s\n", pw->pw_name, pw_status (pw->pw_passwd)); } else { (void) eprintf(_("%s: malformed password data obtained for user %s\n"), Prog, pw->pw_name); } } NORETURN static void fail_exit (int status, bool process_selinux) { if (spw_locked) { if (spw_unlock (process_selinux) == 0) { eprintf(_("%s: failed to unlock %s\n"), Prog, spw_dbname()); SYSLOG(LOG_ERR, "failed to unlock %s", spw_dbname()); /* continue */ } } if (pw_locked) { if (pw_unlock (process_selinux) == 0) { eprintf(_("%s: failed to unlock %s\n"), Prog, pw_dbname()); SYSLOG(LOG_ERR, "failed to unlock %s", pw_dbname()); /* continue */ } } exit (status); } NORETURN static void oom (bool process_selinux) { eprintf(_("%s: out of memory\n"), Prog); fail_exit (E_FAILURE, process_selinux); } /* * open_files - lock and open the password files * * open_files() opens password files if available. */ static void open_files(bool process_selinux) { if (pw_lock () == 0) { eprintf(_("%s: cannot lock %s; try again later.\n"), Prog, pw_dbname()); exit (E_PWDBUSY); } pw_locked = true; if (pw_open (O_CREAT | O_RDWR) == 0) { eprintf(_("%s: cannot open %s\n"), Prog, pw_dbname()); SYSLOG(LOG_WARN, "cannot open %s", pw_dbname()); fail_exit (E_MISSING, process_selinux); } if (!spw_file_present ()) return; if (spw_lock () == 0) { eprintf(_("%s: cannot lock %s; try again later.\n"), Prog, spw_dbname()); fail_exit (E_PWDBUSY, process_selinux); } spw_locked = true; if (spw_open (O_CREAT | O_RDWR) == 0) { eprintf(_("%s: cannot open %s\n"), Prog, spw_dbname()); SYSLOG(LOG_WARN, "cannot open %s", spw_dbname()); fail_exit (E_FAILURE, process_selinux); } } /* * close_files - close all of the files that were opened * * close_files() closes all of the files that were opened for this * user. This causes a possibly modified entry to be written out. */ static void close_files(bool process_selinux) { if (spw_locked) { if (spw_close (process_selinux) == 0) { eprintf(_("%s: failure while writing changes to %s\n"), Prog, spw_dbname()); SYSLOG(LOG_ERR, "failure while writing changes to %s", spw_dbname()); fail_exit (E_FAILURE, process_selinux); } if (spw_unlock (process_selinux) == 0) { eprintf(_("%s: failed to unlock %s\n"), Prog, spw_dbname()); SYSLOG(LOG_ERR, "failed to unlock %s", spw_dbname()); /* continue */ } spw_locked = false; } if (pw_close (process_selinux) == 0) { eprintf(_("%s: failure while writing changes to %s\n"), Prog, pw_dbname()); SYSLOG(LOG_ERR, "failure while writing changes to %s", pw_dbname()); fail_exit (E_FAILURE, process_selinux); } if (pw_unlock (process_selinux) == 0) { eprintf(_("%s: failed to unlock %s\n"), Prog, pw_dbname()); SYSLOG(LOG_ERR, "failed to unlock %s", pw_dbname()); /* continue */ } pw_locked = false; } static char *update_crypt_pw (char *cp, bool process_selinux) { if (!use_pam) { if (do_update_pwd) { cp = xstrdup (crypt_passwd); } } if (dflg) strcpy(cp, ""); if (uflg && strprefix(cp, "!")) { if (cp[1] == '\0') { (void) eprintf(_("%s: unlocking the password would result in a passwordless account.\n" "You should set a password with usermod -p to unlock the password of this account.\n"), Prog); fail_exit (E_FAILURE, process_selinux); } else { cp++; } } if (lflg && *cp != '!') { char *newpw; newpw = xaprintf("!%s", cp); if (!use_pam) { if (do_update_pwd) { free (cp); } } cp = newpw; } return cp; } static void update_noshadow(bool process_selinux) { const struct passwd *pw; struct passwd *npw; int ret; pw = pw_locate (name); if (NULL == pw) { (void) eprintf(_("%s: user '%s' does not exist in %s\n"), Prog, name, pw_dbname ()); fail_exit (E_NOPERM, process_selinux); } check_password(pw, pwd_to_spwd(pw), process_selinux); npw = __pw_dup (pw); if (NULL == npw) { oom (process_selinux); } npw->pw_passwd = update_crypt_pw (npw->pw_passwd, process_selinux); ret = pw_update(npw); #ifdef WITH_AUDIT if (lflg) { audit_logger(AUDIT_ACCT_LOCK, "locked-password", NULL, pw->pw_uid, ret ? SHADOW_AUDIT_SUCCESS : SHADOW_AUDIT_FAILURE); } if (uflg) { audit_logger(AUDIT_ACCT_UNLOCK, "unlocked-password", NULL, pw->pw_uid, ret ? SHADOW_AUDIT_SUCCESS : SHADOW_AUDIT_FAILURE); } if (dflg) { audit_logger(AUDIT_USER_CHAUTHTOK, "deleted-password", NULL, pw->pw_uid, ret ? SHADOW_AUDIT_SUCCESS : SHADOW_AUDIT_FAILURE); } #endif /* WITH_AUDIT */ if (ret == 0) { (void) eprintf(_("%s: failed to prepare the new %s entry '%s'\n"), Prog, pw_dbname (), npw->pw_name); fail_exit (E_FAILURE, process_selinux); } } static void update_shadow(bool process_selinux) { const struct passwd *pw; const struct spwd *sp; struct spwd *nsp; int ret; pw = pw_locate(name); if (NULL == pw) { fprintf(stderr, _("%s: user '%s' does not exist in %s\n"), Prog, name, pw_dbname ()); fail_exit (E_NOPERM, process_selinux); } sp = spw_locked ? spw_locate(name) : NULL; if (NULL == sp) { /* Try to update the password in /etc/passwd instead. */ update_noshadow (process_selinux); return; } check_password(pw, sp, process_selinux); nsp = __spw_dup (sp); if (NULL == nsp) { oom (process_selinux); } nsp->sp_pwdp = update_crypt_pw (nsp->sp_pwdp, process_selinux); if (xflg) { nsp->sp_max = age_max; } if (wflg) { nsp->sp_warn = warn; } if (iflg) { nsp->sp_inact = inact; } if (!use_pam) { if (do_update_age) { nsp->sp_lstchg = gettime () / DAY; if (0 == nsp->sp_lstchg) { /* Better disable aging than requiring a password * change */ nsp->sp_lstchg = -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; } ret = spw_update(nsp); #ifdef WITH_AUDIT if (lflg) { audit_logger(AUDIT_ACCT_LOCK, "locked-password", NULL, pw->pw_uid, ret ? SHADOW_AUDIT_SUCCESS : SHADOW_AUDIT_FAILURE); } if (uflg) { audit_logger(AUDIT_ACCT_UNLOCK, "unlocked-password", NULL, pw->pw_uid, ret ? SHADOW_AUDIT_SUCCESS : SHADOW_AUDIT_FAILURE); } if (dflg) { audit_logger(AUDIT_USER_CHAUTHTOK, "deleted-password", NULL, pw->pw_uid, ret ? SHADOW_AUDIT_SUCCESS : SHADOW_AUDIT_FAILURE); } if (eflg) { audit_logger(AUDIT_USER_MGMT, "expired-password", NULL, pw->pw_uid, ret ? SHADOW_AUDIT_SUCCESS : SHADOW_AUDIT_FAILURE); } /* Audit aging parameter changes if any were modified */ if (xflg || wflg || iflg) { char aging_msg[256]; stprintf_a(aging_msg, "changed-password-aging min=%ld max=%ld warn=%ld inact=%ld", nsp->sp_min, nsp->sp_max, nsp->sp_warn, nsp->sp_inact); audit_logger(AUDIT_USER_MGMT, aging_msg, NULL, pw->pw_uid, ret ? SHADOW_AUDIT_SUCCESS : SHADOW_AUDIT_FAILURE); } #endif /* WITH_AUDIT */ if (ret == 0) { (void) eprintf(_("%s: failed to prepare the new %s entry '%s'\n"), Prog, spw_dbname (), nsp->sp_namp); fail_exit (E_FAILURE, process_selinux); } } /* * 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 (*) * -i # set sp_inact to # days (*) * -k change password only if expired * -l lock the password of the named account (*) * -r # change password in # repository * -S show password status of named account * -u unlock the password of the named account (*) * -w # set sp_warn to # days (*) * -x # set sp_max to # days (*) * -s read password from stdin (*) * * (*) 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 */ char *cp; /* Miscellaneous character pointing */ const struct spwd *sp; /* Shadow file entry for user */ struct option_flags flags = {.chroot = false, .prefix = false}; bool process_selinux; sanitize_env (); check_fds (); log_set_progname(Prog); log_set_logfd(stderr); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); prefix = process_prefix_flag ("-P", argc, argv); if (prefix[0]) { use_pam = false; do_update_age = true; } #ifdef WITH_AUDIT audit_help_open(); #endif /* WITH_AUDIT */ /* * The program behaves differently when executed by root than when * executed by a normal user. */ amroot = (getuid () == 0); OPENLOG (Prog); { /* * Parse the command line options. */ 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'}, {"quiet", no_argument, NULL, 'q'}, {"repository", required_argument, NULL, 'r'}, {"root", required_argument, NULL, 'R'}, {"prefix", required_argument, NULL, 'P'}, {"status", no_argument, NULL, 'S'}, {"unlock", no_argument, NULL, 'u'}, {"warndays", required_argument, NULL, 'w'}, {"maxdays", required_argument, NULL, 'x'}, {"stdin", no_argument, NULL, 's'}, {NULL, 0, NULL, '\0'} }; while ((c = getopt_long (argc, argv, "adehi:kln:qr:R:P:Suw:x:s", long_options, NULL)) != -1) { switch (c) { case 'a': aflg = true; break; case 'd': dflg = true; anyflag = true; break; case 'e': eflg = true; anyflag = true; break; case 'h': usage (E_SUCCESS); /*@notreached@*/break; case 'i': if (a2sl(&inact, optarg, NULL, 0, -1, LONG_MAX) == -1) { eprintf(_("%s: invalid numeric argument '%s'\n"), Prog, optarg); usage (E_BAD_ARG); } iflg = true; anyflag = true; break; case 'k': /* change only if expired, like Linux-PAM passwd -k. */ kflg = true; /* ok for users */ break; case 'l': lflg = true; anyflag = true; break; case 'q': qflg = true; /* ok for users */ break; case 'r': /* -r repository (files|nis|nisplus) */ /* only "files" supported for now */ if (!streq(optarg, "files")) { eprintf(_("%s: repository %s not supported\n"), Prog, optarg); exit (E_BAD_ARG); } break; case 'R': /* no-op, handled in process_root_flag () */ flags.chroot = true; break; case 'P': /* no-op, handled in process_prefix_flag () */ flags.prefix = true; break; case 'S': Sflg = true; /* ok for users */ break; case 'u': uflg = true; anyflag = true; break; case 'w': if (a2sl(&warn, optarg, NULL, 0, -1, LONG_MAX) == -1) { (void) eprintf(_("%s: invalid numeric argument '%s'\n"), Prog, optarg); usage (E_BAD_ARG); } wflg = true; anyflag = true; break; case 'x': if (a2sl(&age_max, optarg, NULL, 0, -1, LONG_MAX) == -1) { (void) eprintf(_("%s: invalid numeric argument '%s'\n"), Prog, optarg); usage (E_BAD_ARG); } xflg = true; anyflag = true; break; case 's': if (!amroot) { (void) eprintf(_("%s: only root can use --stdin/-s option\n"), Prog); usage (E_BAD_ARG); } sflg = true; break; default: usage (E_BAD_ARG); } } } process_selinux = !flags.chroot && !flags.prefix; /* * 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 (NULL == pw) { (void) eprintf(_("%s: Cannot determine your user name.\n"), Prog); SYSLOG(LOG_WARN, "Cannot determine the user name of the caller (UID %lu)", (unsigned long) getuid()); exit (E_NOPERM); } myname = xstrdup (pw->pw_name); if (optind < argc) { if (!is_valid_user_name(argv[optind], true) && !is_valid_upn(argv[optind], true)) { eprintf(_("%s: Provided user name is not a valid name\n"), Prog); fail_exit (E_NOPERM, process_selinux); } 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) { (void) eprintf(_("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } prefix_setpwent (); while ( (pw = prefix_getpwent ()) != NULL ) { print_status (pw); } prefix_endpwent (); 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 ( (Sflg && kflg) || (anyflag && (Sflg || kflg))) { usage (E_USAGE); } if (anyflag && !amroot) { #ifdef WITH_AUDIT audit_logger(AUDIT_USER_CHAUTHTOK, "attempted-to-change-password-attribute", NULL, getuid(), SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ (void) eprintf(_("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } pw = xprefix_getpwnam (name); if (NULL == pw) { (void) eprintf(_("%s: user '%s' does not exist\n"), Prog, name); exit (E_NOPERM); } #ifdef WITH_SELINUX /* only do this check when getuid()==0 because it's a pre-condition for changing a password without entering the old one */ if (amroot && (check_selinux_permit (Prog) != 0)) { #ifdef WITH_AUDIT audit_logger(AUDIT_USER_CHAUTHTOK, "attempted-to-change-password", NULL, pw->pw_uid, SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ SYSLOG(LOG_ALERT, "root is not authorized by SELinux to change the password of %s", name); (void) eprintf(_("%s: root is not authorized by SELinux to change the password of %s\n"), Prog, name); exit (E_NOPERM); } #endif /* WITH_SELINUX */ /* * If the UID of the user does not match the current real UID, * check if I'm root. */ if (!amroot && (pw->pw_uid != getuid ())) { #ifdef WITH_AUDIT audit_logger(AUDIT_USER_CHAUTHTOK, "attempted-to-change-password", NULL, pw->pw_uid, SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ (void) eprintf(_("%s: You may not view or modify password information for %s.\n"), Prog, name); SYSLOG(LOG_WARN, "can't view or modify password information for %s", name); closelog (); exit (E_NOPERM); } if (Sflg) { print_status (pw); exit (E_SUCCESS); } if (!use_pam) { /* * The user name is valid, so let's get the shadow file entry. */ sp = prefix_getspnam (name); /* !use_pam, no need for xprefix_getspnam */ if (NULL == sp) { if (errno == EACCES) { (void) eprintf(_("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } sp = pwd_to_spwd (pw); } cp = sp->sp_pwdp; /* * If there are no other flags, just change the password. */ if (!anyflag) { strtcpy_a(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, process_selinux); /* * Let the user know whose password is being changed. */ if (!qflg) { (void) printf (_("Changing password for %s\n"), name); } if (new_password (pw) != 0) { (void) eprintf(_("The password for %s is unchanged.\n"), name); closelog (); exit (E_NOPERM); } do_update_pwd = true; do_update_age = true; } } /* * 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 && use_pam) { if (sflg) { cp = agetpass_stdin (); if (cp == NULL) { exit (E_FAILURE); } do_pam_passwd_non_interactive ("passwd", name, cp); erase_pass (cp); } else { do_pam_passwd (name, qflg, kflg); } exit (E_SUCCESS); } #endif /* USE_PAM */ if (setuid (0) != 0) { (void) fputs (_("Cannot change ID to root.\n"), stderr); SYSLOG(LOG_ERR, "can't setuid(0)"); closelog (); exit (E_NOPERM); } open_files(process_selinux); update_shadow(process_selinux); close_files(process_selinux); nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); SYSLOG(LOG_INFO, "password for '%s' changed by '%s'", name, myname); closelog (); if (!qflg) { if (!anyflag) { #ifndef USE_PAM (void) printf (_("%s: password changed.\n"), Prog); #endif /* USE_PAM */ } else { (void) printf (_("%s: password changed.\n"), Prog); } } return E_SUCCESS; }