Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20180704233216.GD29590@altlinux.org>
Date: Thu, 5 Jul 2018 02:32:16 +0300
From: "Dmitry V. Levin" <ldv@...linux.org>
To: owl-dev@...ts.openwall.com
Subject: [PATCH 3/5] pam_tcb: use pam_get_authtok(3) instead of
 _unix_read_password

This follows the change implemented in Linux-PAM commit Linux-PAM-1.3.0~5.

pam_get_authtok(3) was introduced in Linux-PAM by commit
Linux-PAM-1_0_90~4 back in 2008.

As pam_get_authtok(3) does not support not_set_pass option,
the support for this not much useful option is dropped.
Instead we get a proper support for authtok_type= option.
---
 pam_tcb/pam_tcb.8         |   6 ++-
 pam_tcb/pam_unix_auth.c   |   4 +-
 pam_tcb/pam_unix_passwd.c |  83 +++++++---------------------
 pam_tcb/support.c         | 134 ++--------------------------------------------
 pam_tcb/support.h         |  30 ++---------
 5 files changed, 32 insertions(+), 225 deletions(-)

diff --git a/pam_tcb/pam_tcb.8 b/pam_tcb/pam_tcb.8
index 38a338c..0c6ed3c 100644
--- a/pam_tcb/pam_tcb.8
+++ b/pam_tcb/pam_tcb.8
@@ -171,8 +171,10 @@ but applies to the (new)
 .B PAM_AUTHTOK
 only.  This is intended for stacking password management modules.
 .TP
-.B not_set_pass
-Don't set the PAM items with passwords used by this module.
+.BR authtok_type =
+This option can be used to modify the password prompt
+when changing passwords to include the type of the password.
+The default is empty.
 .TP
 .B likeauth
 When called as a credential setting module, return the same value as
diff --git a/pam_tcb/pam_unix_auth.c b/pam_tcb/pam_unix_auth.c
index 9740870..8857daf 100644
--- a/pam_tcb/pam_unix_auth.c
+++ b/pam_tcb/pam_unix_auth.c
@@ -12,7 +12,6 @@
 #include "attribute.h"
 #include "support.h"
 
-#define DATA_AUTHTOK			"-UN*X-PASS"
 #define DATA_AUTH_RETVAL		"-UN*X-AUTH-RETVAL"
 
 static void retval_cleanup(unused pam_handle_t * pamh, void *data,
@@ -84,8 +83,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags,
 		goto out_save_retval;
 	}
 
-	retval = _unix_read_password(pamh, NULL, PROMPT_PASS, NULL,
-	    DATA_AUTHTOK, &pass);
+	retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, NULL);
 
 	if (retval != PAM_SUCCESS) {
 #if defined(PAM_CONV_AGAIN) && defined(PAM_INCOMPLETE)
diff --git a/pam_tcb/pam_unix_passwd.c b/pam_tcb/pam_unix_passwd.c
index ee99609..1561d14 100644
--- a/pam_tcb/pam_unix_passwd.c
+++ b/pam_tcb/pam_unix_passwd.c
@@ -23,9 +23,6 @@
 #include "attribute.h"
 #include "support.h"
 
-#define DATA_OLD_AUTHTOK		"-UN*X-OLD-PASS"
-#define DATA_NEW_AUTHTOK		"-UN*X-NEW-PASS"
-
 #define TRIES				3
 
 #define TMP_SUFFIX			".tmp"
@@ -246,8 +243,7 @@ static char *get_pwfile(const char *forwho)
 	}
 }
 
