680 lines
21 KiB
Diff
680 lines
21 KiB
Diff
|
From 55deead9f2a98c3ba1fd5754bd38203b6c02b6a1 Mon Sep 17 00:00:00 2001
|
|||
|
From: Sumit Bose <sbose@redhat.com>
|
|||
|
Date: Mon, 16 Oct 2017 14:13:10 +0200
|
|||
|
Subject: [PATCH 44/79] pam_sss: refactoring, use struct cert_auth_info
|
|||
|
MIME-Version: 1.0
|
|||
|
Content-Type: text/plain; charset=UTF-8
|
|||
|
Content-Transfer-Encoding: 8bit
|
|||
|
|
|||
|
Similar as in the PAM responder this patch replaces the individual
|
|||
|
certificate authentication related attributes by a struct which can be
|
|||
|
used as a list. With the pam_sss can handle multiple SSS_PAM_CERT_INFO
|
|||
|
message and place the data in individual list items.
|
|||
|
|
|||
|
If multiple certificates are returned before prompting for the PIN a
|
|||
|
dialog to select a certificate is shown to the users. If available a GDM
|
|||
|
PAM extension is used to let the user choose from a list. All coded
|
|||
|
needed at runtime to check if the extension is available and handle the
|
|||
|
data is provided by GDM as macros. This means that there are no
|
|||
|
additional run-time requirements.
|
|||
|
|
|||
|
Related to https://pagure.io/SSSD/sssd/issue/3560
|
|||
|
|
|||
|
Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
|
|||
|
Tested-by: Scott Poore <spoore@redhat.com>
|
|||
|
---
|
|||
|
contrib/sssd.spec.in | 9 +
|
|||
|
src/external/pam.m4 | 12 ++
|
|||
|
src/sss_client/pam_message.h | 8 +-
|
|||
|
src/sss_client/pam_sss.c | 439 ++++++++++++++++++++++++++++++++++---------
|
|||
|
4 files changed, 370 insertions(+), 98 deletions(-)
|
|||
|
|
|||
|
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
|
|||
|
index 4aafd1832b67161ff1c25a4e9ad689586a227a25..c716efdce05ab7b9178be66f34d09124c78071b5 100644
|
|||
|
--- a/contrib/sssd.spec.in
|
|||
|
+++ b/contrib/sssd.spec.in
|
|||
|
@@ -121,6 +121,12 @@
|
|||
|
%global with_kcm_option --without-kcm
|
|||
|
%endif
|
|||
|
|
|||
|
+%if (0%{?fedora} >= 27 || (0%{?rhel} >= 7 && 0%{?rhel7_minor} > 4))
|
|||
|
+ %global with_gdm_pam_extensions 1
|
|||
|
+%else
|
|||
|
+ %global with_gdm_pam_extensions 0
|
|||
|
+%endif
|
|||
|
+
|
|||
|
Name: @PACKAGE_NAME@
|
|||
|
Version: @PACKAGE_VERSION@
|
|||
|
Release: 0@PRERELEASE_VERSION@%{?dist}
|
|||
|
@@ -233,6 +239,9 @@ BuildRequires: libuuid-devel
|
|||
|
BuildRequires: jansson-devel
|
|||
|
BuildRequires: libcurl-devel
|
|||
|
%endif
|
|||
|
+%if (0%{?with_gdm_pam_extensions} == 1)
|
|||
|
+BuildRequires: gdm-devel
|
|||
|
+%endif
|
|||
|
|
|||
|
%description
|
|||
|
Provides a set of daemons to manage access to remote directories and
|
|||
|
diff --git a/src/external/pam.m4 b/src/external/pam.m4
|
|||
|
index 4776b6ae338409f0a2729dfc4cf5962463a40dfd..0dc7f19d0df6a4588cf893ecff6e518111462433 100644
|
|||
|
--- a/src/external/pam.m4
|
|||
|
+++ b/src/external/pam.m4
|
|||
|
@@ -27,3 +27,15 @@ AC_CHECK_FUNCS(pam_modutil_getlogin pam_vsyslog)
|
|||
|
|
|||
|
dnl restore LIBS
|
|||
|
LIBS="$save_LIBS"
|
|||
|
+
|
|||
|
+PKG_CHECK_MODULES([GDM_PAM_EXTENSIONS], [gdm-pam-extensions],
|
|||
|
+ [found_gdm_pam_extensions=yes],
|
|||
|
+ [AC_MSG_NOTICE([gdm-pam-extensions were not found. gdm support
|
|||
|
+for multiple certificates will not be build.
|
|||
|
+])])
|
|||
|
+
|
|||
|
+AC_SUBST(GDM_PAM_EXTENSIONS_CFLAGS)
|
|||
|
+
|
|||
|
+AS_IF([test x"$found_gdm_pam_extensions" = xyes],
|
|||
|
+ [AC_DEFINE_UNQUOTED(HAVE_GDM_PAM_EXTENSIONS, 1,
|
|||
|
+ [Build with gdm-pam-extensions support])])
|
|||
|
diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h
|
|||
|
index f215392f6879f01a0ca12abc8807bac5fc1f1cbb..11526a80a767ff5602b194d14765ff261e8f9707 100644
|
|||
|
--- a/src/sss_client/pam_message.h
|
|||
|
+++ b/src/sss_client/pam_message.h
|
|||
|
@@ -29,6 +29,8 @@
|
|||
|
|
|||
|
#include "sss_client/sss_cli.h"
|
|||
|
|
|||
|
+struct cert_auth_info;
|
|||
|
+
|
|||
|
struct pam_items {
|
|||
|
const char *pam_service;
|
|||
|
const char *pam_user;
|
|||
|
@@ -59,11 +61,9 @@ struct pam_items {
|
|||
|
char *first_factor;
|
|||
|
bool password_prompting;
|
|||
|
|
|||
|
- char *cert_user;
|
|||
|
- char *token_name;
|
|||
|
- char *module_name;
|
|||
|
- char *key_id;
|
|||
|
bool user_name_hint;
|
|||
|
+ struct cert_auth_info *cert_list;
|
|||
|
+ struct cert_auth_info *selected_cert;
|
|||
|
};
|
|||
|
|
|||
|
int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer);
|
|||
|
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
|
|||
|
index 303809b9ea05b5a8709c05ae230d5f289b57de31..c147d4b3d76443d69e27eb2da042f8eebd1ae6ab 100644
|
|||
|
--- a/src/sss_client/pam_sss.c
|
|||
|
+++ b/src/sss_client/pam_sss.c
|
|||
|
@@ -36,6 +36,10 @@
|
|||
|
#include <security/pam_modules.h>
|
|||
|
#include <security/pam_appl.h>
|
|||
|
|
|||
|
+#ifdef HAVE_GDM_PAM_EXTENSIONS
|
|||
|
+#include <gdm/gdm-pam-extensions.h>
|
|||
|
+#endif
|
|||
|
+
|
|||
|
#include "sss_pam_compat.h"
|
|||
|
#include "sss_pam_macros.h"
|
|||
|
|
|||
|
@@ -43,6 +47,7 @@
|
|||
|
#include "pam_message.h"
|
|||
|
#include "util/atomic_io.h"
|
|||
|
#include "util/authtok-utils.h"
|
|||
|
+#include "util/dlinklist.h"
|
|||
|
|
|||
|
#include <libintl.h>
|
|||
|
#define _(STRING) dgettext (PACKAGE, STRING)
|
|||
|
@@ -118,6 +123,40 @@ static void close_fd(pam_handle_t *pamh, void *ptr, int err)
|
|||
|
sss_pam_close_fd();
|
|||
|
}
|
|||
|
|
|||
|
+struct cert_auth_info {
|
|||
|
+ char *cert_user;
|
|||
|
+ char *cert;
|
|||
|
+ char *token_name;
|
|||
|
+ char *module_name;
|
|||
|
+ char *key_id;
|
|||
|
+ struct cert_auth_info *prev;
|
|||
|
+ struct cert_auth_info *next;
|
|||
|
+};
|
|||
|
+
|
|||
|
+static void free_cai(struct cert_auth_info *cai)
|
|||
|
+{
|
|||
|
+ if (cai != NULL) {
|
|||
|
+ free(cai->cert_user);
|
|||
|
+ free(cai->cert);
|
|||
|
+ free(cai->token_name);
|
|||
|
+ free(cai->key_id);
|
|||
|
+ free(cai);
|
|||
|
+ }
|
|||
|
+}
|
|||
|
+
|
|||
|
+static void free_cert_list(struct cert_auth_info *list)
|
|||
|
+{
|
|||
|
+ struct cert_auth_info *cai;
|
|||
|
+ struct cert_auth_info *cai_next;
|
|||
|
+
|
|||
|
+ if (list != NULL) {
|
|||
|
+ DLIST_FOR_EACH_SAFE(cai, cai_next, list) {
|
|||
|
+ DLIST_REMOVE(list, cai);
|
|||
|
+ free_cai(cai);
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+}
|
|||
|
+
|
|||
|
static void overwrite_and_free_authtoks(struct pam_items *pi)
|
|||
|
{
|
|||
|
if (pi->pam_authtok != NULL) {
|
|||
|
@@ -158,17 +197,9 @@ static void overwrite_and_free_pam_items(struct pam_items *pi)
|
|||
|
free(pi->otp_challenge);
|
|||
|
pi->otp_challenge = NULL;
|
|||
|
|
|||
|
- free(pi->cert_user);
|
|||
|
- pi->cert_user = NULL;
|
|||
|
-
|
|||
|
- free(pi->token_name);
|
|||
|
- pi->token_name = NULL;
|
|||
|
-
|
|||
|
- free(pi->module_name);
|
|||
|
- pi->module_name = NULL;
|
|||
|
-
|
|||
|
- free(pi->key_id);
|
|||
|
- pi->key_id = NULL;
|
|||
|
+ free_cert_list(pi->cert_list);
|
|||
|
+ pi->cert_list = NULL;
|
|||
|
+ pi->selected_cert = NULL;
|
|||
|
}
|
|||
|
|
|||
|
static int null_strcmp(const char *s1, const char *s2) {
|
|||
|
@@ -821,6 +852,90 @@ static int eval_user_info_response(pam_handle_t *pamh, size_t buflen,
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
+static int parse_cert_info(struct pam_items *pi, uint8_t *buf, size_t len,
|
|||
|
+ size_t *p, const char **cert_user)
|
|||
|
+{
|
|||
|
+ struct cert_auth_info *cai = NULL;
|
|||
|
+ size_t offset;
|
|||
|
+ int ret;
|
|||
|
+
|
|||
|
+ if (buf[*p + (len - 1)] != '\0') {
|
|||
|
+ D(("cert info does not end with \\0."));
|
|||
|
+ return EINVAL;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ cai = calloc(1, sizeof(struct cert_auth_info));
|
|||
|
+ if (cai == NULL) {
|
|||
|
+ return ENOMEM;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ cai->cert_user = strdup((char *) &buf[*p]);
|
|||
|
+ if (cai->cert_user == NULL) {
|
|||
|
+ D(("strdup failed"));
|
|||
|
+ ret = ENOMEM;
|
|||
|
+ goto done;
|
|||
|
+ }
|
|||
|
+ if (cert_user != NULL) {
|
|||
|
+ *cert_user = cai->cert_user;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ offset = strlen(cai->cert_user) + 1;
|
|||
|
+ if (offset >= len) {
|
|||
|
+ D(("Cert message size mismatch"));
|
|||
|
+ ret = EINVAL;
|
|||
|
+ goto done;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ cai->token_name = strdup((char *) &buf[*p + offset]);
|
|||
|
+ if (cai->token_name == NULL) {
|
|||
|
+ D(("strdup failed"));
|
|||
|
+ ret = ENOMEM;
|
|||
|
+ goto done;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ offset += strlen(cai->token_name) + 1;
|
|||
|
+ if (offset >= len) {
|
|||
|
+ D(("Cert message size mismatch"));
|
|||
|
+ ret = EINVAL;
|
|||
|
+ goto done;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ cai->module_name = strdup((char *) &buf[*p + offset]);
|
|||
|
+ if (cai->module_name == NULL) {
|
|||
|
+ D(("strdup failed"));
|
|||
|
+ ret = ENOMEM;
|
|||
|
+ goto done;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ offset += strlen(cai->module_name) + 1;
|
|||
|
+ if (offset >= len) {
|
|||
|
+ D(("Cert message size mismatch"));
|
|||
|
+ ret = EINVAL;
|
|||
|
+ goto done;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ cai->key_id = strdup((char *) &buf[*p + offset]);
|
|||
|
+ if (cai->key_id == NULL) {
|
|||
|
+ D(("strdup failed"));
|
|||
|
+ ret = ENOMEM;
|
|||
|
+ goto done;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ D(("cert user: [%s] token name: [%s] module: [%s] key id: [%s]",
|
|||
|
+ cai->cert_user, cai->token_name, cai->module_name,
|
|||
|
+ cai->key_id));
|
|||
|
+
|
|||
|
+ DLIST_ADD(pi->cert_list, cai);
|
|||
|
+ ret = 0;
|
|||
|
+
|
|||
|
+done:
|
|||
|
+ if (ret != 0) {
|
|||
|
+ free_cai(cai);
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ return ret;
|
|||
|
+}
|
|||
|
+
|
|||
|
static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
|
|||
|
struct pam_items *pi)
|
|||
|
{
|
|||
|
@@ -832,6 +947,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
|
|||
|
int32_t len;
|
|||
|
int32_t pam_status;
|
|||
|
size_t offset;
|
|||
|
+ const char *cert_user;
|
|||
|
|
|||
|
if (buflen < (2*sizeof(int32_t))) {
|
|||
|
D(("response buffer is too small"));
|
|||
|
@@ -988,27 +1104,21 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
- free(pi->cert_user);
|
|||
|
- pi->cert_user = strdup((char *) &buf[p]);
|
|||
|
- if (pi->cert_user == NULL) {
|
|||
|
- D(("strdup failed"));
|
|||
|
- break;
|
|||
|
- }
|
|||
|
-
|
|||
|
- if (type == SSS_PAM_CERT_INFO && *pi->cert_user == '\0') {
|
|||
|
- D(("Invalid CERT message"));
|
|||
|
- break;
|
|||
|
- }
|
|||
|
-
|
|||
|
if (type == SSS_PAM_CERT_INFO_WITH_HINT) {
|
|||
|
pi->user_name_hint = true;
|
|||
|
} else {
|
|||
|
pi->user_name_hint = false;
|
|||
|
}
|
|||
|
|
|||
|
+ ret = parse_cert_info(pi, buf, len, &p, &cert_user);
|
|||
|
+ if (ret != 0) {
|
|||
|
+ D(("Failed to parse cert info"));
|
|||
|
+ break;
|
|||
|
+ }
|
|||
|
+
|
|||
|
if ((pi->pam_user == NULL || *(pi->pam_user) == '\0')
|
|||
|
- && *pi->cert_user != '\0') {
|
|||
|
- ret = pam_set_item(pamh, PAM_USER, pi->cert_user);
|
|||
|
+ && *cert_user != '\0') {
|
|||
|
+ ret = pam_set_item(pamh, PAM_USER, cert_user);
|
|||
|
if (ret != PAM_SUCCESS) {
|
|||
|
D(("Failed to set PAM_USER during "
|
|||
|
"Smartcard authentication [%s]",
|
|||
|
@@ -1027,59 +1137,6 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
|
|||
|
|
|||
|
pi->pam_user_size = strlen(pi->pam_user) + 1;
|
|||
|
}
|
|||
|
-
|
|||
|
- offset = strlen(pi->cert_user) + 1;
|
|||
|
- if (offset >= len) {
|
|||
|
- D(("Cert message size mismatch"));
|
|||
|
- free(pi->cert_user);
|
|||
|
- pi->cert_user = NULL;
|
|||
|
- break;
|
|||
|
- }
|
|||
|
- free(pi->token_name);
|
|||
|
- pi->token_name = strdup((char *) &buf[p + offset]);
|
|||
|
- if (pi->token_name == NULL) {
|
|||
|
- D(("strdup failed"));
|
|||
|
- free(pi->cert_user);
|
|||
|
- pi->cert_user = NULL;
|
|||
|
- break;
|
|||
|
- }
|
|||
|
-
|
|||
|
- offset += strlen(pi->token_name) + 1;
|
|||
|
- if (offset >= len) {
|
|||
|
- D(("Cert message size mismatch"));
|
|||
|
- free(pi->cert_user);
|
|||
|
- pi->cert_user = NULL;
|
|||
|
- free(pi->token_name);
|
|||
|
- pi->token_name = NULL;
|
|||
|
- break;
|
|||
|
- }
|
|||
|
- free(pi->module_name);
|
|||
|
- pi->module_name = strdup((char *) &buf[p + offset]);
|
|||
|
- if (pi->module_name == NULL) {
|
|||
|
- D(("strdup failed"));
|
|||
|
- break;
|
|||
|
- }
|
|||
|
-
|
|||
|
- offset += strlen(pi->module_name) + 1;
|
|||
|
- if (offset >= len) {
|
|||
|
- D(("Cert message size mismatch"));
|
|||
|
- free(pi->cert_user);
|
|||
|
- pi->cert_user = NULL;
|
|||
|
- free(pi->token_name);
|
|||
|
- pi->token_name = NULL;
|
|||
|
- free(pi->module_name);
|
|||
|
- pi->module_name = NULL;
|
|||
|
- break;
|
|||
|
- }
|
|||
|
- free(pi->key_id);
|
|||
|
- pi->key_id = strdup((char *) &buf[p + offset]);
|
|||
|
- if (pi->key_id == NULL) {
|
|||
|
- D(("strdup failed"));
|
|||
|
- break;
|
|||
|
- }
|
|||
|
- D(("cert user: [%s] token name: [%s] module: [%s] key id: [%s]",
|
|||
|
- pi->cert_user, pi->token_name, pi->module_name,
|
|||
|
- pi->key_id));
|
|||
|
break;
|
|||
|
case SSS_PASSWORD_PROMPTING:
|
|||
|
D(("Password prompting available."));
|
|||
|
@@ -1175,10 +1232,8 @@ static int get_pam_items(pam_handle_t *pamh, uint32_t flags,
|
|||
|
pi->otp_challenge = NULL;
|
|||
|
pi->password_prompting = false;
|
|||
|
|
|||
|
- pi->cert_user = NULL;
|
|||
|
- pi->token_name = NULL;
|
|||
|
- pi->module_name = NULL;
|
|||
|
- pi->key_id = NULL;
|
|||
|
+ pi->cert_list = NULL;
|
|||
|
+ pi->selected_cert = NULL;
|
|||
|
|
|||
|
return PAM_SUCCESS;
|
|||
|
}
|
|||
|
@@ -1484,6 +1539,184 @@ done:
|
|||
|
|
|||
|
#define SC_PROMPT_FMT "PIN for %s"
|
|||
|
|
|||
|
+#ifndef discard_const
|
|||
|
+#define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
|
|||
|
+#endif
|
|||
|
+
|
|||
|
+#define CERT_SEL_PROMPT_FMT "Certificate: %s"
|
|||
|
+#define SEL_TITLE discard_const("Please select a certificate")
|
|||
|
+
|
|||
|
+static int prompt_multi_cert_gdm(pam_handle_t *pamh, struct pam_items *pi)
|
|||
|
+{
|
|||
|
+#ifdef HAVE_GDM_PAM_EXTENSIONS
|
|||
|
+ int ret;
|
|||
|
+ size_t cert_count = 0;
|
|||
|
+ size_t c;
|
|||
|
+ const struct pam_conv *conv;
|
|||
|
+ struct cert_auth_info *cai;
|
|||
|
+ GdmPamExtensionChoiceListRequest *request = NULL;
|
|||
|
+ GdmPamExtensionChoiceListResponse *response = NULL;
|
|||
|
+ struct pam_message prompt_message;
|
|||
|
+ const struct pam_message *prompt_messages[1];
|
|||
|
+ struct pam_response *reply = NULL;
|
|||
|
+ char *prompt;
|
|||
|
+
|
|||
|
+ if (!GDM_PAM_EXTENSION_SUPPORTED(GDM_PAM_EXTENSION_CHOICE_LIST)) {
|
|||
|
+ return ENOTSUP;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ if (pi->cert_list == NULL) {
|
|||
|
+ return EINVAL;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ DLIST_FOR_EACH(cai, pi->cert_list) {
|
|||
|
+ cert_count++;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ ret = pam_get_item(pamh, PAM_CONV, (const void **)&conv);
|
|||
|
+ if (ret != PAM_SUCCESS) {
|
|||
|
+ ret = EIO;
|
|||
|
+ return ret;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ request = calloc(1, GDM_PAM_EXTENSION_CHOICE_LIST_REQUEST_SIZE(cert_count));
|
|||
|
+ if (request == NULL) {
|
|||
|
+ ret = ENOMEM;
|
|||
|
+ goto done;
|
|||
|
+ }
|
|||
|
+ GDM_PAM_EXTENSION_CHOICE_LIST_REQUEST_INIT(request, SEL_TITLE, cert_count);
|
|||
|
+
|
|||
|
+ c = 0;
|
|||
|
+ DLIST_FOR_EACH(cai, pi->cert_list) {
|
|||
|
+ ret = asprintf(&prompt, CERT_SEL_PROMPT_FMT, cai->key_id);
|
|||
|
+ if (ret == -1) {
|
|||
|
+ ret = ENOMEM;
|
|||
|
+ goto done;
|
|||
|
+ }
|
|||
|
+ request->list.items[c].key = cai->key_id;
|
|||
|
+ request->list.items[c++].text = prompt;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ GDM_PAM_EXTENSION_MESSAGE_TO_BINARY_PROMPT_MESSAGE(request,
|
|||
|
+ &prompt_message);
|
|||
|
+ prompt_messages[0] = &prompt_message;
|
|||
|
+
|
|||
|
+ ret = conv->conv(1, prompt_messages, &reply, conv->appdata_ptr);
|
|||
|
+ if (ret != PAM_SUCCESS) {
|
|||
|
+ ret = EIO;
|
|||
|
+ goto done;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ ret = EIO;
|
|||
|
+ response = GDM_PAM_EXTENSION_REPLY_TO_CHOICE_LIST_RESPONSE(reply);
|
|||
|
+ if (response->key == NULL) {
|
|||
|
+ goto done;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ DLIST_FOR_EACH(cai, pi->cert_list) {
|
|||
|
+ if (strcmp(response->key, cai->key_id) == 0) {
|
|||
|
+ pam_info(pamh, "Certificate ‘%s’ selected", cai->key_id);
|
|||
|
+ pi->selected_cert = cai;
|
|||
|
+ ret = 0;
|
|||
|
+ break;
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+
|
|||
|
+done:
|
|||
|
+ if (request != NULL) {
|
|||
|
+ for (c = 0; c < cert_count; c++) {
|
|||
|
+ free(discard_const(request->list.items[c++].text));
|
|||
|
+ }
|
|||
|
+ free(request);
|
|||
|
+ }
|
|||
|
+ free(response);
|
|||
|
+
|
|||
|
+ return ret;
|
|||
|
+#else
|
|||
|
+ return ENOTSUP;
|
|||
|
+#endif
|
|||
|
+}
|
|||
|
+
|
|||
|
+#define TEXT_CERT_SEL_PROMPT_FMT "%s[%zu] Certificate: %s\n"
|
|||
|
+#define TEXT_SEL_TITLE discard_const("Please select a certificate by typing " \
|
|||
|
+ "the corresponding number\n")
|
|||
|
+static int prompt_multi_cert(pam_handle_t *pamh, struct pam_items *pi)
|
|||
|
+{
|
|||
|
+ int ret;
|
|||
|
+ size_t cert_count = 0;
|
|||
|
+ size_t tries = 0;
|
|||
|
+ long int resp = -1;
|
|||
|
+ struct cert_auth_info *cai;
|
|||
|
+ char *prompt;
|
|||
|
+ char *tmp;
|
|||
|
+ char *answer;
|
|||
|
+ char *ep;
|
|||
|
+
|
|||
|
+ /* First check if gdm extension is supported */
|
|||
|
+ ret = prompt_multi_cert_gdm(pamh, pi);
|
|||
|
+ if (ret != ENOTSUP) {
|
|||
|
+ return ret;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ if (pi->cert_list == NULL) {
|
|||
|
+ return EINVAL;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ prompt = strdup(TEXT_SEL_TITLE);
|
|||
|
+ if (prompt == NULL) {
|
|||
|
+ return ENOMEM;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ DLIST_FOR_EACH(cai, pi->cert_list) {
|
|||
|
+ cert_count++;
|
|||
|
+ ret = asprintf(&tmp, TEXT_CERT_SEL_PROMPT_FMT, prompt, cert_count,
|
|||
|
+ cai->key_id);
|
|||
|
+ free(prompt);
|
|||
|
+ if (ret == -1) {
|
|||
|
+ return ENOMEM;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ prompt = tmp;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ do {
|
|||
|
+ ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_ON, prompt, NULL,
|
|||
|
+ &answer);
|
|||
|
+ if (ret != PAM_SUCCESS) {
|
|||
|
+ D(("do_pam_conversation failed."));
|
|||
|
+ break;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ errno = 0;
|
|||
|
+ resp = strtol(answer, &ep, 10);
|
|||
|
+ if (errno == 0 && *ep == '\0' && resp > 0 && resp <= cert_count) {
|
|||
|
+ /* do not free answer ealier because ep is pointing to it */
|
|||
|
+ free(answer);
|
|||
|
+ break;
|
|||
|
+ }
|
|||
|
+ free(answer);
|
|||
|
+ resp = -1;
|
|||
|
+ } while (++tries < 5);
|
|||
|
+ free(prompt);
|
|||
|
+
|
|||
|
+ pi->selected_cert = NULL;
|
|||
|
+ ret = ENOENT;
|
|||
|
+ if (resp > 0 && resp <= cert_count) {
|
|||
|
+ cert_count = 0;
|
|||
|
+ DLIST_FOR_EACH(cai, pi->cert_list) {
|
|||
|
+ cert_count++;
|
|||
|
+ if (resp == cert_count) {
|
|||
|
+ pam_info(pamh, "Certificate ‘%s’ selected", cai->key_id);
|
|||
|
+ pi->selected_cert = cai;
|
|||
|
+ ret = 0;
|
|||
|
+ break;
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ return ret;
|
|||
|
+}
|
|||
|
+
|
|||
|
static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
|
|||
|
{
|
|||
|
int ret;
|
|||
|
@@ -1495,19 +1728,20 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
|
|||
|
const struct pam_message *mesg[2] = { NULL, NULL };
|
|||
|
struct pam_message m[2] = { { 0 }, { 0 } };
|
|||
|
struct pam_response *resp = NULL;
|
|||
|
+ struct cert_auth_info *cai = pi->selected_cert;
|
|||
|
|
|||
|
- if (pi->token_name == NULL || *pi->token_name == '\0') {
|
|||
|
+ if (cai == NULL || cai->token_name == NULL || *cai->token_name == '\0') {
|
|||
|
return EINVAL;
|
|||
|
}
|
|||
|
|
|||
|
- size = sizeof(SC_PROMPT_FMT) + strlen(pi->token_name);
|
|||
|
+ size = sizeof(SC_PROMPT_FMT) + strlen(cai->token_name);
|
|||
|
prompt = malloc(size);
|
|||
|
if (prompt == NULL) {
|
|||
|
D(("malloc failed."));
|
|||
|
return ENOMEM;
|
|||
|
}
|
|||
|
|
|||
|
- ret = snprintf(prompt, size, SC_PROMPT_FMT, pi->token_name);
|
|||
|
+ ret = snprintf(prompt, size, SC_PROMPT_FMT, cai->token_name);
|
|||
|
if (ret < 0 || ret >= size) {
|
|||
|
D(("snprintf failed."));
|
|||
|
free(prompt);
|
|||
|
@@ -1604,9 +1838,9 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
|
|||
|
pi->pam_authtok_size=0;
|
|||
|
} else {
|
|||
|
|
|||
|
- ret = sss_auth_pack_sc_blob(answer, 0, pi->token_name, 0,
|
|||
|
- pi->module_name, 0,
|
|||
|
- pi->key_id, 0,
|
|||
|
+ ret = sss_auth_pack_sc_blob(answer, 0, cai->token_name, 0,
|
|||
|
+ cai->module_name, 0,
|
|||
|
+ cai->key_id, 0,
|
|||
|
NULL, 0, &needed_size);
|
|||
|
if (ret != EAGAIN) {
|
|||
|
D(("sss_auth_pack_sc_blob failed."));
|
|||
|
@@ -1621,9 +1855,9 @@ static int prompt_sc_pin(pam_handle_t *pamh, struct pam_items *pi)
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
- ret = sss_auth_pack_sc_blob(answer, 0, pi->token_name, 0,
|
|||
|
- pi->module_name, 0,
|
|||
|
- pi->key_id, 0,
|
|||
|
+ ret = sss_auth_pack_sc_blob(answer, 0, cai->token_name, 0,
|
|||
|
+ cai->module_name, 0,
|
|||
|
+ cai->key_id, 0,
|
|||
|
(uint8_t *) pi->pam_authtok, needed_size,
|
|||
|
&needed_size);
|
|||
|
if (ret != EOK) {
|
|||
|
@@ -1786,7 +2020,17 @@ static int get_authtok_for_authentication(pam_handle_t *pamh,
|
|||
|
ret = prompt_2fa(pamh, pi, _("First Factor: "),
|
|||
|
_("Second Factor: "));
|
|||
|
}
|
|||
|
- } else if (pi->token_name != NULL && *(pi->token_name) != '\0') {
|
|||
|
+ } else if (pi->cert_list != NULL) {
|
|||
|
+ if (pi->cert_list->next == NULL) {
|
|||
|
+ /* Only one certificate */
|
|||
|
+ pi->selected_cert = pi->cert_list;
|
|||
|
+ } else {
|
|||
|
+ ret = prompt_multi_cert(pamh, pi);
|
|||
|
+ if (ret != 0) {
|
|||
|
+ D(("Failed to select certificate"));
|
|||
|
+ return PAM_AUTHTOK_ERR;
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
ret = prompt_sc_pin(pamh, pi);
|
|||
|
} else {
|
|||
|
ret = prompt_password(pamh, pi, _("Password: "));
|
|||
|
@@ -1905,14 +2149,21 @@ static int check_login_token_name(pam_handle_t *pamh, struct pam_items *pi,
|
|||
|
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;
|
|||
|
+ }
|
|||
|
|
|||
|
login_token_name = getenv("PKCS11_LOGIN_TOKEN_NAME");
|
|||
|
if (login_token_name == NULL) {
|
|||
|
return PAM_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
- while (pi->token_name == NULL
|
|||
|
- || strcmp(login_token_name, pi->token_name) != 0) {
|
|||
|
+ 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) {
|
|||
|
--
|
|||
|
2.15.1
|
|||
|
|