10
13
* 2. Redistributions in binary form must reproduce the above copyright
11
14
* notice, this list of conditions and the following disclaimer in the
12
15
* documentation and/or other materials provided with the distribution.
13
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
14
* may be used to endorse or promote products derived from this software
15
* without specific prior written permission.
16
* 3. The name of the copyright holders or contributors may not be used to
17
* endorse or promote products derived from this software without
18
* specific prior written permission.
17
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
18
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
21
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
20
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
33
#include <config.h>
32
#ident "$Id: passwd.c 1932 2008-03-26 22:00:50Z nekral-guest $"
35
#ident "$Id: passwd.c 2612 2009-04-10 22:34:23Z nekral-guest $"
71
char *Prog; /* Program name */
68
73
static char *name; /* The name of user whose password is being changed */
69
74
static char *myname; /* The current user's name */
70
static char *Prog; /* Program name */
71
static int amroot; /* The real UID was 0 */
75
static bool amroot; /* The caller's real UID was 0 */
74
aflg = 0, /* -a - show status for all users */
75
dflg = 0, /* -d - delete password */
76
eflg = 0, /* -e - force password change */
77
iflg = 0, /* -i - set inactive days */
78
kflg = 0, /* -k - change only if expired */
79
lflg = 0, /* -l - lock account */
80
nflg = 0, /* -n - set minimum days */
81
qflg = 0, /* -q - quiet mode */
82
Sflg = 0, /* -S - show password status */
83
uflg = 0, /* -u - unlock account */
84
wflg = 0, /* -w - set warning days */
85
xflg = 0; /* -x - set maximum days */
78
aflg = false, /* -a - show status for all users */
79
dflg = false, /* -d - delete password */
80
eflg = false, /* -e - force password change */
81
iflg = false, /* -i - set inactive days */
82
kflg = false, /* -k - change only if expired */
83
lflg = false, /* -l - lock the user's password */
84
nflg = false, /* -n - set minimum days */
85
qflg = false, /* -q - quiet mode */
86
Sflg = false, /* -S - show password status */
87
uflg = false, /* -u - unlock the user's password */
88
wflg = false, /* -w - set warning days */
89
xflg = false; /* -x - set maximum days */
88
92
* set to 1 if there are any flags which require root privileges,
89
93
* and require username to be specified
91
static int anyflag = 0;
95
static bool anyflag = false;
93
97
static long age_min = 0; /* Minimum days before change */
94
98
static long age_max = 0; /* Maximum days until change */
95
99
static long warn = 0; /* Warning days before change */
96
100
static long inact = 0; /* Days without change before locked */
98
static int do_update_age = 0;
103
static bool do_update_age = false;
106
static bool pw_locked = false;
107
static bool spw_locked = false;
155
168
" -k, --keep-tokens change password only if expired\n"
156
169
" -i, --inactive INACTIVE set password inactive after expiration\n"
158
" -l, --lock lock the named account\n"
171
" -l, --lock lock the password of the named account\n"
159
172
" -n, --mindays MIN_DAYS set minimum number of days before password\n"
160
173
" change to MIN_DAYS\n"
161
174
" -q, --quiet quiet mode\n"
162
175
" -r, --repository REPOSITORY change password in REPOSITORY repository\n"
163
176
" -S, --status report password status on the named account\n"
164
" -u, --unlock unlock the named account\n"
177
" -u, --unlock unlock the password of the named account\n"
165
178
" -w, --warndays WARN_DAYS set expiration warning days to WARN_DAYS\n"
166
" -x, --maxdays MAX_DAYS set maximim number of days before password\n"
179
" -x, --maxdays MAX_DAYS set maximum number of days before password\n"
167
180
" change to MAX_DAYS\n"
245
260
* for initial login passwords.
247
262
if ((method = getdef_str ("ENCRYPT_METHOD")) == NULL) {
248
if (!getdef_bool ("MD5_CRYPT_ENAB"))
263
if (!getdef_bool ("MD5_CRYPT_ENAB")) {
249
264
pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
251
if ( !strcmp (method, "MD5")
267
if ( (strcmp (method, "MD5") == 0)
252
268
#ifdef USE_SHA_CRYPT
253
|| !strcmp (method, "SHA256")
254
|| !strcmp (method, "SHA512")
269
|| (strcmp (method, "SHA256") == 0)
270
|| (strcmp (method, "SHA512") == 0)
257
273
pass_max_len = -1;
259
275
pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
262
279
if (pass_max_len == -1) {
498
541
const struct passwd *pw;
499
542
struct passwd *npw;
502
fputs (_("Cannot lock the password file; try again later.\n"),
504
SYSLOG ((LOG_WARN, "can't lock password file"));
544
if (pw_lock () == 0) {
546
_("%s: cannot lock %s; try again later.\n"),
505
548
exit (E_PWDBUSY);
507
if (!pw_open (O_RDWR)) {
508
fputs (_("Cannot open the password file.\n"), stderr);
509
SYSLOG ((LOG_ERR, "can't open password file"));
551
if (pw_open (O_RDWR) == 0) {
553
_("%s: cannot open %s\n"),
555
SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ()));
510
556
fail_exit (E_MISSING);
512
558
pw = pw_locate (name);
514
fprintf (stderr, _("%s: %s not found in /etc/passwd\n"),
561
_("%s: user '%s' does not exist in %s\n"),
562
Prog, name, pw_dbname ());
516
563
fail_exit (E_NOPERM);
518
565
npw = __pw_dup (pw);
521
569
npw->pw_passwd = update_crypt_pw (npw->pw_passwd);
522
if (!pw_update (npw)) {
523
fputs (_("Error updating the password entry.\n"), stderr);
524
SYSLOG ((LOG_ERR, "error updating password entry"));
525
fail_exit (E_FAILURE);
528
fputs (_("Cannot commit password file changes.\n"), stderr);
529
SYSLOG ((LOG_ERR, "can't rewrite password file"));
530
fail_exit (E_FAILURE);
570
if (pw_update (npw) == 0) {
572
_("%s: failed to prepare the new %s entry '%s'\n"),
573
Prog, pw_dbname (), npw->pw_name);
574
fail_exit (E_FAILURE);
576
if (pw_close () == 0) {
578
_("%s: failure while writing changes to %s\n"),
580
SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
581
fail_exit (E_FAILURE);
583
if (pw_unlock () == 0) {
584
fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
585
SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
535
591
static void update_shadow (void)
537
593
const struct spwd *sp;
538
594
struct spwd *nsp;
541
fputs (_("Cannot lock the password file; try again later.\n"),
543
SYSLOG ((LOG_WARN, "can't lock password file"));
596
if (spw_lock () == 0) {
598
_("%s: cannot lock %s; try again later.\n"),
599
Prog, spw_dbname ());
544
600
exit (E_PWDBUSY);
546
if (!spw_open (O_RDWR)) {
547
fputs (_("Cannot open the password file.\n"), stderr);
548
SYSLOG ((LOG_ERR, "can't open password file"));
603
if (spw_open (O_RDWR) == 0) {
604
fprintf (stderr, _("%s: cannot open %s\n"), Prog, spw_dbname ());
605
SYSLOG ((LOG_WARN, "cannot open %s", spw_dbname ()));
549
606
fail_exit (E_FAILURE);
551
608
sp = spw_locate (name);
553
610
/* Try to update the password in /etc/passwd instead. */
555
612
update_noshadow ();
613
if (spw_unlock () == 0) {
614
fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
615
SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
559
621
nsp = __spw_dup (sp);
562
625
nsp->sp_pwdp = update_crypt_pw (nsp->sp_pwdp);
564
627
nsp->sp_max = (age_max * DAY) / SCALE;
566
630
nsp->sp_min = (age_min * DAY) / SCALE;
568
633
nsp->sp_warn = (warn * DAY) / SCALE;
570
636
nsp->sp_inact = (inact * DAY) / SCALE;
572
nsp->sp_lstchg = time ((time_t *) 0) / SCALE;
574
/* Set the account expiry field to 1.
575
* Some PAM implementation consider zero as a non expired
640
nsp->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
641
if (0 == nsp->sp_lstchg) {
642
/* Better disable aging than requiring a password
584
650
* Force change on next login, like SunOS 4.x passwd -e or Solaris
585
651
* 2.x passwd -f. Solaris 2.x seems to do the same thing (set
586
652
* sp_lstchg to 0).
589
655
nsp->sp_lstchg = 0;
591
if (!spw_update (nsp)) {
592
fputs (_("Error updating the password entry.\n"), stderr);
593
SYSLOG ((LOG_ERR, "error updating password entry"));
594
fail_exit (E_FAILURE);
597
fputs (_("Cannot commit password file changes.\n"), stderr);
598
SYSLOG ((LOG_ERR, "can't rewrite password file"));
599
fail_exit (E_FAILURE);
604
static long getnumber (const char *numstr)
609
val = strtol (numstr, &errptr, 10);
610
if (*errptr || errno == ERANGE) {
611
fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog,
658
if (spw_update (nsp) == 0) {
660
_("%s: failed to prepare the new %s entry '%s'\n"),
661
Prog, spw_dbname (), nsp->sp_namp);
662
fail_exit (E_FAILURE);
664
if (spw_close () == 0) {
666
_("%s: failure while writing changes to %s\n"),
667
Prog, spw_dbname ());
668
SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ()));
669
fail_exit (E_FAILURE);
671
if (spw_unlock () == 0) {
672
fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
673
SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
618
679
#ifdef WITH_SELINUX
620
check_selinux_access(const char *change_user, int change_uid, unsigned int access)
680
static int check_selinux_access (const char *changed_user,
682
access_vector_t requested_access)
623
685
security_context_t user_context;
624
687
const char *user;
626
689
/* if in permissive mode then allow the operation */
627
if (security_getenforce() == 0)
690
if (security_getenforce() == 0) {
630
694
/* get the context of the process which executed passwd */
631
if (getprevcon(&user_context))
695
if (getprevcon(&user_context) != 0) {
634
699
/* get the "user" portion of the context (the part before the first
637
701
c = context_new(user_context);
638
702
user = context_user_get(c);
640
704
/* if changing a password for an account with UID==0 or for an account
641
705
where the identity matches then return success */
642
if (change_uid != 0 && strcmp(change_user, user) == 0) {
706
if (changed_uid != 0 && strcmp(changed_user, user) == 0) {
645
709
struct av_decision avd;
647
retval = security_compute_av(user_context, user_context,
648
SECCLASS_PASSWD, access, &avd);
711
retval = security_compute_av(user_context,
649
716
if ((retval == 0) &&
650
((access & avd.allowed) == access)) {
717
((requested_access & avd.allowed) == requested_access)) {
746
813
long_options, &option_index)) != -1) {
760
inact = getnumber (optarg);
827
if ( (getlong (optarg, &inact) == 0)
830
_("%s: invalid numeric argument '%s'\n"),
766
838
/* change only if expired, like Linux-PAM passwd -k. */
767
kflg++; /* ok for users */
839
kflg = true; /* ok for users */
774
age_min = getnumber (optarg);
846
if ( (getlong (optarg, &age_min) == 0)
849
_("%s: invalid numeric argument '%s'\n"),
779
qflg++; /* ok for users */
857
qflg = true; /* ok for users */
782
860
/* -r repository (files|nis|nisplus) */
783
861
/* only "files" supported for now */
784
862
if (strcmp (optarg, "files") != 0) {
787
("%s: repository %s not supported\n"),
864
_("%s: repository %s not supported\n"),
789
866
exit (E_BAD_ARG);
793
Sflg++; /* ok for users */
870
Sflg = true; /* ok for users */
800
warn = getnumber (optarg);
877
if ( (getlong (optarg, &warn) == 0)
880
_("%s: invalid numeric argument '%s'\n"),
806
age_max = getnumber (optarg);
888
if ( (getlong (optarg, &age_max) == 0)
891
_("%s: invalid numeric argument '%s'\n"),
811
899
usage (E_BAD_ARG);
821
909
pw = get_my_pwent ();
824
_("%s: Cannot determine your user name.\n"), Prog);
912
_("%s: Cannot determine your user name.\n"), Prog);
913
SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)",
914
(unsigned long) getuid ()));
827
917
myname = xstrdup (pw->pw_name);
829
919
name = argv[optind];
834
925
* Make sure that at most one username was specified.
927
if (argc > (optind+1)) {
840
932
* The -a flag requires -S, no other flags, no username, and
841
933
* you must be root. --marekm
844
if (anyflag || !Sflg || (optind < argc))
936
if (anyflag || !Sflg || (optind < argc)) {
847
940
fprintf (stderr, _("%s: Permission denied.\n"), Prog);
851
while ((pw = getpwent ()))
944
while ( (pw = getpwent ()) != NULL ) {
852
945
print_status (pw);
853
948
exit (E_SUCCESS);
886
984
pw = xgetpwnam (name);
888
fprintf (stderr, _("%s: unknown user %s\n"), Prog, name);
986
fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, name);
891
989
#ifdef WITH_SELINUX
892
990
/* only do this check when getuid()==0 because it's a pre-condition for
893
991
changing a password without entering the old one */
894
992
if ((is_selinux_enabled() > 0) && (getuid() == 0) &&
895
(check_selinux_access(name, pw->pw_uid, PASSWD__PASSWD) != 0))
993
(check_selinux_access (name, pw->pw_uid, PASSWD__PASSWD) != 0)) {
897
994
security_context_t user_context;
898
995
if (getprevcon(&user_context) < 0) {
899
996
user_context = strdup("Unknown user context");
901
998
syslog(LOG_ALERT,
902
"%s is not authorized to change the password of %s",
904
fprintf(stderr, _("%s: %s is not authorized to change the "
906
Prog, user_context, name);
999
"%s is not authorized to change the password of %s",
1000
user_context, name);
1002
_("%s: %s is not authorized to change the password of %s\n"),
1003
Prog, user_context, name);
907
1004
freecon(user_context);
1007
#endif /* WITH_SELINUX */
914
1010
* If the UID of the user does not match the current real UID,
915
1011
* check if I'm root.
917
if (!amroot && pw->pw_uid != getuid ()) {
1013
if (!amroot && (pw->pw_uid != getuid ())) {
918
1014
fprintf (stderr,
920
("%s: You may not view or modify password information for %s.\n"),
1015
_("%s: You may not view or modify password information for %s.\n"),
922
1017
SYSLOG ((LOG_WARN,
923
1018
"%s: can't view or modify password information for %s",