diff --git a/ChangeLog b/ChangeLog index a8ee0af0..4fd204df 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,11 @@ * src/usermod.c (update_group, update_gshadow): Reduce complexity and document checks. Some checks were always true/false within their call context. + * NEWS, src/usermod.c; man/usermod.8.xml: When the shadow file + exists but there are no shadow entries, an entry has to be created + if the password is changed and passwd requires a shadow entry, or + if aging features are used (-e or -f). Document this and also that + -e and -f require a shadow file. 2011-07-08 Nicolas François diff --git a/NEWS b/NEWS index f544a851..87ba9cb5 100644 --- a/NEWS +++ b/NEWS @@ -61,6 +61,9 @@ shadow-4.1.4.3 -> shadow-4.1.5 UNRELEASED this group isn't the user's primary group. - usermod * Accept options in any order (username not necessarily at the end) + * When the shadow file exists but there are no shadow entries, an entry + is created if the password is changed and passwd requires a + shadow entry, or if aging features are used (-e or -f). *** translation * Updated Brazilian Portuguese translation. diff --git a/man/usermod.8.xml b/man/usermod.8.xml index 43b18319..e1317c70 100644 --- a/man/usermod.8.xml +++ b/man/usermod.8.xml @@ -127,6 +127,11 @@ The date on which the user account will be disabled. The date is specified in the format YYYY-MM-DD. + + This option requires a /etc/shadow file. + A /etc/shadow entry will be created if + there were none. + @@ -144,6 +149,11 @@ as the password has expired, and a value of -1 disables the feature. + + This option requires a /etc/shadow file. + A /etc/shadow entry will be created if + there were none. + diff --git a/src/usermod.c b/src/usermod.c index 504ac583..d4bd5b16 100644 --- a/src/usermod.c +++ b/src/usermod.c @@ -417,7 +417,13 @@ static void new_pwent (struct passwd *pwent) pwent->pw_name, user_newname)); pwent->pw_name = xstrdup (user_newname); } - if (!is_shadow_pwd) { + /* Update the password in passwd if there is no shadow file or if + * the password is currently in passwd (pw_passwd != "x"). + * We do not force the usage of shadow passwords if they are not + * used for this account. + */ + if ( (!is_shadow_pwd) + || (strcmp (pwent->pw_passwd, SHADOW_PASSWD_STRING) != 0)) { pwent->pw_passwd = new_pw_passwd (pwent->pw_passwd); } @@ -522,12 +528,23 @@ static void new_spent (struct spwd *spent) spent->sp_namp, old_exp, new_exp)); spent->sp_expire = user_newexpire; } + + /* Always update the shadowed password if there is a shadow entry + * (even if shadowed passwords might not be enabled for this + * account (pw_passwd != "x")). + * It seems better to update the password in both places in case a + * shadow and a non shadow entry exist. + * This might occur if: + * + there were already both entries + * + aging has been requested + */ spent->sp_pwdp = new_pw_passwd (spent->sp_pwdp); + if (pflg) { spent->sp_lstchg = (long) time ((time_t *) 0) / SCALE; if (0 == spent->sp_lstchg) { /* Better disable aging than requiring a password - * change */ + * change. */ spent->sp_lstchg = -1; } } @@ -1380,13 +1397,46 @@ static void usr_update (void) new_pwent (&pwent); - /* - * Locate the entry in /etc/shadow. It doesn't have to exist, and - * won't be created if it doesn't. - */ - if (is_shadow_pwd && ((spwd = spw_locate (user_name)) != NULL)) { - spent = *spwd; - new_spent (&spent); + /* If the shadow file does not exist, it won't be created */ + if (is_shadow_pwd) { + spwd = spw_locate (user_name); + if (NULL != spwd) { + /* Update the shadow entry if it exists */ + spent = *spwd; + new_spent (&spent); + } else if ( ( pflg + && (strcmp (pwent.pw_passwd, SHADOW_PASSWD_STRING) == 0)) + || eflg || fflg) { + /* In some cases, we force the creation of a + * shadow entry: + * + new password requested and passwd indicates + * a shadowed password + * + aging information is requested + */ + memset (&spent, 0, sizeof spent); + spent.sp_namp = user_name; + + /* The user explicitly asked for a shadow feature. + * Enable shadowed passwords for this new account. + */ + spent.sp_pwdp = xstrdup (pwent.pw_passwd); + pwent.pw_passwd = xstrdup (SHADOW_PASSWD_STRING); + + spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE; + if (0 == spent.sp_lstchg) { + /* Better disable aging than + * requiring a password change */ + spent.sp_lstchg = -1; + } + spent.sp_min = getdef_num ("PASS_MIN_DAYS", -1); + spent.sp_max = getdef_num ("PASS_MAX_DAYS", -1); + spent.sp_warn = getdef_num ("PASS_WARN_AGE", -1); + spent.sp_inact = -1; + spent.sp_expire = -1; + spent.sp_flag = SHADOW_SP_FLAG_UNSET; + new_spent (&spent); + spwd = &spent; /* entry needs to be committed */ + } } if (lflg || uflg || gflg || cflg || dflg || sflg || pflg