From f724123e20f8d4a1c85473d917da6c65a10d6d62 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Tue, 18 Sep 2018 09:53:37 +0200
Subject: [PATCH 41/47] pam_sss: add option require_cert_auth
With this new option pam_sss will wait until a Smartcard is available
and then try to authenticate with the help of the Smartcard.
Related https://pagure.io/SSSD/sssd/issue/3650
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
(cherry picked from commit 49be8974b490c368d349752f3196af0c9ed28dd5)
---
src/man/pam_sss.8.xml | 25 ++++++++++++
src/responder/pam/pamsrv_cmd.c | 12 ++++++
src/responder/pam/pamsrv_p11.c | 5 ++-
src/sss_client/pam_message.c | 4 ++
src/sss_client/pam_message.h | 1 +
src/sss_client/pam_sss.c | 92 ++++++++++++++++++++++++++----------------
src/sss_client/sss_cli.h | 2 +
src/util/sss_pam_data.c | 1 +
src/util/sss_pam_data.h | 1 +
9 files changed, 107 insertions(+), 36 deletions(-)
diff --git a/src/man/pam_sss.8.xml b/src/man/pam_sss.8.xml
index ca2e8e20678d102525a9252678dd83459c3338ac..9998519f16c934e0d578760a57cc0908db760bfb 100644
--- a/src/man/pam_sss.8.xml
+++ b/src/man/pam_sss.8.xml
@@ -53,6 +53,9 @@
<arg choice='opt'>
<replaceable>try_cert_auth</replaceable>
</arg>
+ <arg choice='opt'>
+ <replaceable>require_cert_auth</replaceable>
+ </arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -223,6 +226,28 @@ auth sufficient pam_sss.so allow_missing_name
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <option>require_cert_auth</option>
+ </term>
+ <listitem>
+ <para>
+ Do certificate based authentication, i.e.
+ authentication with a Smartcard or similar devices. If a
+ Smartcard is not available the user will be prompted to
+ insert one. SSSD will wait for a Smartcard until the
+ timeout defined by p11_wait_for_card_timeout passed,
+ please see
+ <citerefentry><refentrytitle>sssd.conf</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for details.
+ </para>
+ <para>
+ If no Smartcard is available after the timeout or
+ certificate based authentication is not allowed for the
+ current service PAM_AUTHINFO_UNAVAIL is returned.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index c8df32de9e72e9f5ce33e26f0a13101a99f01d5f..6e37f831602e4c367176cc14126dbbec72c858cd 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -317,6 +317,11 @@ static int pam_parse_in_data_v2(struct pam_data *pd,
size, body, blen, &c);
if (ret != EOK) return ret;
break;
+ case SSS_PAM_ITEM_FLAGS:
+ ret = extract_uint32_t(&pd->cli_flags, size,
+ body, blen, &c);
+ if (ret != EOK) return ret;
+ break;
default:
DEBUG(SSSDBG_CRIT_FAILURE,
"Ignoring unknown data type [%d].\n", type);
@@ -1447,6 +1452,13 @@ static void pam_forwarder_cert_cb(struct tevent_req *req)
"No certificate found and no logon name given, " \
"authentication not possible.\n");
ret = ENOENT;
+ } else if (pd->cli_flags & PAM_CLI_FLAGS_TRY_CERT_AUTH) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "try_cert_auth flag set but no certificate available, "
+ "request finished.\n");
+ preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
+ pam_reply(preq);
+ return;
} else {
if (pd->cmd == SSS_PAM_AUTHENTICATE) {
DEBUG(SSSDBG_CRIT_FAILURE,
diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
index ffa6787e967488ac408ce0f0a11b96066c29b630..8b8859d9d335aec6d310201256522fa8afdd3694 100644
--- a/src/responder/pam/pamsrv_p11.c
+++ b/src/responder/pam/pamsrv_p11.c
@@ -721,7 +721,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
struct timeval tv;
int pipefd_to_child[2] = PIPE_INIT;
int pipefd_from_child[2] = PIPE_INIT;
- const char *extra_args[13] = { NULL };
+ const char *extra_args[14] = { NULL };
uint8_t *write_buf = NULL;
size_t write_buf_len = 0;
size_t arg_c;
@@ -748,6 +748,9 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
/* extra_args are added in revers order */
arg_c = 0;
+ if ((pd->cli_flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH) && pd->priv == 1) {
+ extra_args[arg_c++] = "--wait_for_card";
+ }
extra_args[arg_c++] = nss_db;
extra_args[arg_c++] = "--nssdb";
if (verify_opts != NULL) {
diff --git a/src/sss_client/pam_message.c b/src/sss_client/pam_message.c
index b239f6f53da54054c52e484bdd076193709cb003..036ae2ad17742c123ba59e39a122ea605b7b95a6 100644
--- a/src/sss_client/pam_message.c
+++ b/src/sss_client/pam_message.c
@@ -126,6 +126,7 @@ int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer)
len += 3*sizeof(uint32_t); /* cli_pid */
len += *pi->requested_domains != '\0' ?
2*sizeof(uint32_t) + pi->requested_domains_size : 0;
+ len += 3*sizeof(uint32_t); /* flags */
buf = malloc(len);
if (buf == NULL) {
@@ -164,6 +165,9 @@ int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer)
pi->pam_newauthtok, pi->pam_newauthtok_size,
&buf[rp]);
+ rp += add_uint32_t_item(SSS_PAM_ITEM_FLAGS, (uint32_t) pi->flags,
+ &buf[rp]);
+
SAFEALIGN_SETMEM_UINT32(buf + rp, SSS_END_OF_PAM_REQUEST, &rp);
if (rp != len) {
diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h
index 11526a80a767ff5602b194d14765ff261e8f9707..50fedcd82d8ace520d0360d85d163f91df0cb100 100644
--- a/src/sss_client/pam_message.h
+++ b/src/sss_client/pam_message.h
@@ -51,6 +51,7 @@ struct pam_items {
enum sss_authtok_type pam_newauthtok_type;
size_t pam_newauthtok_size;
pid_t cli_pid;
+ uint32_t flags;
const char *login_name;
char *domain_name;
const char *requested_domains;
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index 96ff15adad867aceae17431cd5256ae52e4b9306..b4c1036ad68a97821f5d0aee873fa18fe5e72683 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -134,6 +134,7 @@ static void free_cai(struct cert_auth_info *cai)
free(cai->cert_user);
free(cai->cert);
free(cai->token_name);
+ free(cai->module_name);
free(cai->key_id);
free(cai->prompt_str);
free(cai);
@@ -1247,6 +1248,8 @@ static int get_pam_items(pam_handle_t *pamh, uint32_t flags,
pi->cert_list = NULL;
pi->selected_cert = NULL;
+ pi->flags = flags;
+
return PAM_SUCCESS;
}
@@ -1267,6 +1270,7 @@ static void print_pam_items(struct pam_items *pi)
D(("Newauthtok: %s", CHECK_AND_RETURN_PI_STRING(pi->pam_newauthtok)));
D(("Cli_PID: %d", pi->cli_pid));
D(("Requested domains: %s", pi->requested_domains));
+ D(("Flags: %d", pi->flags));
}
static int send_and_receive(pam_handle_t *pamh, struct pam_items *pi,
@@ -1999,6 +2003,8 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv,
*flags |= PAM_CLI_FLAGS_PROMPT_ALWAYS;
} else if (strcmp(*argv, "try_cert_auth") == 0) {
*flags |= PAM_CLI_FLAGS_TRY_CERT_AUTH;
+ } else if (strcmp(*argv, "require_cert_auth") == 0) {
+ *flags |= PAM_CLI_FLAGS_REQUIRE_CERT_AUTH;
} else {
logger(pamh, LOG_WARNING, "unknown option: %s", *argv);
}
@@ -2274,55 +2280,51 @@ static int get_authtok_for_password_change(pam_handle_t *pamh,
return PAM_SUCCESS;
}
-#define SC_ENTER_FMT "Please enter smart card labeled\n %s\nand press enter"
+#define SC_ENTER_LABEL_FMT "Please enter smart card labeled\n %s"
+#define SC_ENTER_FMT "Please enter smart card"
static int check_login_token_name(pam_handle_t *pamh, struct pam_items *pi,
- bool quiet_mode)
+ int retries, bool quiet_mode)
{
int ret;
int pam_status;
char *login_token_name;
char *prompt = NULL;
- size_t size;
- char *answer = NULL;
- /* TODO: check multiple cert case */
- struct cert_auth_info *cai = pi->cert_list;
-
- if (cai == NULL) {
- D(("No certificate information available"));
- return EINVAL;
- }
+ uint32_t orig_flags = pi->flags;
login_token_name = getenv("PKCS11_LOGIN_TOKEN_NAME");
+ if (login_token_name == NULL
+ && !(pi->flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH)) {
+ return PAM_SUCCESS;
+ }
+
if (login_token_name == NULL) {
- return PAM_SUCCESS;
+ ret = asprintf(&prompt, SC_ENTER_FMT);
+ } else {
+ ret = asprintf(&prompt, SC_ENTER_LABEL_FMT, login_token_name);
+ }
+ if (ret == -1) {
+ return ENOMEM;
}
- while (cai->token_name == NULL
- || strcmp(login_token_name, cai->token_name) != 0) {
- size = sizeof(SC_ENTER_FMT) + strlen(login_token_name);
- prompt = malloc(size);
- if (prompt == NULL) {
- D(("malloc failed."));
- return ENOMEM;
- }
+ pi->flags |= PAM_CLI_FLAGS_REQUIRE_CERT_AUTH;
+
+ /* TODO: check multiple cert case */
+ while (pi->cert_list == NULL || pi->cert_list->token_name == NULL
+ || (login_token_name != NULL
+ && strcmp(login_token_name,
+ pi->cert_list->token_name) != 0)) {
- ret = snprintf(prompt, size, SC_ENTER_FMT,
- login_token_name);
- if (ret < 0 || ret >= size) {
- D(("snprintf failed."));
- free(prompt);
- return EFAULT;
+ if (retries < 0) {
+ ret = PAM_AUTHINFO_UNAVAIL;
+ goto done;
}
+ retries--;
- ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, prompt,
- NULL, &answer);
- free(prompt);
+ ret = do_pam_conversation(pamh, PAM_TEXT_INFO, prompt, NULL, NULL);
if (ret != PAM_SUCCESS) {
D(("do_pam_conversation failed."));
- return ret;
- } else {
- free(answer);
+ goto done;
}
pam_status = send_and_receive(pamh, pi, SSS_PAM_PREAUTH, quiet_mode);
@@ -2335,7 +2337,14 @@ static int check_login_token_name(pam_handle_t *pamh, struct pam_items *pi,
}
}
- return PAM_SUCCESS;
+ ret = PAM_SUCCESS;
+
+done:
+
+ pi->flags = orig_flags;
+ free(prompt);
+
+ return ret;
}
static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
@@ -2394,8 +2403,19 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
&& (pi.pam_authtok == NULL
|| (flags & PAM_CLI_FLAGS_PROMPT_ALWAYS))
&& access(PAM_PREAUTH_INDICATOR, F_OK) == 0) {
+
+ if (flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH) {
+ /* Do not use PAM_CLI_FLAGS_REQUIRE_CERT_AUTH in the first
+ * SSS_PAM_PREAUTH run. In case a card is already inserted
+ * we do not have to prompt to insert a card. */
+ pi.flags &= ~PAM_CLI_FLAGS_REQUIRE_CERT_AUTH;
+ pi.flags |= PAM_CLI_FLAGS_TRY_CERT_AUTH;
+ }
+
pam_status = send_and_receive(pamh, &pi, SSS_PAM_PREAUTH,
quiet_mode);
+
+ pi.flags = flags;
if (pam_status != PAM_SUCCESS) {
D(("send_and_receive returned [%d] during pre-auth",
pam_status));
@@ -2414,8 +2434,10 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
return PAM_AUTHINFO_UNAVAIL;
}
- if (strcmp(pi.pam_service, "gdm-smartcard") == 0) {
- ret = check_login_token_name(pamh, &pi, quiet_mode);
+ if (strcmp(pi.pam_service, "gdm-smartcard") == 0
+ || (flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH)) {
+ ret = check_login_token_name(pamh, &pi, retries,
+ quiet_mode);
if (ret != PAM_SUCCESS) {
D(("check_login_token_name failed.\n"));
return ret;
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index 38e3f999d799556a56ac08f0f3a6b538b8cde9f3..af8a43916d43b631092941fed13c520273a1acc5 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -363,6 +363,7 @@ enum pam_item_type {
SSS_PAM_ITEM_CLI_LOCALE,
SSS_PAM_ITEM_CLI_PID,
SSS_PAM_ITEM_REQUESTED_DOMAINS,
+ SSS_PAM_ITEM_FLAGS,
};
#define PAM_CLI_FLAGS_USE_FIRST_PASS (1 << 0)
@@ -374,6 +375,7 @@ enum pam_item_type {
#define PAM_CLI_FLAGS_ALLOW_MISSING_NAME (1 << 6)
#define PAM_CLI_FLAGS_PROMPT_ALWAYS (1 << 7)
#define PAM_CLI_FLAGS_TRY_CERT_AUTH (1 << 8)
+#define PAM_CLI_FLAGS_REQUIRE_CERT_AUTH (1 << 9)
#define SSS_NSS_MAX_ENTRIES 256
#define SSS_NSS_HEADER_SIZE (sizeof(uint32_t) * 4)
diff --git a/src/util/sss_pam_data.c b/src/util/sss_pam_data.c
index 5e41349b9e98974563bf55c41ce36c26b897ac99..cb8779c1dff04832f623eb518d2b010107d4b045 100644
--- a/src/util/sss_pam_data.c
+++ b/src/util/sss_pam_data.c
@@ -176,6 +176,7 @@ void pam_print_data(int l, struct pam_data *pd)
DEBUG(l, "priv: %d\n", pd->priv);
DEBUG(l, "cli_pid: %d\n", pd->cli_pid);
DEBUG(l, "logon name: %s\n", PAM_SAFE_ITEM(pd->logon_name));
+ DEBUG(l, "flags: %d\n", pd->cli_flags);
}
int pam_add_response(struct pam_data *pd, enum response_type type,
diff --git a/src/util/sss_pam_data.h b/src/util/sss_pam_data.h
index 7d74fa6a0026d3964f33c8529063b1dceae45688..c9898105418fc76b45d78883a0520f37d0ae1c05 100644
--- a/src/util/sss_pam_data.h
+++ b/src/util/sss_pam_data.h
@@ -58,6 +58,7 @@ struct pam_data {
struct sss_auth_token *newauthtok;
uint32_t cli_pid;
char *logon_name;
+ uint32_t cli_flags;
int pam_status;
int response_delay;
--
2.14.4