summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/man/pam_sss.8.xml17
-rw-r--r--src/sss_client/pam_sss.c239
2 files changed, 164 insertions, 92 deletions
diff --git a/src/man/pam_sss.8.xml b/src/man/pam_sss.8.xml
index 9a681960..aed76e2f 100644
--- a/src/man/pam_sss.8.xml
+++ b/src/man/pam_sss.8.xml
@@ -31,6 +31,9 @@
<arg choice='opt'>
<replaceable>use_authtok</replaceable>
</arg>
+ <arg choice='opt'>
+ <replaceable>retry=N</replaceable>
+ </arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -75,6 +78,20 @@
password module.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <option>retry=N</option>
+ </term>
+ <listitem>
+ <para>If specified the user is asked another N times for a
+ password if authentication fails. Default is 0.</para>
+ <para>Please note that this option might not work as
+ expected if the application calling PAM handles the user
+ dialog on its own. A typical example is
+ <command>sshd</command> with
+ <option>PasswordAuthentication</option>.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index a3e3d86b..1b98cf01 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -39,6 +39,7 @@
#include <fcntl.h>
#include <errno.h>
#include <locale.h>
+#include <stdbool.h>
#include <security/pam_modules.h>
#include <security/pam_ext.h>
@@ -60,6 +61,8 @@
#define PW_RESET_MSG_FILENAME_TEMPLATE SSSD_CONF_DIR"/customize/%s/pam_sss_pw_reset_message.%s"
#define PW_RESET_MSG_MAX_SIZE 4096
+#define OPT_RETRY_KEY "retry="
+
struct pam_items {
const char* pam_service;
const char* pam_user;
@@ -205,6 +208,9 @@ static void overwrite_and_free_pam_items(struct pam_items *pi)
pi->pam_newauthtok = NULL;
}
+ pi->pamstack_authtok = NULL;
+ pi->pamstack_oldauthtok = NULL;
+
free(pi->domain_name);
}
@@ -1201,8 +1207,10 @@ static int prompt_new_password(pam_handle_t *pamh, struct pam_items *pi)
}
static void eval_argv(pam_handle_t *pamh, int argc, const char **argv,
- uint32_t *flags)
+ uint32_t *flags, int *retries)
{
+ char *ep;
+
for (; argc-- > 0; ++argv) {
if (strcmp(*argv, "forward_pass") == 0) {
*flags |= FLAGS_FORWARD_PASS;
@@ -1210,6 +1218,28 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv,
*flags |= FLAGS_USE_FIRST_PASS;
} else if (strcmp(*argv, "use_authtok") == 0) {
*flags |= FLAGS_USE_AUTHTOK;
+ } else if (strncmp(*argv, OPT_RETRY_KEY, strlen(OPT_RETRY_KEY)) == 0) {
+ if (*(*argv+6) == '\0') {
+ logger(pamh, LOG_ERR, "Missing argument to option retry.");
+ *retries = 0;
+ } else {
+ errno = 0;
+ *retries = strtol(*argv+6, &ep, 10);
+ if (errno != 0) {
+ D(("strtol failed [%d][%s]", errno, strerror(errno)));
+ *retries = 0;
+ }
+ if (*ep != '\0') {
+ logger(pamh, LOG_ERR, "Argument to option retry contains "
+ "extra characters.");
+ *retries = 0;
+ }
+ if (*retries < 0) {
+ logger(pamh, LOG_ERR, "Argument to option retry must not "
+ "be negative.");
+ *retries = 0;
+ }
+ }
} else {
logger(pamh, LOG_WARNING, "unknown option: %s", *argv);
}
@@ -1334,12 +1364,14 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
struct pam_items pi;
uint32_t flags = 0;
int *exp_data;
+ bool retry = false;
+ int retries = 0;
bindtextdomain(PACKAGE, LOCALEDIR);
D(("Hello pam_sssd: %d", task));
- eval_argv(pamh, argc, argv, &flags);
+ eval_argv(pamh, argc, argv, &flags, &retries);
ret = get_pam_items(pamh, &pi);
if (ret != PAM_SUCCESS) {
@@ -1347,106 +1379,129 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
return ret;
}
- switch(task) {
- case SSS_PAM_AUTHENTICATE:
- ret = get_authtok_for_authentication(pamh, &pi, flags);
- if (ret != PAM_SUCCESS) {
- D(("failed to get authentication token: %s",
- pam_strerror(pamh, ret)));
- return ret;
- }
- break;
- case SSS_PAM_CHAUTHTOK:
- ret = get_authtok_for_password_change(pamh, &pi, flags, pam_flags);
- if (ret != PAM_SUCCESS) {
- D(("failed to get tokens for password change: %s",
- pam_strerror(pamh, ret)));
- return ret;
- }
- if (pam_flags & PAM_PRELIM_CHECK) {
- task = SSS_PAM_CHAUTHTOK_PRELIM;
- }
- break;
- case SSS_PAM_ACCT_MGMT:
- case SSS_PAM_SETCRED:
- case SSS_PAM_OPEN_SESSION:
- case SSS_PAM_CLOSE_SESSION:
- break;
- default:
- D(("Illegal task [%d]", task));
- return PAM_SYSTEM_ERR;
- }
-
- pam_status = send_and_receive(pamh, &pi, task);
-
- switch (task) {
- case SSS_PAM_AUTHENTICATE:
- /* We allow sssd to send the return code PAM_NEW_AUTHTOK_REQD during
- * authentication, see sss_cli.h for details */
- if (pam_status == PAM_NEW_AUTHTOK_REQD) {
- D(("Authtoken expired, trying to change it"));
-
- exp_data = malloc(sizeof(int));
- if (exp_data == NULL) {
- D(("malloc failed."));
- pam_status = PAM_BUF_ERR;
- break;
- }
- *exp_data = 1;
+ do {
+ retry = false;
- pam_status = pam_set_data(pamh, PWEXP_FLAG, exp_data,
- free_exp_data);
- if (pam_status != PAM_SUCCESS) {
- D(("pam_set_data failed."));
- }
- }
- break;
- case SSS_PAM_ACCT_MGMT:
- if (pam_status == PAM_SUCCESS &&
- pam_get_data(pamh, PWEXP_FLAG, (const void **) &exp_data) ==
- PAM_SUCCESS) {
- ret = do_pam_conversation(pamh, PAM_TEXT_INFO,
- _("Password expired. Change your password now."),
- NULL, NULL);
+ switch(task) {
+ case SSS_PAM_AUTHENTICATE:
+ ret = get_authtok_for_authentication(pamh, &pi, flags);
if (ret != PAM_SUCCESS) {
- D(("do_pam_conversation failed."));
+ D(("failed to get authentication token: %s",
+ pam_strerror(pamh, ret)));
+ return ret;
}
- pam_status = PAM_NEW_AUTHTOK_REQD;
- }
- break;
- case SSS_PAM_CHAUTHTOK:
- if (pam_status != PAM_SUCCESS && pam_status != PAM_USER_UNKNOWN) {
- ret = pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ break;
+ case SSS_PAM_CHAUTHTOK:
+ ret = get_authtok_for_password_change(pamh, &pi, flags, pam_flags);
if (ret != PAM_SUCCESS) {
- D(("Failed to unset PAM_AUTHTOK [%s]",
- pam_strerror(pamh,ret)));
+ D(("failed to get tokens for password change: %s",
+ pam_strerror(pamh, ret)));
+ return ret;
}
- ret = pam_set_item(pamh, PAM_OLDAUTHTOK, NULL);
- if (ret != PAM_SUCCESS) {
- D(("Failed to unset PAM_OLDAUTHTOK [%s]",
- pam_strerror(pamh,ret)));
+ if (pam_flags & PAM_PRELIM_CHECK) {
+ task = SSS_PAM_CHAUTHTOK_PRELIM;
}
- }
- break;
- case SSS_PAM_CHAUTHTOK_PRELIM:
- if (pam_status == PAM_PERM_DENIED && pi.pam_authtok_size == 0 &&
- getuid() == 0 &&
- pam_get_data(pamh, PWEXP_FLAG, (const void **) &exp_data) !=
- PAM_SUCCESS) {
-
- ret = select_pw_reset_message(pamh, &pi);
- if (ret != 0) {
- D(("select_pw_reset_message failed.\n"));
+ break;
+ case SSS_PAM_ACCT_MGMT:
+ case SSS_PAM_SETCRED:
+ case SSS_PAM_OPEN_SESSION:
+ case SSS_PAM_CLOSE_SESSION:
+ break;
+ default:
+ D(("Illegal task [%d]", task));
+ return PAM_SYSTEM_ERR;
+ }
+
+ pam_status = send_and_receive(pamh, &pi, task);
+
+ switch (task) {
+ case SSS_PAM_AUTHENTICATE:
+ /* We allow sssd to send the return code PAM_NEW_AUTHTOK_REQD during
+ * authentication, see sss_cli.h for details */
+ if (pam_status == PAM_NEW_AUTHTOK_REQD) {
+ D(("Authtoken expired, trying to change it"));
+
+ exp_data = malloc(sizeof(int));
+ if (exp_data == NULL) {
+ D(("malloc failed."));
+ pam_status = PAM_BUF_ERR;
+ break;
+ }
+ *exp_data = 1;
+
+ pam_status = pam_set_data(pamh, PWEXP_FLAG, exp_data,
+ free_exp_data);
+ if (pam_status != PAM_SUCCESS) {
+ D(("pam_set_data failed."));
+ }
}
- }
- default:
- /* nothing to do */
- break;
- }
+ break;
+ case SSS_PAM_ACCT_MGMT:
+ if (pam_status == PAM_SUCCESS &&
+ pam_get_data(pamh, PWEXP_FLAG, (const void **) &exp_data) ==
+ PAM_SUCCESS) {
+ ret = do_pam_conversation(pamh, PAM_TEXT_INFO,
+ _("Password expired. Change your password now."),
+ NULL, NULL);
+ if (ret != PAM_SUCCESS) {
+ D(("do_pam_conversation failed."));
+ }
+ pam_status = PAM_NEW_AUTHTOK_REQD;
+ }
+ break;
+ case SSS_PAM_CHAUTHTOK:
+ if (pam_status != PAM_SUCCESS && pam_status != PAM_USER_UNKNOWN) {
+ ret = pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ if (ret != PAM_SUCCESS) {
+ D(("Failed to unset PAM_AUTHTOK [%s]",
+ pam_strerror(pamh,ret)));
+ }
+ ret = pam_set_item(pamh, PAM_OLDAUTHTOK, NULL);
+ if (ret != PAM_SUCCESS) {
+ D(("Failed to unset PAM_OLDAUTHTOK [%s]",
+ pam_strerror(pamh,ret)));
+ }
+ }
+ break;
+ case SSS_PAM_CHAUTHTOK_PRELIM:
+ if (pam_status == PAM_PERM_DENIED && pi.pam_authtok_size == 0 &&
+ getuid() == 0 &&
+ pam_get_data(pamh, PWEXP_FLAG, (const void **) &exp_data) !=
+ PAM_SUCCESS) {
+
+ ret = select_pw_reset_message(pamh, &pi);
+ if (ret != 0) {
+ D(("select_pw_reset_message failed.\n"));
+ }
+ }
+ default:
+ /* nothing to do */
+ break;
+ }
+ overwrite_and_free_pam_items(&pi);
+ D(("retries [%d].", retries));
- overwrite_and_free_pam_items(&pi);
+ if (pam_status != PAM_SUCCESS &&
+ (task == SSS_PAM_AUTHENTICATE || task == SSS_PAM_CHAUTHTOK_PRELIM) &&
+ retries > 0) {
+ retry = true;
+ retries--;
+
+ flags &= !FLAGS_USE_FIRST_PASS;
+ ret = pam_set_item(pamh, PAM_AUTHTOK, NULL);
+ if (ret != PAM_SUCCESS) {
+ D(("Failed to unset PAM_AUTHTOK [%s]",
+ pam_strerror(pamh,ret)));
+ }
+ ret = pam_set_item(pamh, PAM_OLDAUTHTOK, NULL);
+ if (ret != PAM_SUCCESS) {
+ D(("Failed to unset PAM_OLDAUTHTOK [%s]",
+ pam_strerror(pamh,ret)));
+ }
+ }
+ } while(retry);
return pam_status;
}