src/passwd.c: implement reading password from pipe

New option --stdin/-t is available for root user. It is useful
for automation/setup and it makes shadow utils passwd more versatile.

Signed-off-by: Tomas Halman <tomas@halman.net>
This commit is contained in:
Tomas Halman
2024-01-25 11:00:09 +01:00
committed by Serge Hallyn
parent 3fff9d7621
commit 49001ca846
2 changed files with 92 additions and 47 deletions
+11
View File
@@ -358,6 +358,17 @@
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>-s</option>, <option>--stdin</option>
</term>
<listitem>
<para>
This option is used to indicate that passwd should read the new password from standard
input, which can be a pipe.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
+81 -47
View File
@@ -68,7 +68,8 @@ static bool
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 */
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,
@@ -158,6 +159,7 @@ usage (int status)
(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);
}
@@ -252,7 +254,7 @@ static int new_password (const struct passwd *pw)
pass_max_len = getdef_num ("PASS_MAX_LEN", 8);
}
}
if (!qflg) {
if (!qflg && !sflg) {
if (pass_max_len == -1) {
(void) printf (_(
"Enter the new password (minimum of %d characters)\n"
@@ -266,56 +268,69 @@ static int new_password (const struct passwd *pw)
}
}
warned = false;
for (i = getdef_num ("PASS_CHANGE_TRIES", 5); i > 0; i--) {
cp = agetpass (_("New password: "));
if (NULL == cp) {
MEMZERO(orig);
MEMZERO(pass);
return -1;
}
if (warned && (strcmp (pass, cp) != 0)) {
warned = false;
}
STRTCPY(pass, cp);
erase_pass (cp);
if (!amroot && !obscure(orig, pass, pw)) {
(void) puts (_("Try again."));
continue;
}
if (sflg) {
/*
* If enabled, warn about weak passwords even if you are
* root (enter this password again to use it anyway).
* --marekm
* root is setting the passphrase from stdin
*/
if (amroot && !warned && getdef_bool ("PASS_ALWAYS_WARN")
&& !obscure(orig, pass, pw)) {
(void) puts (_("\nWarning: weak password (enter it again to use it anyway)."));
warned = true;
continue;
}
cp = agetpass (_("Re-enter new password: "));
cp = agetpass_stdin ();
if (NULL == cp) {
MEMZERO(orig);
return -1;
}
STRTCPY (pass, cp);
erase_pass (cp);
} else {
warned = false;
for (i = getdef_num ("PASS_CHANGE_TRIES", 5); i > 0; i--) {
cp = agetpass (_("New password: "));
if (NULL == cp) {
MEMZERO(orig);
MEMZERO(pass);
return -1;
}
if (warned && (strcmp (pass, cp) != 0)) {
warned = false;
}
STRTCPY(pass, cp);
erase_pass (cp);
if (!amroot && !obscure(orig, pass, pw)) {
(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, pw)) {
(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(orig);
MEMZERO(pass);
return -1;
}
if (strcmp (cp, pass) != 0) {
erase_pass (cp);
(void) fputs (_("They don't match; try again.\n"), stderr);
} else {
erase_pass (cp);
break;
}
}
MEMZERO(orig);
if (i == 0) {
MEMZERO(pass);
return -1;
}
if (strcmp (cp, pass) != 0) {
erase_pass (cp);
(void) fputs (_("They don't match; try again.\n"), stderr);
} else {
erase_pass (cp);
break;
}
}
MEMZERO(orig);
if (i == 0) {
MEMZERO(pass);
return -1;
}
/*
* Encrypt the password, then wipe the cleartext password.
@@ -684,6 +699,7 @@ static void update_shadow (void)
* -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.
*
@@ -751,10 +767,11 @@ int main (int argc, char **argv)
{"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:",
while ((c = getopt_long (argc, argv, "adehi:kln:qr:R:P:Suw:x:s",
long_options, NULL)) != -1) {
switch (c) {
case 'a':
@@ -847,6 +864,15 @@ int main (int argc, char **argv)
xflg = true;
anyflag = true;
break;
case 's':
if (!amroot) {
(void) fprintf (stderr,
_("%s: only root can use --stdin/-s option\n"),
Prog);
usage (E_BAD_ARG);
}
sflg = true;
break;
default:
usage (E_BAD_ARG);
}
@@ -1038,7 +1064,16 @@ int main (int argc, char **argv)
* Don't set the real UID for PAM...
*/
if (!anyflag && use_pam) {
do_pam_passwd (name, qflg, kflg);
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 */
@@ -1072,4 +1107,3 @@ int main (int argc, char **argv)
return E_SUCCESS;
}