-static int do_setpass(pam_handle_t *pamh, const char *forwho,
-    const char *fromwhat, char *towhat)
+static int do_setpass(pam_handle_t *pamh, const char *forwho, char *towhat)
 {
 	struct passwd *pw = NULL;
 	char *file;
@@ -382,9 +378,6 @@ static int unix_approve_pass(pam_handle_t *pamh,
 
 static int unix_prelim(pam_handle_t *pamh, const char *user)
 {
-	int lctrl[OPT_SIZE];
-	char *greeting;
-	const char *oldpass;
 	int retval = PAM_SUCCESS;
 
 	D(("called"));
@@ -392,20 +385,15 @@ static int unix_prelim(pam_handle_t *pamh, const char *user)
 	if (_unix_blankpasswd(pamh, user))
 		goto out;
 
-	if (asprintf(&greeting, MESSAGE_CHANGING, user) < 0) {
-		pam_syslog(pamh, LOG_CRIT, "Out of memory");
-		return PAM_BUF_ERR;
-	}
-
 	if (off(UNIX__IAMROOT)) {
-		memcpy(lctrl, pam_unix_param.ctrl, sizeof(lctrl));
-		set(UNIX__OLD_PASSWD);
-		retval = _unix_read_password(pamh, greeting,
-		    PROMPT_OLDPASS, NULL,
-		    DATA_OLD_AUTHTOK, &oldpass);
-		free(greeting);
-		memcpy(pam_unix_param.ctrl, lctrl,
-		    sizeof(pam_unix_param.ctrl));
+		const char *oldpass;
+
+		if (off(UNIX__QUIET)) {
+			retval = pam_info(pamh, MESSAGE_CHANGING, user);
+			if (retval != PAM_SUCCESS)
+				return retval;
+		}
+		retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &oldpass, NULL);
 
 		if (retval != PAM_SUCCESS) {
 			pam_syslog(pamh, LOG_NOTICE,
@@ -423,14 +411,8 @@ static int unix_prelim(pam_handle_t *pamh, const char *user)
 		}
 	} else {
 		D(("process run by root so no authentication is done"));
-		oldpass = NULL;
-		retval = PAM_SUCCESS;
 	}
 
-	retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *)oldpass);
-	if (retval != PAM_SUCCESS)
-		pam_syslog(pamh, LOG_CRIT, "Failed to set PAM_OLDAUTHTOK");
-
 	retval = unix_verify_shadow(user);
 	if (retval == PAM_AUTHTOK_ERR) {
 		if (off(UNIX__IAMROOT)) {
@@ -465,6 +447,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
     int argc, const char **argv)
 {
 	int retval, retry;
+	pam_item_t item;
 	char oldprefix[HASH_PREFIX_SIZE];
 	/* <DO NOT free() THESE> */
 	const char *user, *oldpass, *newpass;
@@ -519,33 +502,15 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
 
 	D(("do update"));
 
-	/*
-	 * Get the old token back. NULL was ok only if root (at this
-	 * point we assume that this has already been enforced on a
-	 * previous call to this function).
-	 */
-	if (off(UNIX_NOT_SET_PASS)) {
-		pam_item_t item;
-
-		retval = pam_get_item(pamh, PAM_OLDAUTHTOK, &item);
-		oldpass = item;
-	} else {
-		pam_data_t item;
-
-		retval = pam_get_data(pamh, DATA_OLD_AUTHTOK, &item);
-		if (retval == PAM_NO_MODULE_DATA) {
-			retval = PAM_SUCCESS;
-			item = NULL;
-		}
-		oldpass = item;
-	}
-	D(("oldpass=[%s]", oldpass));
-
+	retval = pam_get_item(pamh, PAM_OLDAUTHTOK, &item);
 	if (retval != PAM_SUCCESS) {
 		pam_syslog(pamh, LOG_NOTICE, "User not authenticated");
 		return retval;
 	}
 
+	oldpass = item;
+	D(("oldpass=[%s]", oldpass));
+
 	/* check account expiration */
 	retval = unix_verify_shadow(user);
 	if (retval != PAM_SUCCESS) {
@@ -557,20 +522,10 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
 	D(("get new password now"));
 
 	retval = PAM_AUTHTOK_ERR;
-	retry = 0;
+	retry = on(UNIX_USE_FIRST_PASS) ? TRIES - 1 : 0;
 	newhash = NULL;
 	while (retval != PAM_SUCCESS && retry++ < TRIES) {
-		int old_authtok_usage = pam_unix_param.authtok_usage;
-		/*
-		 * use_authtok is to force the use of a previously entered
-		 * password, needed for pluggable password strength checking.
-		 */
-		if (on(UNIX_USE_AUTHTOK))
-			pam_unix_param.authtok_usage = USE_FORCED;
-		retval = _unix_read_password(pamh, NULL,
-		    PROMPT_NEWPASS1, PROMPT_NEWPASS2,
-		    DATA_NEW_AUTHTOK, &newpass);
-		pam_unix_param.authtok_usage = old_authtok_usage;
+		retval = pam_get_authtok(pamh, PAM_AUTHTOK, &newpass, NULL);
 
 		D(("returned to pam_sm_chauthtok"));
 
@@ -592,10 +547,11 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
 		retval = unix_approve_pass(pamh, oldpass, newpass);
 	}
 
+	_pam_overwrite((char *)oldpass);
+
 	if (retval != PAM_SUCCESS) {
 		pam_syslog(pamh, LOG_NOTICE, "New password not acceptable");
 		_pam_overwrite((char *)newpass);
-		_pam_overwrite((char *)oldpass);
 		return retval;
 	}
 
@@ -612,10 +568,9 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
 
 	/* update the password database(s) -- race conditions? */
 	if (newhash)
-		retval = do_setpass(pamh, user, oldpass, newhash);
+		retval = do_setpass(pamh, user, newhash);
 	else
 		retval = PAM_BUF_ERR;
-	_pam_overwrite((char *)oldpass);
 	_pam_delete(newhash);
 
 	if (retval == PAM_SUCCESS) {
diff --git a/pam_tcb/support.c b/pam_tcb/support.c
index 322d366..5a60a5d 100644
--- a/pam_tcb/support.c
+++ b/pam_tcb/support.c
@@ -32,12 +32,6 @@ IO_LOOP(write_loop, write, const)
 #include "attribute.h"
 #include "support.h"
 
-static void data_cleanup(unused pam_handle_t *pamh, void *data,
-    unused int error_status)
-{
-	_pam_delete(data);
-}
-
 int unix_getspnam(struct spwd **spw, const struct passwd *pw)
 {
 	D(("called"));
@@ -640,109 +634,6 @@ int _unix_verify_password(pam_handle_t *pamh,
 	return retval;
 }
 
-/*
- * Obtain a password from the user.
- */
-
-int _unix_read_password(pam_handle_t *pamh,
-    const char *comment, const char *prompt1, const char *prompt2,
-    const char *data_name, const char **pass)
-{
-	pam_item_t item;
-	char *token;
-	int authtok_flag;
-	int retval = PAM_SUCCESS;
-
-	D(("called"));
-
-	/* make sure nothing inappropriate gets returned */
-	*pass = token = NULL;
-
-	/* which authentication token are we getting? */
-	authtok_flag = on(UNIX__OLD_PASSWD) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
-
-	/* should we obtain the password from a PAM item? */
-	if (pam_unix_param.authtok_usage != USE_NONE) {
-		retval = pam_get_item(pamh, authtok_flag, &item);
-		if (retval != PAM_SUCCESS) {
-			/* very strange */
-			return retval;
-		} else if (item) { /* we have a password! */
-			*pass = item;
-			item = NULL;
-			return PAM_SUCCESS;
-		} else if (pam_unix_param.authtok_usage == USE_FORCED) {
-			return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
-		} else if (on(UNIX_USE_AUTHTOK)
-		    && off(UNIX__OLD_PASSWD)) {
-			return PAM_AUTHTOK_RECOVER_ERR;
-		}
-	}
-
-	/* getting here implies we will have to get the password from the
-	 * user directly */
-	if (comment && off(UNIX__QUIET)) {
-		retval = pam_info(pamh, "%s", comment);
-	}
-
-	if (retval == PAM_SUCCESS && prompt1) {
-		retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, &token, "%s",
-		    prompt1);
-	}
-
-	if (retval == PAM_SUCCESS && token && prompt2) {
-		char *resp;
-		retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, &resp, "%s",
-		    prompt2);
-		if (retval != PAM_SUCCESS || !resp) {
-			_pam_delete(token);
-		} else if (strcmp(token, resp)) {
-			_pam_delete(token);
-			pam_error(pamh, "%s", MESSAGE_MISTYPED);
-			retval = PAM_AUTHTOK_RECOVER_ERR;
-		}
-		_pam_delete(resp);
-	}
-
-	if (retval != PAM_SUCCESS) {
-		pam_syslog(pamh, LOG_NOTICE,
-		    "Unable to obtain a password");
-		return retval;
-	}
-
-	/* 'token' is the entered password */
-	if (off(UNIX_NOT_SET_PASS)) {
-		/* we store this password as an item */
-		retval = pam_set_item(pamh, authtok_flag, token);
-		_pam_delete(token);
-		if (retval != PAM_SUCCESS ||
-		    (retval = pam_get_item(pamh, authtok_flag, &item))
-		    != PAM_SUCCESS) {
-			pam_syslog(pamh, LOG_CRIT,
-			    "Error manipulating password");
-			return retval;
-		}
-	} else {
-		/*
-		 * Then store it as data specific to this module. pam_end()
-		 * will arrange to clean it up.
-		 */
-		retval = pam_set_data(pamh, data_name, (void *)token,
-		    data_cleanup);
-		if (retval != PAM_SUCCESS) {
-			_pam_delete(token);
-			pam_syslog(pamh, LOG_CRIT,
-			    "Error manipulating password");
-			return retval;
-		}
-		item = token;
-	}
-
-	*pass = item;
-
-	return PAM_SUCCESS;
-}
-
 static char *crypt_wrapper_ra(pam_handle_t *pamh, const char *key,
     const char *salt)
 {
@@ -824,7 +715,8 @@ static struct bool_names {
 	int optval, negate;
 } unix_bools[] = {
 	{"audit", UNIX_AUDIT, 0},
-	{"not_set_pass", UNIX_NOT_SET_PASS, 0},
+	{"use_first_pass", UNIX_USE_FIRST_PASS, 0},
+	{"try_first_pass", UNIX_TRY_FIRST_PASS, 0},
 	{"use_authtok", UNIX_USE_AUTHTOK, 0},
 	{"shadow", UNIX_SHADOW, 0},
 	{"passwd", UNIX_PASSWD, 0},
@@ -849,10 +741,6 @@ static int parse_opt(pam_handle_t *pamh, const char *item,
 
 	if (!strcmp(item, "md5"))
 		opt = "prefix=$1$";
-	else if (!strcmp(item, "try_first_pass"))
-		opt = "authtok_usage=try";
-	else if (!strcmp(item, "use_first_pass"))
-		opt = "authtok_usage=forced";
 	else
 		opt = item;
 
@@ -905,7 +793,7 @@ int _set_ctrl(pam_handle_t *pamh, int flags, int argc, const char **argv)
 	unsigned int i;
 	const char *param;
 	struct cmdline_opts the_cmdline_opts[] = {
-		{"authtok_usage=", NULL, NULL},
+		{"authtok_type=", NULL, NULL},
 		{"helper=", NULL, NULL},
 		{"count=", NULL, NULL},
 		{"write_to=", NULL, NULL},
@@ -970,22 +858,6 @@ int _set_ctrl(pam_handle_t *pamh, int flags, int argc, const char **argv)
 	} else
 		pam_unix_param.count = 0;
 
-	param = get_optval("authtok_usage=", the_cmdline_opts);
-	if (param) {
-		if (!strcmp(param, "no"))
-			pam_unix_param.authtok_usage = USE_NONE;
-		else if (!strcmp(param, "try"))
-			pam_unix_param.authtok_usage = USE_TRY;
-		else if (!strcmp(param, "forced"))
-			pam_unix_param.authtok_usage = USE_FORCED;
-		else {
-			pam_syslog(pamh, LOG_ERR,
-			    "Invalid authtok_usage= argument: %s", param);
-			return 0;
-		}
-	} else
-		pam_unix_param.authtok_usage = USE_NONE;
-
 	param = get_optval("write_to=", the_cmdline_opts);
 	if (param) {
 		if (!strcmp(param, "passwd"))
diff --git a/pam_tcb/support.h b/pam_tcb/support.h
index f2cf89e..4abf1f1 100644
--- a/pam_tcb/support.h
+++ b/pam_tcb/support.h
@@ -27,18 +27,6 @@
 /* should be large enough to hold "*NP*" */
 #define HASH_PREFIX_SIZE		5
 
-/* Password prompt to use for authentication */
-#define PROMPT_PASS \
-	_("Password: ")
-
-/* Prompts to use for password changes */
-#define PROMPT_OLDPASS \
-	_("Enter current password: ")
-#define PROMPT_NEWPASS1 \
-	_("Enter new password: ")
-#define PROMPT_NEWPASS2 \
-	_("Re-type new password: ")
-
 /* Possible messages during account management */
 #define MESSAGE_ACCT_EXPIRED \
 	_("Your account has expired; please contact your system administrator.")
@@ -56,8 +44,6 @@
 	_("No password supplied.")
 #define MESSAGE_TOOSOON \
 	_("You must wait longer to change your password.")
-#define MESSAGE_MISTYPED \
-	_("Sorry, passwords do not match.")
 
 /*
  * Here are the various boolean options recognized by the unix module.
@@ -71,7 +57,11 @@ enum {
 
 	UNIX_AUDIT,		/* print more things than debug, */
 				/* some information may be sensitive */
-	UNIX_NOT_SET_PASS,	/* don't set the AUTHTOK items */
+	UNIX_USE_FIRST_PASS,	/* don't prompt the user for passwords */
+	UNIX_TRY_FIRST_PASS,	/* take passwords from PAM_AUTHTOK and possibly
+				   PAM_OLDAUTHTOK items, but prompt the user
+				   if the appropriate PAM item is unset */
+	UNIX_AUTHTOK_TYPE,	/* the type of password to use in prompts */
 
 	UNIX__PRELIM,		/* internal */
 	UNIX__UPDATE,		/* internal */
@@ -101,12 +91,6 @@ enum {
 	((_UNIX_BOOLS - 1) / _INT_BITS + 1)
 
 enum {
-	USE_NONE = 0,		/* ask for password via the conv function */
-	USE_TRY,		/* try to get password(s) from PAM_*AUTHTOK */
-	USE_FORCED		/* get password(s) from PAM_*AUTHTOK or fail */
-};
-
-enum {
 	WRITE_PASSWD = 0,	/* write changed password to /etc/passwd */
 	WRITE_SHADOW,		/* write changed password to /etc/shadow */
 	WRITE_TCB		/* write changed password to /etc/tcb/ */
@@ -119,7 +103,6 @@ struct cmdline_opts {
 
 struct pam_unix_params {
 	unsigned int ctrl[OPT_SIZE];
-	int authtok_usage;
 	int write_to;
 	const unsigned char *crypt_prefix;
 	const unsigned char *helper;
@@ -182,9 +165,6 @@ extern int _unix_fork(pam_handle_t *, cb_func, const void *);
 extern int _set_ctrl(pam_handle_t *, int flags, int argc, const char **argv);
 extern int _unix_blankpasswd(pam_handle_t *, const char *user);
 extern int _unix_verify_password(pam_handle_t *, const char *, const char *);
-extern int _unix_read_password(pam_handle_t *, const char *comment,
-    const char *prompt1, const char *prompt2, const char *data_name,
-    const char **pass);
 extern int unix_getspnam(struct spwd **, const struct passwd *);
 extern char *crypt_wrapper(pam_handle_t *, const char *, const char *);
 extern char *do_crypt(pam_handle_t *, const char *);
-- 
ldv

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.