URL: https://github.com/SSSD/sssd/pull/671 Author: sumit-bose Title: #671: PAM: use PKCS#11 URIs to restrict certificate selection Action: opened
PR body: """ With the new option 'p11_uri' to the PAM responder can be used to restrict the selection of certificates in p11_child with the help of a PKCS#11 URI. The implementation of for the NSS version of p11_child is not available in this PR. As you can see in the first patch the support for PKCS#11 URIs in NSS is limited and I have to talk to NSS developers first if this will change of if it would make more sense to use the PKCS#11 URI calls form libp11kit for the NSS version as well. To avoid rebase issues this PR is already on top of PR#668. Related to https://pagure.io/SSSD/sssd/issue/3814 """ To pull the PR as Git branch: git remote add ghsssd https://github.com/SSSD/sssd git fetch ghsssd pull/671/head:pr671 git checkout pr671
From c3b18db91f9a9b3101f62084f55b2e0fc770883d Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Fri, 14 Sep 2018 12:47:00 +0200 Subject: [PATCH 01/10] p11_child: add --wait_for_card option The --wait_for_card option will let the p11_child wait until a Smartcard/token is available in a slot with the removable flag. Related to https://pagure.io/SSSD/sssd/issue/3650 --- src/p11_child/p11_child.h | 5 +- src/p11_child/p11_child_common.c | 12 ++- src/p11_child/p11_child_nss.c | 105 ++++++++++++++++------- src/p11_child/p11_child_openssl.c | 136 ++++++++++++++++++++++++------ 4 files changed, 196 insertions(+), 62 deletions(-) diff --git a/src/p11_child/p11_child.h b/src/p11_child/p11_child.h index 1e9fc3d1c..dd8fdeafb 100644 --- a/src/p11_child/p11_child.h +++ b/src/p11_child/p11_child.h @@ -25,6 +25,9 @@ #ifndef __P11_CHILD_H__ #define __P11_CHILD_H__ +/* Time to wait during a C_Finalize C_Initialize cycle to discover + * new slots. */ +#define PKCS11_FINIALIZE_INITIALIZE_WAIT_TIME 3 struct p11_ctx; enum op_mode { @@ -41,7 +44,7 @@ enum pin_mode { }; errno_t init_p11_ctx(TALLOC_CTX *mem_ctx, const char *nss_db, - struct p11_ctx **p11_ctx); + bool wait_for_card, struct p11_ctx **p11_ctx); errno_t init_verification(struct p11_ctx *p11_ctx, struct cert_verify_opts *cert_verify_opts); diff --git a/src/p11_child/p11_child_common.c b/src/p11_child/p11_child_common.c index 125430d13..bc5f6b09b 100644 --- a/src/p11_child/p11_child_common.c +++ b/src/p11_child/p11_child_common.c @@ -57,6 +57,7 @@ static const char *op_mode_str(enum op_mode mode) static int do_work(TALLOC_CTX *mem_ctx, enum op_mode mode, const char *ca_db, struct cert_verify_opts *cert_verify_opts, + bool wait_for_card, const char *cert_b64, const char *pin, const char *module_name, const char *token_name, const char *key_id, char **multi) @@ -64,7 +65,7 @@ static int do_work(TALLOC_CTX *mem_ctx, enum op_mode mode, const char *ca_db, int ret; struct p11_ctx *p11_ctx; - ret = init_p11_ctx(mem_ctx, ca_db, &p11_ctx); + ret = init_p11_ctx(mem_ctx, ca_db, wait_for_card, &p11_ctx); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "init_p11_ctx failed.\n"); return ret; @@ -157,6 +158,7 @@ int main(int argc, const char *argv[]) char *token_name = NULL; char *key_id = NULL; char *cert_b64 = NULL; + bool wait_for_card = false; struct poptOption long_options[] = { POPT_AUTOHELP @@ -174,6 +176,7 @@ int main(int argc, const char *argv[]) SSSD_LOGGER_OPTS {"auth", 0, POPT_ARG_NONE, NULL, 'a', _("Run in auth mode"), NULL}, {"pre", 0, POPT_ARG_NONE, NULL, 'p', _("Run in pre-auth mode"), NULL}, + {"wait_for_card", 0, POPT_ARG_NONE, NULL, 'w', _("Wait until card is available"), NULL}, {"verification", 0, POPT_ARG_NONE, NULL, 'v', _("Run in verification mode"), NULL}, {"pin", 0, POPT_ARG_NONE, NULL, 'i', _("Expect PIN on stdin"), NULL}, @@ -258,6 +261,9 @@ int main(int argc, const char *argv[]) } pin_mode = PIN_KEYPAD; break; + case 'w': + wait_for_card = true; + break; default: fprintf(stderr, "\nInvalid option %s: %s\n\n", poptBadOption(pc, 0), poptStrerror(opt)); @@ -360,8 +366,8 @@ int main(int argc, const char *argv[]) } } - ret = do_work(main_ctx, mode, nss_db, cert_verify_opts, cert_b64, - pin, module_name, token_name, key_id, &multi); + ret = do_work(main_ctx, mode, nss_db, cert_verify_opts, wait_for_card, + cert_b64, pin, module_name, token_name, key_id, &multi); if (ret != 0) { DEBUG(SSSDBG_OP_FAILURE, "do_work failed.\n"); goto fail; diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c index d6a0b804a..b2777d1d2 100644 --- a/src/p11_child/p11_child_nss.c +++ b/src/p11_child/p11_child_nss.c @@ -51,6 +51,7 @@ struct p11_ctx { CERTCertDBHandle *handle; struct cert_verify_opts *cert_verify_opts; const char *nss_db; + bool wait_for_card; }; #define EXP_USAGES ( certificateUsageSSLClient \ @@ -141,6 +142,19 @@ static int talloc_free_handle(struct p11_ctx *p11_ctx) return 0; } +static NSSInitContext *get_nss_ctx(const char *nss_db) +{ + uint32_t flags = NSS_INIT_READONLY + | NSS_INIT_FORCEOPEN + | NSS_INIT_NOROOTINIT + | NSS_INIT_OPTIMIZESPACE + | NSS_INIT_PK11RELOAD; + NSSInitParameters parameters = { 0 }; + parameters.length = sizeof (parameters); + + return NSS_InitContext(nss_db, "", "", SECMOD_DB, ¶meters, flags); +} + errno_t init_verification(struct p11_ctx *p11_ctx, struct cert_verify_opts *cert_verify_opts) { @@ -256,14 +270,15 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, SECItem signed_random_value = {0}; SECKEYPublicKey *pub_key; CERTCertificate *found_cert = NULL; - PK11SlotList *list = NULL; - PK11SlotListElement *le; const char *label; char *key_id_str = NULL; CERTCertList *valid_certs = NULL; char *cert_b64 = NULL; char *multi = NULL; PRCList *node; + CK_SLOT_INFO slInfo; + PK11TokenStatus token_status; + size_t s; PK11_SetPasswordFunc(password_passthrough); @@ -297,28 +312,50 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, mod_list_item->module->dllName); } - list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, - NULL); - if (list == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "PK11_GetAllTokens failed.\n"); - ret = EIO; - goto done; - } + for (;;) { + mod_list = SECMOD_GetDefaultModuleList(); + for (mod_list_item = mod_list; mod_list_item != NULL; + mod_list_item = mod_list_item->next) { + for (s = 0; s < mod_list_item->module->slotCount; s++) { + slInfo.flags = 0; + rv = PK11_GetSlotInfo(mod_list_item->module->slots[s], &slInfo); + DEBUG(SSSDBG_TRACE_ALL, + "Description [%s] Manufacturer [%s] flags [%lu] " + "removable [%s] token present [%s].\n", + slInfo.slotDescription, slInfo.manufacturerID, + slInfo.flags, + (slInfo.flags & CKF_REMOVABLE_DEVICE) ? "true": "false", + (slInfo.flags & CKF_TOKEN_PRESENT) ? "true": "false"); + + if (rv == SECSuccess && (slInfo.flags & CKF_REMOVABLE_DEVICE)) { + slot = PK11_ReferenceSlot(mod_list_item->module->slots[s]); + break; + } + } + } - for (le = list->head; le; le = le->next) { - CK_SLOT_INFO slInfo; + /* When e.g. using Yubikeys the slot isn't present until the device is + * inserted, so we should wait for a slot as well. */ + if (p11_ctx->wait_for_card && slot == NULL) { + rv = NSS_ShutdownContext(p11_ctx->nss_ctx); + if (rv != SECSuccess) { + DEBUG(SSSDBG_OP_FAILURE, "NSS_ShutdownContext failed [%d][%s].\n", + PR_GetError(), PORT_ErrorToString(PR_GetError())); + } - slInfo.flags = 0; - rv = PK11_GetSlotInfo(le->slot, &slInfo); - DEBUG(SSSDBG_TRACE_ALL, - "Description [%s] Manufacturer [%s] flags [%lu].\n", - slInfo.slotDescription, slInfo.manufacturerID, slInfo.flags); - if (rv == SECSuccess && (slInfo.flags & CKF_REMOVABLE_DEVICE)) { - slot = PK11_ReferenceSlot(le->slot); + sleep(PKCS11_FINIALIZE_INITIALIZE_WAIT_TIME); + + p11_ctx->nss_ctx = get_nss_ctx(p11_ctx->nss_db); + if (p11_ctx->nss_ctx == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "NSS_InitContext failed [%d][%s].\n", + PR_GetError(), PORT_ErrorToString(PR_GetError())); + return EIO; + } + } else { break; } } - PK11_FreeSlotList(list); + if (slot == NULL) { DEBUG(SSSDBG_OP_FAILURE, "No removable slots found.\n"); ret = EIO; @@ -332,6 +369,22 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, module = PK11_GetModule(slot); module_name = module->dllName == NULL ? "NSS-Internal" : module->dllName; + if (!(slInfo.flags & CKF_TOKEN_PRESENT)) { + DEBUG(SSSDBG_TRACE_ALL, "Token not present.\n"); + if (p11_ctx->wait_for_card) { + token_status = PK11_WaitForTokenEvent(slot, PK11TokenPresentEvent, + PR_INTERVAL_NO_TIMEOUT, 0, 0); + if (token_status != PK11TokenPresent) { + DEBUG(SSSDBG_OP_FAILURE, "PK11_WaitForTokenEvent failed.\n"); + ret = EIO; + goto done; + } + } else { + ret = EIO; + goto done; + } + } + DEBUG(SSSDBG_TRACE_ALL, "Found [%s] in slot [%s][%d] of module [%d][%s].\n", token_name, slot_name, (int) slot_id, (int) module_id, module_name); @@ -651,26 +704,18 @@ static int talloc_nss_shutdown(struct p11_ctx *p11_ctx) } errno_t init_p11_ctx(TALLOC_CTX *mem_ctx, const char *nss_db, - struct p11_ctx **p11_ctx) + bool wait_for_card, struct p11_ctx **p11_ctx) { struct p11_ctx *ctx; - uint32_t flags = NSS_INIT_READONLY - | NSS_INIT_FORCEOPEN - | NSS_INIT_NOROOTINIT - | NSS_INIT_OPTIMIZESPACE - | NSS_INIT_PK11RELOAD; - NSSInitParameters parameters = { 0 }; - parameters.length = sizeof (parameters); - ctx = talloc_zero(mem_ctx, struct p11_ctx); if (ctx == NULL) { DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n"); return ENOMEM; } ctx->nss_db = nss_db; + ctx->wait_for_card = wait_for_card; - ctx->nss_ctx = NSS_InitContext(nss_db, "", "", SECMOD_DB, ¶meters, - flags); + ctx->nss_ctx = get_nss_ctx(nss_db); if (ctx->nss_ctx == NULL) { DEBUG(SSSDBG_OP_FAILURE, "NSS_InitContext failed [%d][%s].\n", PR_GetError(), PORT_ErrorToString(PR_GetError())); diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c index bf4418f86..d4572d99c 100644 --- a/src/p11_child/p11_child_openssl.c +++ b/src/p11_child/p11_child_openssl.c @@ -40,6 +40,7 @@ struct p11_ctx { X509_STORE *x509_store; const char *ca_db; + bool wait_for_card; }; static int talloc_cleanup_openssl(struct p11_ctx *p11_ctx) @@ -48,8 +49,9 @@ static int talloc_cleanup_openssl(struct p11_ctx *p11_ctx) return 0; } + errno_t init_p11_ctx(TALLOC_CTX *mem_ctx, const char *ca_db, - struct p11_ctx **p11_ctx) + bool wait_for_card, struct p11_ctx **p11_ctx) { int ret; struct p11_ctx *ctx; @@ -73,6 +75,7 @@ errno_t init_p11_ctx(TALLOC_CTX *mem_ctx, const char *ca_db, } ctx->ca_db = ca_db; + ctx->wait_for_card = wait_for_card; talloc_set_destructor(ctx, talloc_cleanup_openssl); *p11_ctx = ctx; @@ -547,6 +550,45 @@ static int sign_data(CK_FUNCTION_LIST *module, CK_SESSION_HANDLE session, return ret; } +static errno_t wait_for_card(CK_FUNCTION_LIST *module, CK_SLOT_ID *slot_id) +{ + CK_FLAGS wait_flags = 0; + CK_RV rv; + CK_SLOT_INFO info; + + rv = module->C_WaitForSlotEvent(wait_flags, slot_id, NULL); + if (rv != CKR_OK) { + if (rv != CKR_FUNCTION_NOT_SUPPORTED) { + DEBUG(SSSDBG_OP_FAILURE, + "C_WaitForSlotEvent failed [%lu][%s].\n", + rv, p11_kit_strerror(rv)); + return EIO; + } + + /* Poor man's wait */ + do { + sleep(10); + rv = module->C_GetSlotInfo(*slot_id, &info); + if (rv != CKR_OK) { + DEBUG(SSSDBG_OP_FAILURE, "C_GetSlotInfo failed\n"); + return EIO; + } + DEBUG(SSSDBG_TRACE_ALL, + "Description [%s] Manufacturer [%s] flags [%lu] " + "removable [%s] token present [%s].\n", + info.slotDescription, info.manufacturerID, info.flags, + (info.flags & CKF_REMOVABLE_DEVICE) ? "true": "false", + (info.flags & CKF_TOKEN_PRESENT) ? "true": "false"); + if ((info.flags & CKF_REMOVABLE_DEVICE) + && (info.flags & CKF_TOKEN_PRESENT)) { + break; + } + } while (true); + } + + return EOK; +} + #define MAX_SLOTS 64 errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, @@ -588,39 +630,62 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, return EIO; } - DEBUG(SSSDBG_TRACE_ALL, "Module List:\n"); - for (c = 0; modules[c] != NULL; c++) { - mod_name = p11_kit_module_get_name(modules[c]); - mod_file_name = p11_kit_module_get_filename(modules[c]); - DEBUG(SSSDBG_TRACE_ALL, "common name: [%s].\n", mod_name); - DEBUG(SSSDBG_TRACE_ALL, "dll name: [%s].\n", mod_file_name); - free(mod_name); - free(mod_file_name); + for (;;) { + DEBUG(SSSDBG_TRACE_ALL, "Module List:\n"); + for (c = 0; modules[c] != NULL; c++) { + mod_name = p11_kit_module_get_name(modules[c]); + mod_file_name = p11_kit_module_get_filename(modules[c]); + DEBUG(SSSDBG_TRACE_ALL, "common name: [%s].\n", mod_name); + DEBUG(SSSDBG_TRACE_ALL, "dll name: [%s].\n", mod_file_name); + free(mod_name); + free(mod_file_name); - num_slots = MAX_SLOTS; - rv = modules[c]->C_GetSlotList(CK_TRUE, slots, &num_slots); - if (rv != CKR_OK) { - DEBUG(SSSDBG_OP_FAILURE, "C_GetSlotList failed.\n"); - ret = EIO; - goto done; - } - - for (s = 0; s < num_slots; s++) { - rv = modules[c]->C_GetSlotInfo(slots[s], &info); + num_slots = MAX_SLOTS; + rv = modules[c]->C_GetSlotList(CK_FALSE, slots, &num_slots); if (rv != CKR_OK) { - DEBUG(SSSDBG_OP_FAILURE, "C_GetSlotInfo failed\n"); + DEBUG(SSSDBG_OP_FAILURE, "C_GetSlotList failed.\n"); ret = EIO; goto done; } - DEBUG(SSSDBG_TRACE_ALL, - "Description [%s] Manufacturer [%s] flags [%lu] removable [%s].\n", - info.slotDescription, info.manufacturerID, info.flags, - (info.flags & CKF_REMOVABLE_DEVICE) ? "true": "false"); - if ((info.flags & CKF_REMOVABLE_DEVICE)) { + + for (s = 0; s < num_slots; s++) { + rv = modules[c]->C_GetSlotInfo(slots[s], &info); + if (rv != CKR_OK) { + DEBUG(SSSDBG_OP_FAILURE, "C_GetSlotInfo failed\n"); + ret = EIO; + goto done; + } + DEBUG(SSSDBG_TRACE_ALL, + "Description [%s] Manufacturer [%s] flags [%lu] " + "removable [%s] token present [%s].\n", + info.slotDescription, info.manufacturerID, info.flags, + (info.flags & CKF_REMOVABLE_DEVICE) ? "true": "false", + (info.flags & CKF_TOKEN_PRESENT) ? "true": "false"); + if ((info.flags & CKF_REMOVABLE_DEVICE)) { + break; + } + } + if (s != num_slots) { break; } } - if (s != num_slots) { + + /* When e.g. using Yubikeys the slot isn't present until the device is + * inserted, so we should wait for a slot as well. */ + if (p11_ctx->wait_for_card && modules[c] == NULL) { + p11_kit_modules_finalize_and_release(modules); + + sleep(PKCS11_FINIALIZE_INITIALIZE_WAIT_TIME); + + modules = p11_kit_modules_load_and_initialize(0); + if (modules == NULL) { + DEBUG(SSSDBG_OP_FAILURE, + "p11_kit_modules_load_and_initialize failed.\n"); + ret = EIO; + goto done; + } + + } else { break; } } @@ -631,14 +696,29 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, goto done; } - rv = modules[c]->C_GetTokenInfo(slots[s], &token_info); + slot_id = slots[s]; + + if (!(info.flags & CKF_TOKEN_PRESENT)) { + DEBUG(SSSDBG_TRACE_ALL, "Token not present.\n"); + if (p11_ctx->wait_for_card) { + ret = wait_for_card(modules[c], &slot_id); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "wait_for_card failed.\n"); + goto done; + } + } else { + ret = EIO; + goto done; + } + } + + rv = modules[c]->C_GetTokenInfo(slot_id, &token_info); if (rv != CKR_OK) { DEBUG(SSSDBG_OP_FAILURE, "C_GetTokenInfo failed.\n"); ret = EIO; goto done; } - slot_id = slots[s]; module_id = c; slot_name = p11_kit_space_strdup(info.slotDescription, sizeof(info.slotDescription)); From 7ad34cfcfb7a1f80cfd37dc7e137eb7b2911a246 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Tue, 18 Sep 2018 18:15:02 +0200 Subject: [PATCH 02/10] PAM: add p11_wait_for_card_timeout option If the --wait_for_card is used to call p11_child the PAM responder should be prepared to wait longer until p11_child can return successfully. Related to https://pagure.io/SSSD/sssd/issue/3650 --- src/confdb/confdb.h | 1 + src/config/SSSDConfig/__init__.py.in | 1 + src/config/cfg_rules.ini | 1 + src/config/etc/sssd.api.conf | 1 + src/man/sssd.conf.5.xml | 14 ++++++++++++++ src/responder/pam/pamsrv_cmd.c | 15 +++++++++++++++ src/util/util.h | 1 + 7 files changed, 34 insertions(+) diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h index 625d15626..87904c214 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -130,6 +130,7 @@ #define CONFDB_PAM_CERT_AUTH "pam_cert_auth" #define CONFDB_PAM_CERT_DB_PATH "pam_cert_db_path" #define CONFDB_PAM_P11_CHILD_TIMEOUT "p11_child_timeout" +#define CONFDB_PAM_WAIT_FOR_CARD_TIMEOUT "p11_wait_for_card_timeout" #define CONFDB_PAM_APP_SERVICES "pam_app_services" #define CONFDB_PAM_P11_ALLOWED_SERVICES "pam_p11_allowed_services" diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in index 81a03adfe..4d1dba2d2 100644 --- a/src/config/SSSDConfig/__init__.py.in +++ b/src/config/SSSDConfig/__init__.py.in @@ -104,6 +104,7 @@ option_strings = { 'p11_child_timeout' : _('How many seconds will pam_sss wait for p11_child to finish'), 'pam_app_services' : _('Which PAM services are permitted to contact application domains'), 'pam_p11_allowed_services' : _('Allowed services for using smartcards'), + 'p11_wait_for_card_timeout' : _('Additional timeout to wait for a card if requested'), # [sudo] 'sudo_timed' : _('Whether to evaluate the time-based attributes in sudo rules'), diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini index 78f215ef9..50a8f1dfc 100644 --- a/src/config/cfg_rules.ini +++ b/src/config/cfg_rules.ini @@ -127,6 +127,7 @@ option = pam_cert_db_path option = p11_child_timeout option = pam_app_services option = pam_p11_allowed_services +option = p11_wait_for_card_timeout [rule/allowed_sudo_options] validator = ini_allowed_options diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf index 52494c0e6..bb686c344 100644 --- a/src/config/etc/sssd.api.conf +++ b/src/config/etc/sssd.api.conf @@ -76,6 +76,7 @@ pam_cert_db_path = str, None, false p11_child_timeout = int, None, false pam_app_services = str, None, false pam_p11_allowed_services = str, None, false +p11_wait_for_card_timeout = int, None, false [sudo] # sudo service diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml index c1e38950f..4df016331 100644 --- a/src/man/sssd.conf.5.xml +++ b/src/man/sssd.conf.5.xml @@ -1464,6 +1464,20 @@ pam_p11_allowed_services = +my_pam_service, -login </para> </listitem> </varlistentry> + <varlistentry> + <term>p11_wait_for_card_timeout (integer)</term> + <listitem> + <para> + If Smartcard authentication is required how many + extra seconds in addition to p11_child_timeout + should the PAM responder wait until a Smartcard is + inserted. + </para> + <para> + Default: 60 + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c index 817f3c513..c8df32de9 100644 --- a/src/responder/pam/pamsrv_cmd.c +++ b/src/responder/pam/pamsrv_cmd.c @@ -1297,6 +1297,7 @@ static errno_t check_cert(TALLOC_CTX *mctx, struct pam_data *pd) { int p11_child_timeout; + int wait_for_card_timeout; char *cert_verification_opts; errno_t ret; struct tevent_req *req; @@ -1311,6 +1312,20 @@ static errno_t check_cert(TALLOC_CTX *mctx, ret, sss_strerror(ret)); return ret; } + if ((pd->cli_flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH) && pd->priv == 1) { + ret = confdb_get_int(pctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_WAIT_FOR_CARD_TIMEOUT, + P11_WAIT_FOR_CARD_TIMEOUT_DEFAULT, + &wait_for_card_timeout); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to read wait_for_card_timeout from confdb: [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + p11_child_timeout += wait_for_card_timeout; + } ret = confdb_get_string(pctx->rctx->cdb, mctx, CONFDB_MONITOR_CONF_ENTRY, CONFDB_MONITOR_CERT_VERIFICATION, NULL, diff --git a/src/util/util.h b/src/util/util.h index 59e7a96ba..e3e910097 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -724,6 +724,7 @@ errno_t create_preauth_indicator(void); #define P11_CHILD_LOG_FILE "p11_child" #define P11_CHILD_PATH SSSD_LIBEXEC_PATH"/p11_child" #define P11_CHILD_TIMEOUT_DEFAULT 10 +#define P11_WAIT_FOR_CARD_TIMEOUT_DEFAULT 60 #endif /* SSSD_LIBEXEC_PATH */ #endif /* __SSSD_UTIL_H__ */ From 9d0df8607900506e903d525a36a3fe14b81cb9f4 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Tue, 18 Sep 2018 10:11:02 +0200 Subject: [PATCH 03/10] pam_sss: make flags public To allow the PAM responder to act on the config flags set for pam_sss the flags have to be made public first. Related to https://pagure.io/SSSD/sssd/issue/3650 --- src/sss_client/pam_sss.c | 71 ++++++++++++++++++---------------------- src/sss_client/sss_cli.h | 9 +++++ 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c index 59081cc67..b336d1f61 100644 --- a/src/sss_client/pam_sss.c +++ b/src/sss_client/pam_sss.c @@ -52,15 +52,6 @@ #include <libintl.h> #define _(STRING) dgettext (PACKAGE, STRING) -#define FLAGS_USE_FIRST_PASS (1 << 0) -#define FLAGS_FORWARD_PASS (1 << 1) -#define FLAGS_USE_AUTHTOK (1 << 2) -#define FLAGS_IGNORE_UNKNOWN_USER (1 << 3) -#define FLAGS_IGNORE_AUTHINFO_UNAVAIL (1 << 4) -#define FLAGS_USE_2FA (1 << 5) -#define FLAGS_ALLOW_MISSING_NAME (1 << 6) -#define FLAGS_PROMPT_ALWAYS (1 << 7) - #define PWEXP_FLAG "pam_sss:password_expired_flag" #define FD_DESTRUCTOR "pam_sss:fd_destructor" #define PAM_SSS_AUTHOK_TYPE "pam_sss:authtok_type" @@ -1193,13 +1184,13 @@ static int get_pam_items(pam_handle_t *pamh, uint32_t flags, pi->pam_service_size=strlen(pi->pam_service)+1; ret = pam_get_item(pamh, PAM_USER, (const void **) &(pi->pam_user)); - if (ret == PAM_PERM_DENIED && (flags & FLAGS_ALLOW_MISSING_NAME)) { + if (ret == PAM_PERM_DENIED && (flags & PAM_CLI_FLAGS_ALLOW_MISSING_NAME)) { pi->pam_user = ""; ret = PAM_SUCCESS; } if (ret != PAM_SUCCESS) return ret; if (pi->pam_user == NULL) { - if (flags & FLAGS_ALLOW_MISSING_NAME) { + if (flags & PAM_CLI_FLAGS_ALLOW_MISSING_NAME) { pi->pam_user = ""; } else { D(("No user found, aborting.")); @@ -1959,11 +1950,11 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv, for (; argc-- > 0; ++argv) { if (strcmp(*argv, "forward_pass") == 0) { - *flags |= FLAGS_FORWARD_PASS; + *flags |= PAM_CLI_FLAGS_FORWARD_PASS; } else if (strcmp(*argv, "use_first_pass") == 0) { - *flags |= FLAGS_USE_FIRST_PASS; + *flags |= PAM_CLI_FLAGS_USE_FIRST_PASS; } else if (strcmp(*argv, "use_authtok") == 0) { - *flags |= FLAGS_USE_AUTHTOK; + *flags |= PAM_CLI_FLAGS_USE_AUTHTOK; } else if (strncmp(*argv, OPT_DOMAINS_KEY, strlen(OPT_DOMAINS_KEY)) == 0) { if (*(*argv+strlen(OPT_DOMAINS_KEY)) == '\0') { logger(pamh, LOG_ERR, "Missing argument to option domains."); @@ -1997,15 +1988,15 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv, } else if (strcmp(*argv, "quiet") == 0) { *quiet_mode = true; } else if (strcmp(*argv, "ignore_unknown_user") == 0) { - *flags |= FLAGS_IGNORE_UNKNOWN_USER; + *flags |= PAM_CLI_FLAGS_IGNORE_UNKNOWN_USER; } else if (strcmp(*argv, "ignore_authinfo_unavail") == 0) { - *flags |= FLAGS_IGNORE_AUTHINFO_UNAVAIL; + *flags |= PAM_CLI_FLAGS_IGNORE_AUTHINFO_UNAVAIL; } else if (strcmp(*argv, "use_2fa") == 0) { - *flags |= FLAGS_USE_2FA; + *flags |= PAM_CLI_FLAGS_USE_2FA; } else if (strcmp(*argv, "allow_missing_name") == 0) { - *flags |= FLAGS_ALLOW_MISSING_NAME; + *flags |= PAM_CLI_FLAGS_ALLOW_MISSING_NAME; } else if (strcmp(*argv, "prompt_always") == 0) { - *flags |= FLAGS_PROMPT_ALWAYS; + *flags |= PAM_CLI_FLAGS_PROMPT_ALWAYS; } else { logger(pamh, LOG_WARNING, "unknown option: %s", *argv); } @@ -2020,10 +2011,10 @@ static int get_authtok_for_authentication(pam_handle_t *pamh, { int ret; - if ((flags & FLAGS_USE_FIRST_PASS) + if ((flags & PAM_CLI_FLAGS_USE_FIRST_PASS) || ( pi->pamstack_authtok != NULL && *(pi->pamstack_authtok) != '\0' - && !(flags & FLAGS_PROMPT_ALWAYS))) { + && !(flags & PAM_CLI_FLAGS_PROMPT_ALWAYS))) { pi->pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; pi->pam_authtok = strdup(pi->pamstack_authtok); if (pi->pam_authtok == NULL) { @@ -2032,7 +2023,7 @@ static int get_authtok_for_authentication(pam_handle_t *pamh, } pi->pam_authtok_size = strlen(pi->pam_authtok); } else { - if (flags & FLAGS_USE_2FA + if (flags & PAM_CLI_FLAGS_USE_2FA || (pi->otp_vendor != NULL && pi->otp_token_id != NULL && pi->otp_challenge != NULL)) { if (pi->password_prompting) { @@ -2062,7 +2053,7 @@ static int get_authtok_for_authentication(pam_handle_t *pamh, return ret; } - if (flags & FLAGS_FORWARD_PASS) { + if (flags & PAM_CLI_FLAGS_FORWARD_PASS) { if (pi->pam_authtok_type == SSS_AUTHTOK_TYPE_PASSWORD) { ret = pam_set_item(pamh, PAM_AUTHTOK, pi->pam_authtok); } else if (pi->pam_authtok_type == SSS_AUTHTOK_TYPE_2FA @@ -2193,8 +2184,8 @@ static int get_authtok_for_password_change(pam_handle_t *pamh, /* we query for the old password during PAM_PRELIM_CHECK to make * pam_sss work e.g. with pam_cracklib */ if (pam_flags & PAM_PRELIM_CHECK) { - if ( (getuid() != 0 || exp_data ) && !(flags & FLAGS_USE_FIRST_PASS)) { - if (flags & FLAGS_USE_2FA + if ( (getuid() != 0 || exp_data ) && !(flags & PAM_CLI_FLAGS_USE_FIRST_PASS)) { + if (flags & PAM_CLI_FLAGS_USE_2FA || (pi->otp_vendor != NULL && pi->otp_token_id != NULL && pi->otp_challenge != NULL)) { if (pi->password_prompting) { @@ -2253,7 +2244,7 @@ static int get_authtok_for_password_change(pam_handle_t *pamh, } } - if (flags & FLAGS_USE_AUTHTOK) { + if (flags & PAM_CLI_FLAGS_USE_AUTHTOK) { pi->pam_newauthtok_type = SSS_AUTHTOK_TYPE_PASSWORD; pi->pam_newauthtok = strdup(pi->pamstack_authtok); if (pi->pam_newauthtok == NULL) { @@ -2268,7 +2259,7 @@ static int get_authtok_for_password_change(pam_handle_t *pamh, return ret; } - if (flags & FLAGS_FORWARD_PASS) { + if (flags & PAM_CLI_FLAGS_FORWARD_PASS) { ret = pam_set_item(pamh, PAM_AUTHTOK, pi->pam_newauthtok); if (ret != PAM_SUCCESS) { D(("Failed to set PAM_AUTHTOK [%s], " @@ -2376,10 +2367,10 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, ret = get_pam_items(pamh, flags, &pi); if (ret != PAM_SUCCESS) { D(("get items returned error: %s", pam_strerror(pamh,ret))); - if (flags & FLAGS_IGNORE_UNKNOWN_USER && ret == PAM_USER_UNKNOWN) { + if (flags & PAM_CLI_FLAGS_IGNORE_UNKNOWN_USER && ret == PAM_USER_UNKNOWN) { ret = PAM_IGNORE; } - if (flags & FLAGS_IGNORE_AUTHINFO_UNAVAIL + if (flags & PAM_CLI_FLAGS_IGNORE_AUTHINFO_UNAVAIL && ret == PAM_AUTHINFO_UNAVAIL) { ret = PAM_IGNORE; } @@ -2393,13 +2384,13 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, case SSS_PAM_AUTHENTICATE: /* * Only do preauth if - * - FLAGS_USE_FIRST_PASS is not set - * - no password is on the stack or FLAGS_PROMPT_ALWAYS is set + * - PAM_CLI_FLAGS_USE_FIRST_PASS is not set + * - no password is on the stack or PAM_CLI_FLAGS_PROMPT_ALWAYS is set * - preauth indicator file exists. */ - if ( !(flags & FLAGS_USE_FIRST_PASS) + if ( !(flags & PAM_CLI_FLAGS_USE_FIRST_PASS) && (pi.pam_authtok == NULL - || (flags & FLAGS_PROMPT_ALWAYS)) + || (flags & PAM_CLI_FLAGS_PROMPT_ALWAYS)) && access(PAM_PREAUTH_INDICATOR, F_OK) == 0) { pam_status = send_and_receive(pamh, &pi, SSS_PAM_PREAUTH, quiet_mode); @@ -2443,14 +2434,14 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, * The means the preauth step has to be done here as well but * only if * - PAM_PRELIM_CHECK is set - * - FLAGS_USE_FIRST_PASS is not set - * - no password is on the stack or FLAGS_PROMPT_ALWAYS is set + * - PAM_CLI_FLAGS_USE_FIRST_PASS is not set + * - no password is on the stack or PAM_CLI_FLAGS_PROMPT_ALWAYS is set * - preauth indicator file exists. */ if ( (pam_flags & PAM_PRELIM_CHECK) - && !(flags & FLAGS_USE_FIRST_PASS) + && !(flags & PAM_CLI_FLAGS_USE_FIRST_PASS) && (pi.pam_authtok == NULL - || (flags & FLAGS_PROMPT_ALWAYS)) + || (flags & PAM_CLI_FLAGS_PROMPT_ALWAYS)) && access(PAM_PREAUTH_INDICATOR, F_OK) == 0) { pam_status = send_and_receive(pamh, &pi, SSS_PAM_PREAUTH, quiet_mode); @@ -2497,11 +2488,11 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, pam_status = send_and_receive(pamh, &pi, task, quiet_mode); - if (flags & FLAGS_IGNORE_UNKNOWN_USER + if (flags & PAM_CLI_FLAGS_IGNORE_UNKNOWN_USER && pam_status == PAM_USER_UNKNOWN) { pam_status = PAM_IGNORE; } - if (flags & FLAGS_IGNORE_AUTHINFO_UNAVAIL + if (flags & PAM_CLI_FLAGS_IGNORE_AUTHINFO_UNAVAIL && pam_status == PAM_AUTHINFO_UNAVAIL) { pam_status = PAM_IGNORE; } @@ -2581,7 +2572,7 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, retry = true; retries--; - flags &= ~FLAGS_USE_FIRST_PASS; + flags &= ~PAM_CLI_FLAGS_USE_FIRST_PASS; ret = pam_set_item(pamh, PAM_AUTHTOK, NULL); if (ret != PAM_SUCCESS) { D(("Failed to unset PAM_AUTHTOK [%s]", diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h index 24d28ed4b..3404715d8 100644 --- a/src/sss_client/sss_cli.h +++ b/src/sss_client/sss_cli.h @@ -365,6 +365,15 @@ enum pam_item_type { SSS_PAM_ITEM_REQUESTED_DOMAINS, }; +#define PAM_CLI_FLAGS_USE_FIRST_PASS (1 << 0) +#define PAM_CLI_FLAGS_FORWARD_PASS (1 << 1) +#define PAM_CLI_FLAGS_USE_AUTHTOK (1 << 2) +#define PAM_CLI_FLAGS_IGNORE_UNKNOWN_USER (1 << 3) +#define PAM_CLI_FLAGS_IGNORE_AUTHINFO_UNAVAIL (1 << 4) +#define PAM_CLI_FLAGS_USE_2FA (1 << 5) +#define PAM_CLI_FLAGS_ALLOW_MISSING_NAME (1 << 6) +#define PAM_CLI_FLAGS_PROMPT_ALWAYS (1 << 7) + #define SSS_NSS_MAX_ENTRIES 256 #define SSS_NSS_HEADER_SIZE (sizeof(uint32_t) * 4) struct sss_cli_req_data { From fcc6ed90b7471fb1c79deb4631c95ee42538cdbf Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Mon, 17 Sep 2018 17:54:26 +0200 Subject: [PATCH 04/10] pam_sss: add try_cert_auth option With this new option pam_sss can be configured to only do Smartcard authentication or return an error if this is not possible. Related to https://pagure.io/SSSD/sssd/issue/3650 --- src/man/pam_sss.8.xml | 23 +++++++++++++++++++++++ src/sss_client/pam_sss.c | 9 +++++++++ src/sss_client/sss_cli.h | 1 + 3 files changed, 33 insertions(+) diff --git a/src/man/pam_sss.8.xml b/src/man/pam_sss.8.xml index d8e6a2041..ca2e8e206 100644 --- a/src/man/pam_sss.8.xml +++ b/src/man/pam_sss.8.xml @@ -50,6 +50,9 @@ <arg choice='opt'> <replaceable>prompt_always</replaceable> </arg> + <arg choice='opt'> + <replaceable>try_cert_auth</replaceable> + </arg> </cmdsynopsis> </refsynopsisdiv> @@ -200,6 +203,26 @@ auth sufficient pam_sss.so allow_missing_name </para> </listitem> </varlistentry> + <varlistentry> + <term> + <option>try_cert_auth</option> + </term> + <listitem> + <para> + Try to use certificate based authentication, i.e. + authentication with a Smartcard or similar devices. If a + Smartcard is available and the service is allowed for + Smartcard authentication the use will be prompted for a + PIN and the certificate based authentication will + continue + </para> + <para> + If no Smartcard is available 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/sss_client/pam_sss.c b/src/sss_client/pam_sss.c index b336d1f61..96ff15ada 100644 --- a/src/sss_client/pam_sss.c +++ b/src/sss_client/pam_sss.c @@ -1997,6 +1997,8 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv, *flags |= PAM_CLI_FLAGS_ALLOW_MISSING_NAME; } else if (strcmp(*argv, "prompt_always") == 0) { *flags |= PAM_CLI_FLAGS_PROMPT_ALWAYS; + } else if (strcmp(*argv, "try_cert_auth") == 0) { + *flags |= PAM_CLI_FLAGS_TRY_CERT_AUTH; } else { logger(pamh, LOG_WARNING, "unknown option: %s", *argv); } @@ -2405,6 +2407,13 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, } } + if (flags & PAM_CLI_FLAGS_TRY_CERT_AUTH + && pi.cert_list == NULL) { + D(("No certificates for authentication available.")); + overwrite_and_free_pam_items(&pi); + return PAM_AUTHINFO_UNAVAIL; + } + if (strcmp(pi.pam_service, "gdm-smartcard") == 0) { ret = check_login_token_name(pamh, &pi, quiet_mode); if (ret != PAM_SUCCESS) { diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h index 3404715d8..38e3f999d 100644 --- a/src/sss_client/sss_cli.h +++ b/src/sss_client/sss_cli.h @@ -373,6 +373,7 @@ enum pam_item_type { #define PAM_CLI_FLAGS_USE_2FA (1 << 5) #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 SSS_NSS_MAX_ENTRIES 256 #define SSS_NSS_HEADER_SIZE (sizeof(uint32_t) * 4) From e256d9c3be1114a478a297ac05b5304ccddf39d1 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Tue, 18 Sep 2018 09:53:37 +0200 Subject: [PATCH 05/10] 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 --- 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 | 90 +++++++++++++++++++++------------- src/sss_client/sss_cli.h | 2 + src/util/sss_pam_data.c | 1 + src/util/sss_pam_data.h | 1 + 8 files changed, 81 insertions(+), 35 deletions(-) diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c index c8df32de9..6e37f8316 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 ffa6787e9..8b8859d9d 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 b239f6f53..036ae2ad1 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 11526a80a..50fedcd82 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 96ff15ada..b4c1036ad 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; + uint32_t orig_flags = pi->flags; - if (cai == NULL) { - D(("No certificate information available")); - return EINVAL; + login_token_name = getenv("PKCS11_LOGIN_TOKEN_NAME"); + if (login_token_name == NULL + && !(pi->flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH)) { + return PAM_SUCCESS; } - login_token_name = getenv("PKCS11_LOGIN_TOKEN_NAME"); 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; - ret = snprintf(prompt, size, SC_ENTER_FMT, - login_token_name); - if (ret < 0 || ret >= size) { - D(("snprintf failed.")); - free(prompt); - return EFAULT; + /* 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)) { + + 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 38e3f999d..af8a43916 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 5e41349b9..cb8779c1d 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 7d74fa6a0..c98981054 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; From ccbada8aa9bac020c2767c0f5ce9735ca7f19bc5 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Wed, 26 Sep 2018 11:48:37 +0200 Subject: [PATCH 06/10] intg: require SC tests Integration test for the new try_cert_auth and require_cert_auth option for pam_sss. Related to https://pagure.io/SSSD/sssd/issue/3650 --- src/tests/intg/Makefile.am | 16 ++- src/tests/intg/test_pam_responder.py | 188 ++++++++++++++++++++++++--- 2 files changed, 182 insertions(+), 22 deletions(-) diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am index bb3a7f01a..44fb6353a 100644 --- a/src/tests/intg/Makefile.am +++ b/src/tests/intg/Makefile.am @@ -113,6 +113,20 @@ pam_sss_service: echo "password required $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@ echo "session required $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@ +pam_sss_sc_required: + $(MKDIR_P) $(PAM_SERVICE_DIR) + echo "auth required $(DESTDIR)$(pammoddir)/pam_sss.so require_cert_auth retry=1" > $(PAM_SERVICE_DIR)/$@ + echo "account required $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@ + echo "password required $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@ + echo "session required $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@ + +pam_sss_try_sc: + $(MKDIR_P) $(PAM_SERVICE_DIR) + echo "auth required $(DESTDIR)$(pammoddir)/pam_sss.so try_cert_auth" > $(PAM_SERVICE_DIR)/$@ + echo "account required $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@ + echo "password required $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@ + echo "session required $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@ + CLEANFILES=config.py config.pyc passwd group clean-local: @@ -127,7 +141,7 @@ PAM_CERT_DB_PATH="$(abs_builddir)/../test_CA/SSSD_test_CA.pem" SOFTHSM2_CONF="$(abs_builddir)/../test_CA/softhsm2_one.conf" endif -intgcheck-installed: config.py passwd group pam_sss_service +intgcheck-installed: config.py passwd group pam_sss_service pam_sss_sc_required pam_sss_try_sc pipepath="$(DESTDIR)$(pipepath)"; \ if test $${#pipepath} -gt 80; then \ echo "error: Pipe directory path too long," \ diff --git a/src/tests/intg/test_pam_responder.py b/src/tests/intg/test_pam_responder.py index c6d048cd3..06f69a3d8 100644 --- a/src/tests/intg/test_pam_responder.py +++ b/src/tests/intg/test_pam_responder.py @@ -41,6 +41,11 @@ dir='/home/user1', shell='/bin/bash') +USER2 = dict(name='user2', passwd='x', uid=10002, gid=20002, + gecos='User with no Smartcard mapping', + dir='/home/user2', + shell='/bin/bash') + def format_pam_cert_auth_conf(config): """Format a basic SSSD configuration""" @@ -55,8 +60,11 @@ def format_pam_cert_auth_conf(config): [pam] pam_cert_auth = True - pam_p11_allowed_services = +pam_sss_service + pam_p11_allowed_services = +pam_sss_service, +pam_sss_sc_required, \ + +pam_sss_try_sc pam_cert_db_path = {config.PAM_CERT_DB_PATH} + p11_child_timeout = 5 + p11_wait_for_card_timeout = 5 debug_level = 10 [domain/auth_only] @@ -149,6 +157,15 @@ def create_nssdb(): pkcs11_txt.close() +def create_nssdb_no_cert(): + os.mkdir(config.SYSCONFDIR + "/pki") + os.mkdir(config.SYSCONFDIR + "/pki/nssdb") + if subprocess.call(["certutil", "-N", "-d", + "sql:" + config.SYSCONFDIR + "/pki/nssdb/", + "--empty-password"]) != 0: + raise Exception("certutil failed") + + def cleanup_nssdb(): shutil.rmtree(config.SYSCONFDIR + "/pki") @@ -158,14 +175,42 @@ def create_nssdb_fixture(request): request.addfinalizer(cleanup_nssdb) +def create_nssdb_no_cert_fixture(request): + create_nssdb_no_cert() + request.addfinalizer(cleanup_nssdb) + + @pytest.fixture -def simple_pam_cert_auth(request): +def simple_pam_cert_auth(request, passwd_ops_setup): """Setup SSSD with pam_cert_auth=True""" config.PAM_CERT_DB_PATH = os.environ['PAM_CERT_DB_PATH'] conf = format_pam_cert_auth_conf(config) create_conf_fixture(request, conf) create_sssd_fixture(request) create_nssdb_fixture(request) + passwd_ops_setup.useradd(**USER1) + passwd_ops_setup.useradd(**USER2) + return None + + +@pytest.fixture +def simple_pam_cert_auth_no_cert(request, passwd_ops_setup): + """Setup SSSD with pam_cert_auth=True""" + config.PAM_CERT_DB_PATH = os.environ['PAM_CERT_DB_PATH'] + + old_softhsm2_conf = os.environ['SOFTHSM2_CONF'] + del os.environ['SOFTHSM2_CONF'] + + conf = format_pam_cert_auth_conf(config) + create_conf_fixture(request, conf) + create_sssd_fixture(request) + create_nssdb_no_cert_fixture(request) + + os.environ['SOFTHSM2_CONF'] = old_softhsm2_conf + + passwd_ops_setup.useradd(**USER1) + passwd_ops_setup.useradd(**USER2) + return None @@ -176,26 +221,26 @@ def test_preauth_indicator(simple_pam_cert_auth): @pytest.fixture -def pam_wrapper_setup(request): +def env_for_sssctl(request): pwrap_runtimedir = os.getenv("PAM_WRAPPER_SERVICE_DIR") if pwrap_runtimedir is None: raise ValueError("The PAM_WRAPPER_SERVICE_DIR variable is unset\n") + env_for_sssctl = os.environ.copy() + env_for_sssctl['PAM_WRAPPER'] = "1" + env_for_sssctl['SSSD_INTG_PEER_UID'] = "0" + env_for_sssctl['SSSD_INTG_PEER_GID'] = "0" + env_for_sssctl['LD_PRELOAD'] += ':' + os.environ['PAM_WRAPPER_PATH'] -def test_sc_auth_wrong_pin(simple_pam_cert_auth, pam_wrapper_setup, - passwd_ops_setup): + return env_for_sssctl - passwd_ops_setup.useradd(**USER1) - current_env = os.environ.copy() - current_env['PAM_WRAPPER'] = "1" - current_env['SSSD_INTG_PEER_UID'] = "0" - current_env['SSSD_INTG_PEER_GID'] = "0" - current_env['LD_PRELOAD'] += ':' + os.environ['PAM_WRAPPER_PATH'] + +def test_sc_auth_wrong_pin(simple_pam_cert_auth, env_for_sssctl): sssctl = subprocess.Popen(["sssctl", "user-checks", "user1", "--action=auth", "--service=pam_sss_service"], universal_newlines=True, - env=current_env, stdin=subprocess.PIPE, + env=env_for_sssctl, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: @@ -214,19 +259,120 @@ def test_sc_auth_wrong_pin(simple_pam_cert_auth, pam_wrapper_setup, "Authentication failure") != -1 -def test_sc_auth(simple_pam_cert_auth, pam_wrapper_setup, passwd_ops_setup): - - passwd_ops_setup.useradd(**USER1) - current_env = os.environ.copy() - current_env['PAM_WRAPPER'] = "1" - current_env['SSSD_INTG_PEER_UID'] = "0" - current_env['SSSD_INTG_PEER_GID'] = "0" - current_env['LD_PRELOAD'] += ':' + os.environ['PAM_WRAPPER_PATH'] +def test_sc_auth(simple_pam_cert_auth, env_for_sssctl): sssctl = subprocess.Popen(["sssctl", "user-checks", "user1", "--action=auth", "--service=pam_sss_service"], universal_newlines=True, - env=current_env, stdin=subprocess.PIPE, + env=env_for_sssctl, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + try: + out, err = sssctl.communicate(input="123456") + except: + sssctl.kill() + out, err = sssctl.communicate() + + sssctl.stdin.close() + sssctl.stdout.close() + + if sssctl.wait() != 0: + raise Exception("sssctl failed") + + assert err.find("pam_authenticate for user [user1]: Success") != -1 + + +def test_require_sc_auth(simple_pam_cert_auth, env_for_sssctl): + + sssctl = subprocess.Popen(["sssctl", "user-checks", "user1", + "--action=auth", + "--service=pam_sss_sc_required"], + universal_newlines=True, + env=env_for_sssctl, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + try: + out, err = sssctl.communicate(input="123456") + except: + sssctl.kill() + out, err = sssctl.communicate() + + sssctl.stdin.close() + sssctl.stdout.close() + + if sssctl.wait() != 0: + raise Exception("sssctl failed") + + assert err.find("pam_authenticate for user [user1]: Success") != -1 + + +def test_require_sc_auth_no_cert(simple_pam_cert_auth_no_cert, env_for_sssctl): + + # We have to wait about 20s before the command returns because there will + # be 2 run since retry=1 in the PAM configuration and both + # p11_child_timeout and p11_wait_for_card_timeout are 5s in sssd.conf, + # so 2*(5+5)=20. */ + start_time = time.time() + sssctl = subprocess.Popen(["sssctl", "user-checks", "user1", + "--action=auth", + "--service=pam_sss_sc_required"], + universal_newlines=True, + env=env_for_sssctl, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + try: + out, err = sssctl.communicate(input="123456") + except: + sssctl.kill() + out, err = sssctl.communicate() + + sssctl.stdin.close() + sssctl.stdout.close() + + if sssctl.wait() != 0: + raise Exception("sssctl failed") + + end_time = time.time() + assert end_time > start_time and \ + (end_time - start_time) >= 20 and \ + (end_time - start_time) < 40 + assert out.find("Please enter smart card\nPlease enter smart card") != -1 + assert err.find("pam_authenticate for user [user1]: Authentication " + + "service cannot retrieve authentication info") != -1 + + +def test_try_sc_auth_no_map(simple_pam_cert_auth, env_for_sssctl): + + sssctl = subprocess.Popen(["sssctl", "user-checks", "user2", + "--action=auth", + "--service=pam_sss_try_sc"], + universal_newlines=True, + env=env_for_sssctl, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + try: + out, err = sssctl.communicate(input="123456") + except: + sssctl.kill() + out, err = sssctl.communicate() + + sssctl.stdin.close() + sssctl.stdout.close() + + if sssctl.wait() != 0: + raise Exception("sssctl failed") + + assert err.find("pam_authenticate for user [user2]: Authentication " + + "service cannot retrieve authentication info") != -1 + + +def test_try_sc_auth(simple_pam_cert_auth, env_for_sssctl): + + sssctl = subprocess.Popen(["sssctl", "user-checks", "user1", + "--action=auth", + "--service=pam_sss_try_sc"], + universal_newlines=True, + env=env_for_sssctl, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: From fe93f552bd457140788c7ab8136be0d82e201627 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Mon, 8 Oct 2018 10:45:28 +0200 Subject: [PATCH 07/10] p11_child: show PKCS#11 URI in debug output The patch only adds debug messages where the PKCS#11 URI of the selected certificates is shown. The output should help to create suitable URIs to restrict the selection. Related to https://pagure.io/SSSD/sssd/issue/3814 --- src/p11_child/p11_child_nss.c | 240 ++++++++++++++++++++++++++++++ src/p11_child/p11_child_openssl.c | 80 ++++++++++ 2 files changed, 320 insertions(+) diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c index b2777d1d2..fff1f2525 100644 --- a/src/p11_child/p11_child_nss.c +++ b/src/p11_child/p11_child_nss.c @@ -39,6 +39,7 @@ #include <pk11pub.h> #include <prerror.h> #include <ocsp.h> +#include <pkcs11uri.h> #include "util/child_common.h" #include "providers/backend.h" @@ -63,6 +64,239 @@ struct p11_ctx { | certificateUsageStatusResponder \ | certificateUsageSSLCA ) + +static char *get_pkcs11_string(TALLOC_CTX *mem_ctx, const char *in, size_t len) +{ + size_t c = len; + + if (in == NULL || len == 0) { + return NULL; + } + + while(c > 0 && in[c - 1] == ' ') { + c--; + } + + return talloc_strndup(mem_ctx, in, c); +} + +static char *pct_encode(TALLOC_CTX *mem_ctx, SECItem *data) +{ + char *pct; + size_t c; + int ret; + + pct = talloc_zero_size(mem_ctx, sizeof(char) * (3*data->len + 1)); + if (pct == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n"); + return NULL; + } + + for (c = 0; c < data->len; c++) { + ret = snprintf(pct + 3*c, 4, "%%%02X", data->data[c]); + if (ret != 3) { + DEBUG(SSSDBG_OP_FAILURE, "snprintf failed.\n"); + talloc_free(pct); + return NULL; + } + } + + return pct; +} + +static char *get_key_id_pct(TALLOC_CTX *mem_ctx, PK11SlotInfo *slot, + CERTCertificate *cert) +{ + SECItem *key_id = NULL; + char *key_id_str = NULL; + + key_id = PK11_GetLowLevelKeyIDForCert(slot, cert, NULL); + if (key_id == NULL) { + DEBUG(SSSDBG_OP_FAILURE, + "PK11_GetLowLevelKeyIDForCert failed [%d][%s].\n", + PR_GetError(), PORT_ErrorToString(PR_GetError())); + return NULL; + } + + key_id_str = pct_encode(mem_ctx, key_id); + SECITEM_FreeItem(key_id, PR_TRUE); + if (key_id_str == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "pct_encode failed.\n"); + return NULL; + } + + return key_id_str; +} + +static char *get_pkcs11_uri(TALLOC_CTX *mem_ctx, SECMODModule *mod, + PK11SlotInfo *slot, + const char *label, CERTCertificate *cert) +{ + CK_INFO module_info; + CK_SLOT_INFO slot_info; + CK_TOKEN_INFO token_info; + char *values[13]; + PK11URIAttribute attrs[13]; + size_t nattrs = 0; + SECStatus rv; + char *tmp_str; + char *uri_str; + PK11URI *uri; + CK_SLOT_ID slot_id; + char *id_pct; + + rv = PK11_GetModInfo(mod, &module_info); + if (rv != SECSuccess) { + DEBUG(SSSDBG_OP_FAILURE, "PK11_GetModInfo failed.\n"); + return NULL; + } + + rv = PK11_GetSlotInfo(slot, &slot_info); + if (rv != SECSuccess) { + DEBUG(SSSDBG_OP_FAILURE, "PK11_GetSlotInfo failed.\n"); + return NULL; + } + + rv = PK11_GetTokenInfo(slot, &token_info); + if (rv != SECSuccess) { + DEBUG(SSSDBG_OP_FAILURE, "PK11_GetTokenInfo failed.\n"); + return NULL; + } + values[nattrs] = get_pkcs11_string(mem_ctx, + (char *)module_info.libraryDescription, + sizeof(module_info.libraryDescription)); + if (values[nattrs] != NULL && *values[nattrs] != '\0') { + attrs[nattrs].name = PK11URI_PATTR_LIBRARY_DESCRIPTION; + attrs[nattrs].value = values[nattrs]; + nattrs++; + } + + values[nattrs] = get_pkcs11_string(mem_ctx, + (char *)module_info.manufacturerID, + sizeof(module_info.manufacturerID)); + if (values[nattrs] != NULL && *values[nattrs] != '\0') { + attrs[nattrs].name = PK11URI_PATTR_LIBRARY_MANUFACTURER; + attrs[nattrs].value = values[nattrs]; + nattrs++; + } + + values[nattrs] = talloc_asprintf(mem_ctx, "%d.%d", + module_info.libraryVersion.major, + module_info.libraryVersion.minor); + if (values[nattrs] != NULL && *values[nattrs] != '\0') { + attrs[nattrs].name = PK11URI_PATTR_LIBRARY_VERSION; + attrs[nattrs].value = values[nattrs]; + nattrs++; + } + + values[nattrs] = get_pkcs11_string(mem_ctx, + (char *)slot_info.slotDescription, + sizeof(slot_info.slotDescription)); + if (values[nattrs] != NULL && *values[nattrs] != '\0') { + attrs[nattrs].name = PK11URI_PATTR_SLOT_DESCRIPTION; + attrs[nattrs].value = values[nattrs]; + nattrs++; + } + + values[nattrs] = get_pkcs11_string(mem_ctx, + (char *)slot_info.manufacturerID, + sizeof(slot_info.manufacturerID)); + if (values[nattrs] != NULL && *values[nattrs] != '\0') { + attrs[nattrs].name = PK11URI_PATTR_SLOT_MANUFACTURER; + attrs[nattrs].value = values[nattrs]; + nattrs++; + } + + slot_id = PK11_GetSlotID(slot); + values[nattrs] = talloc_asprintf(mem_ctx, "%d", (int) slot_id); + if (values[nattrs] != NULL && *values[nattrs] != '\0') { + attrs[nattrs].name = PK11URI_PATTR_SLOT_ID; + attrs[nattrs].value = values[nattrs]; + nattrs++; + } + + values[nattrs] = get_pkcs11_string(mem_ctx, (char *)token_info.model, + sizeof(token_info.model)); + if (values[nattrs] != NULL && *values[nattrs] != '\0') { + attrs[nattrs].name = PK11URI_PATTR_MODEL; + attrs[nattrs].value = values[nattrs]; + nattrs++; + } + + values[nattrs] = get_pkcs11_string(mem_ctx, + (char *)token_info.manufacturerID, + sizeof(token_info.manufacturerID)); + if (values[nattrs] != NULL && *values[nattrs] != '\0') { + attrs[nattrs].name = PK11URI_PATTR_MANUFACTURER; + attrs[nattrs].value = values[nattrs]; + nattrs++; + } + + values[nattrs] = get_pkcs11_string(mem_ctx, + (char *)token_info.serialNumber, + sizeof(token_info.serialNumber)); + if (values[nattrs] != NULL && *values[nattrs] != '\0') { + attrs[nattrs].name = PK11URI_PATTR_SERIAL; + attrs[nattrs].value = values[nattrs]; + nattrs++; + } + + values[nattrs] = get_pkcs11_string(mem_ctx, (char *)token_info.label, + sizeof(token_info.label)); + if (values[nattrs] != NULL && *values[nattrs] != '\0') { + attrs[nattrs].name = PK11URI_PATTR_TOKEN; + attrs[nattrs].value = values[nattrs]; + nattrs++; + } + + if (label != NULL && *label != '\0') { + attrs[nattrs].name = PK11URI_PATTR_OBJECT; + attrs[nattrs].value = label; + nattrs++; + } + + attrs[nattrs].name = PK11URI_PATTR_TYPE; + attrs[nattrs].value = "cert"; + nattrs++; + + uri = PK11URI_CreateURI(attrs, nattrs, NULL, 0); + if (uri == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "PK11URI_CreateURI failed.\n"); + return NULL; + } + + tmp_str = PK11URI_FormatURI(NULL, uri); + PK11URI_DestroyURI(uri); + if (tmp_str == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "PK11URI_FormatURI failed.\n"); + return NULL; + } + + /* Currently I have no idea how to get the ID properly formatted with the + * NSS PK11 calls. Since all attribute values are treated as strings zeros + * in the IDs cannot be handled. And the IDs cannot be set percent-encoded + * since all attribute values will be escaped which means the '%' sign + * will be escaped to '%25'. Hence for the time being the ID is added + * manually to the end of the URI. */ + id_pct = get_key_id_pct(mem_ctx, slot, cert); + if (id_pct == NULL || *id_pct == '\0') { + DEBUG(SSSDBG_OP_FAILURE, "get_key_id_pct failed.\n"); + PORT_Free(tmp_str); + return NULL; + } + + uri_str = talloc_asprintf(mem_ctx, "%s;%s=%s", tmp_str, + PK11URI_PATTR_ID, id_pct); + talloc_free(id_pct); + if (uri_str == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n"); + return NULL; + } + + return uri_str; + +} + static char *password_passthrough(PK11SlotInfo *slot, PRBool retry, void *arg) { /* give up if 1) no password was supplied, or 2) the password has already @@ -465,6 +699,9 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, cert_list_node->cert->nickname, cert_list_node->cert->subjectName); + DEBUG(SSSDBG_TRACE_ALL, "module uri: %s.\n", PK11_GetModuleURI(module)); + DEBUG(SSSDBG_TRACE_ALL, "token uri: %s.\n", PK11_GetTokenURI(slot)); + if (p11_ctx->handle != NULL) { if (!do_verification(p11_ctx, cert_list_node->cert)) { DEBUG(SSSDBG_OP_FAILURE, @@ -651,6 +888,9 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, DEBUG(SSSDBG_TRACE_ALL, "Found certificate has key id [%s].\n", key_id_str); + DEBUG(SSSDBG_TRACE_ALL, "uri: %s.\n", get_pkcs11_uri(mem_ctx, module, + slot, label, + found_cert)); multi = talloc_asprintf_append(multi, "%s\n%s\n%s\n%s\n%s\n", token_name, module_name, key_id_str, diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c index d4572d99c..09edeefbd 100644 --- a/src/p11_child/p11_child_openssl.c +++ b/src/p11_child/p11_child_openssl.c @@ -29,6 +29,7 @@ #include <openssl/err.h> #include <openssl/rand.h> #include <p11-kit/p11-kit.h> +#include <p11-kit/uri.h> #include <popt.h> @@ -43,6 +44,72 @@ struct p11_ctx { bool wait_for_card; }; + +static char *get_pkcs11_uri(TALLOC_CTX *mem_ctx, CK_INFO *module_info, + CK_SLOT_INFO *slot_info, CK_SLOT_ID slot_id, + CK_TOKEN_INFO *token_info, CK_ATTRIBUTE *label, + CK_ATTRIBUTE *id) +{ + P11KitUri *uri; + char *uri_str = NULL; + char *tmp_str = NULL; + int ret; + CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; + CK_ATTRIBUTE class_attr = {CKA_CLASS, &cert_class, sizeof(CK_OBJECT_CLASS)}; + + uri = p11_kit_uri_new(); + if (uri == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "p11_kit_uri_new failed.\n"); + return NULL; + } + + ret = p11_kit_uri_set_attribute(uri, label); + if (ret != P11_KIT_URI_OK) { + DEBUG(SSSDBG_OP_FAILURE, "p11_kit_uri_set_attribute failed.\n"); + goto done; + } + + ret = p11_kit_uri_set_attribute(uri, id); + if (ret != P11_KIT_URI_OK) { + DEBUG(SSSDBG_OP_FAILURE, "p11_kit_uri_set_attribute failed.\n"); + goto done; + } + + ret = p11_kit_uri_set_attribute(uri, &class_attr); + if (ret != P11_KIT_URI_OK) { + DEBUG(SSSDBG_OP_FAILURE, "p11_kit_uri_set_attribute failed.\n"); + goto done; + } + + + memcpy(p11_kit_uri_get_token_info(uri), token_info, sizeof(CK_TOKEN_INFO)); + + memcpy(p11_kit_uri_get_slot_info(uri), slot_info, sizeof(CK_SLOT_INFO)); + ret = p11_kit_uri_set_slot_id(uri, slot_id); + + memcpy(p11_kit_uri_get_module_info(uri), module_info, sizeof(CK_INFO)); + + ret = p11_kit_uri_format(uri, P11_KIT_URI_FOR_ANY, &tmp_str); + if (ret != P11_KIT_URI_OK) { + DEBUG(SSSDBG_OP_FAILURE, "p11_kit_uri_format failed [%s].\n", + p11_kit_uri_message(ret)); + goto done; + } + + if (tmp_str != NULL) { + uri_str = talloc_strdup(mem_ctx, tmp_str); + free(tmp_str); + if (uri_str == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); + } + } + +done: + p11_kit_uri_free(uri); + + return uri_str; +} + static int talloc_cleanup_openssl(struct p11_ctx *p11_ctx) { CRYPTO_cleanup_all_ex_data(); @@ -234,6 +301,7 @@ struct cert_list { X509 *cert; char *subject_dn; char *cert_b64; + char *uri; CK_KEY_TYPE key_type; CK_OBJECT_HANDLE private_key; }; @@ -608,6 +676,7 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, CK_SLOT_ID slot_id; CK_SLOT_INFO info; CK_TOKEN_INFO token_info; + CK_INFO module_info; CK_RV rv; size_t module_id; char *module_file_name = NULL; @@ -821,6 +890,17 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, } } + memset(&module_info, 0, sizeof(CK_INFO)); + module->C_GetInfo(&module_info); + + DLIST_FOR_EACH(item, cert_list) { + item->uri = get_pkcs11_uri(mem_ctx, &module_info, &info, slot_id, + &token_info, + &item->attributes[1] /* label */, + &item->attributes[0] /* id */); + DEBUG(SSSDBG_TRACE_ALL, "uri: %s.\n", item->uri); + } + /* TODO: check module_name_in, token_name_in, key_id_in */ if (cert_list == NULL) { From 9f664bbd10614c57a77e2957ce38735bc6fb7a27 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Mon, 8 Oct 2018 12:47:25 +0200 Subject: [PATCH 08/10] p11_child: add PKCS#11 uri to restrict selection p11_child gets a new option to restrict the selection of certificates with the help of a PKCS#11 URI. Related to https://pagure.io/SSSD/sssd/issue/3814 --- src/p11_child/p11_child.h | 2 +- src/p11_child/p11_child_common.c | 9 ++-- src/p11_child/p11_child_nss.c | 2 +- src/p11_child/p11_child_openssl.c | 69 ++++++++++++++++++++++++++++++- 4 files changed, 75 insertions(+), 7 deletions(-) diff --git a/src/p11_child/p11_child.h b/src/p11_child/p11_child.h index dd8fdeafb..92ecf74a8 100644 --- a/src/p11_child/p11_child.h +++ b/src/p11_child/p11_child.h @@ -54,5 +54,5 @@ bool do_verification_b64(struct p11_ctx *p11_ctx, const char *cert_b64); errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, enum op_mode mode, const char *pin, const char *module_name_in, const char *token_name_in, - const char *key_id_in, char **_multi); + const char *key_id_in, const char *uri, char **_multi); #endif /* __P11_CHILD_H__ */ diff --git a/src/p11_child/p11_child_common.c b/src/p11_child/p11_child_common.c index bc5f6b09b..097e7fa07 100644 --- a/src/p11_child/p11_child_common.c +++ b/src/p11_child/p11_child_common.c @@ -60,7 +60,7 @@ static int do_work(TALLOC_CTX *mem_ctx, enum op_mode mode, const char *ca_db, bool wait_for_card, const char *cert_b64, const char *pin, const char *module_name, const char *token_name, - const char *key_id, char **multi) + const char *key_id, const char *uri, char **multi) { int ret; struct p11_ctx *p11_ctx; @@ -90,7 +90,7 @@ static int do_work(TALLOC_CTX *mem_ctx, enum op_mode mode, const char *ca_db, } } else { ret = do_card(mem_ctx, p11_ctx, mode, pin, - module_name, token_name, key_id, multi); + module_name, token_name, key_id, uri, multi); } done: @@ -159,6 +159,7 @@ int main(int argc, const char *argv[]) char *key_id = NULL; char *cert_b64 = NULL; bool wait_for_card = false; + char *uri = NULL; struct poptOption long_options[] = { POPT_AUTOHELP @@ -194,6 +195,8 @@ int main(int argc, const char *argv[]) _("Key ID for authentication"), NULL}, {"certificate", 0, POPT_ARG_STRING, &cert_b64, 0, _("certificate to verify, base64 encoded"), NULL}, + {"uri", 0, POPT_ARG_STRING, &uri, 0, + _("PKCS#11 URI to restrict selection"), NULL}, POPT_TABLEEND }; @@ -367,7 +370,7 @@ int main(int argc, const char *argv[]) } ret = do_work(main_ctx, mode, nss_db, cert_verify_opts, wait_for_card, - cert_b64, pin, module_name, token_name, key_id, &multi); + cert_b64, pin, module_name, token_name, key_id, uri, &multi); if (ret != 0) { DEBUG(SSSDBG_OP_FAILURE, "do_work failed.\n"); goto fail; diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c index fff1f2525..f9cbf3f37 100644 --- a/src/p11_child/p11_child_nss.c +++ b/src/p11_child/p11_child_nss.c @@ -480,7 +480,7 @@ bool do_verification_b64(struct p11_ctx *p11_ctx, const char *cert_b64) errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, enum op_mode mode, const char *pin, const char *module_name_in, const char *token_name_in, - const char *key_id_in, char **_multi) + const char *key_id_in, const char *uri, char **_multi) { int ret; SECStatus rv; diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c index 09edeefbd..e5d7c20c8 100644 --- a/src/p11_child/p11_child_openssl.c +++ b/src/p11_child/p11_child_openssl.c @@ -85,7 +85,7 @@ static char *get_pkcs11_uri(TALLOC_CTX *mem_ctx, CK_INFO *module_info, memcpy(p11_kit_uri_get_token_info(uri), token_info, sizeof(CK_TOKEN_INFO)); memcpy(p11_kit_uri_get_slot_info(uri), slot_info, sizeof(CK_SLOT_INFO)); - ret = p11_kit_uri_set_slot_id(uri, slot_id); + p11_kit_uri_set_slot_id(uri, slot_id); memcpy(p11_kit_uri_get_module_info(uri), module_info, sizeof(CK_INFO)); @@ -662,7 +662,7 @@ static errno_t wait_for_card(CK_FUNCTION_LIST *module, CK_SLOT_ID *slot_id) errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, enum op_mode mode, const char *pin, const char *module_name_in, const char *token_name_in, - const char *key_id_in, char **_multi) + const char *key_id_in, const char *uri_str, char **_multi) { int ret; size_t c; @@ -674,6 +674,7 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, CK_ULONG num_slots; CK_SLOT_ID slots[MAX_SLOTS]; CK_SLOT_ID slot_id; + CK_SLOT_ID uri_slot_id; CK_SLOT_INFO info; CK_TOKEN_INFO token_info; CK_INFO module_info; @@ -690,6 +691,19 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, char *multi = NULL; bool pkcs11_session = false; bool pkcs11_login = false; + P11KitUri *uri = NULL; + + if (uri_str != NULL) { + uri = p11_kit_uri_new(); + ret = p11_kit_uri_parse(uri_str, P11_KIT_URI_FOR_ANY, uri); + if (ret != P11_KIT_URI_OK) { + DEBUG(SSSDBG_OP_FAILURE, "p11_kit_uri_parse failed [%d][%s].\n", + ret, p11_kit_uri_message(ret)); + ret = EINVAL; + goto done; + } + } + /* Maybe use P11_KIT_MODULE_TRUSTED ? */ modules = p11_kit_modules_load_and_initialize(0); @@ -709,6 +723,18 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, free(mod_name); free(mod_file_name); + if (uri != NULL) { + memset(&module_info, 0, sizeof(CK_INFO)); + modules[c]->C_GetInfo(&module_info); + + /* Skip modules which do not match the PKCS#11 URI */ + if (p11_kit_uri_match_module_info(uri, &module_info) != 1) { + DEBUG(SSSDBG_TRACE_ALL, + "Not matching URI [%s], skipping.\n", uri_str); + continue; + } + } + num_slots = MAX_SLOTS; rv = modules[c]->C_GetSlotList(CK_FALSE, slots, &num_slots); if (rv != CKR_OK) { @@ -730,6 +756,37 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, info.slotDescription, info.manufacturerID, info.flags, (info.flags & CKF_REMOVABLE_DEVICE) ? "true": "false", (info.flags & CKF_TOKEN_PRESENT) ? "true": "false"); + + /* Skip slots which do not match the PKCS#11 URI */ + if (uri != NULL) { + uri_slot_id = p11_kit_uri_get_slot_id(uri); + if ((uri_slot_id != (CK_SLOT_ID)-1 + && uri_slot_id != slots[s]) + || p11_kit_uri_match_slot_info(uri, &info) != 1) { + DEBUG(SSSDBG_TRACE_ALL, + "Not matching URI [%s], skipping.\n", uri_str); + continue; + } + } + + if ((info.flags & CKF_TOKEN_PRESENT) && uri != NULL) { + rv = modules[c]->C_GetTokenInfo(slots[s], &token_info); + if (rv != CKR_OK) { + DEBUG(SSSDBG_OP_FAILURE, "C_GetTokenInfo failed.\n"); + ret = EIO; + goto done; + } + DEBUG(SSSDBG_TRACE_ALL, "Token label [%s].\n", + token_info.label); + + if (p11_kit_uri_match_token_info(uri, &token_info) != 1) { + DEBUG(SSSDBG_CONF_SETTINGS, + "No matching uri [%s], skipping.\n", uri_str); + continue; + } + + } + if ((info.flags & CKF_REMOVABLE_DEVICE)) { break; } @@ -788,6 +845,13 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, goto done; } + if (uri != NULL && p11_kit_uri_match_token_info(uri, &token_info) != 1) { + DEBUG(SSSDBG_CONF_SETTINGS, "No token matching uri [%s] found.", + uri_str); + ret = ENOENT; + goto done; + } + module_id = c; slot_name = p11_kit_space_strdup(info.slotDescription, sizeof(info.slotDescription)); @@ -970,6 +1034,7 @@ errno_t do_card(TALLOC_CTX *mem_ctx, struct p11_ctx *p11_ctx, free(token_name); free(module_file_name); p11_kit_modules_finalize_and_release(modules); + p11_kit_uri_free(uri); return ret; } From 1e84906827f84f9af03981ba0e2e68507b1b3d1a Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Tue, 9 Oct 2018 10:47:04 +0200 Subject: [PATCH 09/10] PAM: add p11_uri option This patch adds a new option 'p11_uri' to the PAM responder to restrict the selection of certificates in p11_child with the help of a PKCS#11 URI. Related to https://pagure.io/SSSD/sssd/issue/3814 --- src/confdb/confdb.h | 1 + src/config/SSSDConfig/__init__.py.in | 1 + src/config/cfg_rules.ini | 1 + src/config/etc/sssd.api.conf | 1 + src/man/sssd.conf.5.xml | 33 ++++++++++++++++++++++++++++ src/responder/pam/pamsrv.h | 1 + src/responder/pam/pamsrv_cmd.c | 12 +++++++++- src/responder/pam/pamsrv_p11.c | 9 +++++++- 8 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h index 87904c214..741d4bc47 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -133,6 +133,7 @@ #define CONFDB_PAM_WAIT_FOR_CARD_TIMEOUT "p11_wait_for_card_timeout" #define CONFDB_PAM_APP_SERVICES "pam_app_services" #define CONFDB_PAM_P11_ALLOWED_SERVICES "pam_p11_allowed_services" +#define CONFDB_PAM_P11_URI "p11_uri" /* SUDO */ #define CONFDB_SUDO_CONF_ENTRY "config/sudo" diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in index 4d1dba2d2..a20157c71 100644 --- a/src/config/SSSDConfig/__init__.py.in +++ b/src/config/SSSDConfig/__init__.py.in @@ -105,6 +105,7 @@ option_strings = { 'pam_app_services' : _('Which PAM services are permitted to contact application domains'), 'pam_p11_allowed_services' : _('Allowed services for using smartcards'), 'p11_wait_for_card_timeout' : _('Additional timeout to wait for a card if requested'), + 'p11_uri' : _('PKCS#11 URI to restrict the selection of devices for Smartcard authentication'), # [sudo] 'sudo_timed' : _('Whether to evaluate the time-based attributes in sudo rules'), diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini index 50a8f1dfc..09a52df4e 100644 --- a/src/config/cfg_rules.ini +++ b/src/config/cfg_rules.ini @@ -128,6 +128,7 @@ option = p11_child_timeout option = pam_app_services option = pam_p11_allowed_services option = p11_wait_for_card_timeout +option = p11_uri [rule/allowed_sudo_options] validator = ini_allowed_options diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf index bb686c344..c6d6690fb 100644 --- a/src/config/etc/sssd.api.conf +++ b/src/config/etc/sssd.api.conf @@ -77,6 +77,7 @@ p11_child_timeout = int, None, false pam_app_services = str, None, false pam_p11_allowed_services = str, None, false p11_wait_for_card_timeout = int, None, false +p11_uri = str, None, false [sudo] # sudo service diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml index 4df016331..c8d53f01f 100644 --- a/src/man/sssd.conf.5.xml +++ b/src/man/sssd.conf.5.xml @@ -1478,6 +1478,39 @@ pam_p11_allowed_services = +my_pam_service, -login </para> </listitem> </varlistentry> + <varlistentry> + <term>p11_uri (string)</term> + <listitem> + <para> + PKCS#11 URI (see RFC-7512 for details) which can be + used to restrict the selection of devices used for + Smartcard authentication. By default SSSD's + p11_child will search for a PKCS#11 slot (reader) + where the 'removable' flags is set and read the + certificates from the inserted token from the first + slot found. If multiple readers are connected + p11_uri can be use to tell p11_child to use a + specific reader. + </para> + <para> + Example: + <programlisting> +p11_uri = slot-description=My%20Smartcar%20Reader + </programlisting> + or + <programlisting> +p11_uri = library-description=OpenSC%20smartcard%20framework;slot-id=2 + </programlisting> + To find suitable URI please check the debug output + of p11_child. As an alternative the GnuTLS utility + 'p11tool' with e.g. the '--list-all' will show + PKCS#11 URIs as well. + </para> + <para> + Default: none + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h index 5d877566f..60aa97967 100644 --- a/src/responder/pam/pamsrv.h +++ b/src/responder/pam/pamsrv.h @@ -103,6 +103,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx, time_t timeout, const char *verify_opts, struct sss_certmap_ctx *sss_certmap_ctx, + const char *uri, struct pam_data *pd); errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct cert_auth_info **cert_list); diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c index 6e37f8316..a22afd225 100644 --- a/src/responder/pam/pamsrv_cmd.c +++ b/src/responder/pam/pamsrv_cmd.c @@ -1306,6 +1306,7 @@ static errno_t check_cert(TALLOC_CTX *mctx, char *cert_verification_opts; errno_t ret; struct tevent_req *req; + char *uri = NULL; ret = confdb_get_int(pctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY, CONFDB_PAM_P11_CHILD_TIMEOUT, @@ -1342,10 +1343,19 @@ static errno_t check_cert(TALLOC_CTX *mctx, return ret; } + ret = confdb_get_string(pctx->rctx->cdb, mctx, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_P11_URI, NULL, &uri); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to read certificate_verification from confdb: [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + req = pam_check_cert_send(mctx, ev, pctx->p11_child_debug_fd, pctx->nss_db, p11_child_timeout, cert_verification_opts, pctx->sss_certmap_ctx, - pd); + uri, pd); if (req == NULL) { DEBUG(SSSDBG_OP_FAILURE, "pam_check_cert_send failed.\n"); return ENOMEM; diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c index 8b8859d9d..491bd2b01 100644 --- a/src/responder/pam/pamsrv_p11.c +++ b/src/responder/pam/pamsrv_p11.c @@ -711,6 +711,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx, time_t timeout, const char *verify_opts, struct sss_certmap_ctx *sss_certmap_ctx, + const char *uri, struct pam_data *pd) { errno_t ret; @@ -721,7 +722,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[14] = { NULL }; + const char *extra_args[16] = { NULL }; uint8_t *write_buf = NULL; size_t write_buf_len = 0; size_t arg_c; @@ -748,6 +749,12 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx, /* extra_args are added in revers order */ arg_c = 0; + if (uri != NULL) { + DEBUG(SSSDBG_TRACE_ALL, "Adding PKCS#11 URI [%s].\n", uri); + extra_args[arg_c++] = uri; + extra_args[arg_c++] = "--uri"; + } + if ((pd->cli_flags & PAM_CLI_FLAGS_REQUIRE_CERT_AUTH) && pd->priv == 1) { extra_args[arg_c++] = "--wait_for_card"; } From 93b45fcdddee66b246bce9d5a263eafe13f5248f Mon Sep 17 00:00:00 2001 From: Sumit Bose <sb...@redhat.com> Date: Tue, 9 Oct 2018 10:46:43 +0200 Subject: [PATCH 10/10] tests: add PKCS#11 URI tests New PAM responder unit test to test the selection of certificates with the help of PKCS#11 URIs. For this a new SoftHSM2 configuration with 2 slots is created. The new tests will try to access the certificates stored in the slot individually. Related to https://pagure.io/SSSD/sssd/issue/3814 --- src/tests/cmocka/test_pam_srv.c | 120 ++++++++++++++++++++++++++++++++ src/tests/test_CA/Makefile.am | 16 ++++- 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c index 2b02ac27b..7fc9224e1 100644 --- a/src/tests/cmocka/test_pam_srv.c +++ b/src/tests/cmocka/test_pam_srv.c @@ -65,6 +65,7 @@ #endif #define TEST_TOKEN_NAME "SSSD Test Token" +#define TEST_TOKEN2_NAME "SSSD Test Token Number 2" #define TEST_KEY_ID "C554C9F82C2A9D58B70921C143304153A8A42F17" #ifdef HAVE_NSS #define TEST_MODULE_NAME "NSS-Internal" @@ -961,6 +962,54 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen, return EOK; } +static int test_pam_cert2_token2_check_ex(uint32_t status, uint8_t *body, + size_t blen, enum response_type type, + const char *name) +{ + size_t rp = 0; + uint32_t val; + size_t check2_len = 0; + char const *check2_strings[] = { NULL, + TEST_TOKEN2_NAME, + TEST_MODULE_NAME, + TEST2_KEY_ID, + TEST2_PROMPT, + NULL }; + + assert_int_equal(status, 0); + + check2_strings[0] = name; + check2_len = check_string_array_len(check2_strings); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, pam_test_ctx->exp_pam_status); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 2); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, SSS_PAM_DOMAIN_NAME); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, 9); + + assert_int_equal(*(body + rp + val - 1), 0); + assert_string_equal(body + rp, TEST_DOM_NAME); + rp += val; + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, type); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, check2_len); + + check_string_array(check2_strings, body, &rp); + + assert_int_equal(rp, blen); + + return EOK; +} + static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen) { return test_pam_cert_check_ex(status, body, blen, @@ -968,6 +1017,12 @@ static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen) NULL); } +static int test_pam_cert2_check(uint32_t status, uint8_t *body, size_t blen) +{ + return test_pam_cert2_token2_check_ex(status, body, blen, SSS_PAM_CERT_INFO, + "pamuser@"TEST_DOM_NAME); +} + static int test_pam_cert_check_auth_success(uint32_t status, uint8_t *body, size_t blen) { @@ -2476,6 +2531,65 @@ void test_pam_cert_auth_2certs_one_mapping(void **state) assert_int_equal(ret, EOK); } +void test_pam_cert_preauth_uri_token1(void **state) +{ + int ret; + + struct sss_test_conf_param pam_params[] = { + { CONFDB_PAM_P11_URI, "pkcs11:token=SSSD%20Test%20Token" }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2tokens.conf")); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + test_lookup_by_cert_cb, SSSD_TEST_CERT_0001, false); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} + +void test_pam_cert_preauth_uri_token2(void **state) +{ + int ret; + + struct sss_test_conf_param pam_params[] = { + { CONFDB_PAM_P11_URI, "pkcs11:token=SSSD%20Test%20Token%20Number%202" }, + { NULL, NULL }, /* Sentinel */ + }; + + ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb); + assert_int_equal(ret, EOK); + set_cert_auth_param(pam_test_ctx->pctx, CA_DB); + putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/softhsm2_2tokens.conf")); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL, + test_lookup_by_cert_cb, SSSD_TEST_CERT_0002, false); + + will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); + will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); + + set_cmd_cb(test_pam_cert2_check); + ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, + pam_test_ctx->pam_cmds); + assert_int_equal(ret, EOK); + + /* Wait until the test finishes with EOK */ + ret = test_ev_loop(pam_test_ctx->tctx); + assert_int_equal(ret, EOK); +} void test_filter_response(void **state) { @@ -2915,6 +3029,12 @@ int main(int argc, const char *argv[]) pam_test_setup, pam_test_teardown), cmocka_unit_test_setup_teardown(test_pam_cert_auth_no_logon_name_no_key_id, pam_test_setup, pam_test_teardown), +#ifndef HAVE_NSS + cmocka_unit_test_setup_teardown(test_pam_cert_preauth_uri_token1, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_preauth_uri_token2, + pam_test_setup, pam_test_teardown), +#endif /* ! HAVE_NSS */ #endif /* HAVE_TEST_CA */ cmocka_unit_test_setup_teardown(test_filter_response, diff --git a/src/tests/test_CA/Makefile.am b/src/tests/test_CA/Makefile.am index 1bce2c366..b574c7611 100644 --- a/src/tests/test_CA/Makefile.am +++ b/src/tests/test_CA/Makefile.am @@ -24,7 +24,7 @@ pkcs12 = $(addprefix SSSD_test_cert_pkcs12_,$(addsuffix .pem,$(ids))) if HAVE_NSS extra = p11_nssdb p11_nssdb_2certs else -extra = softhsm2_none softhsm2_one softhsm2_two +extra = softhsm2_none softhsm2_one softhsm2_two softhsm2_2tokens endif # If openssl is run in parallel there might be conflicts with the serial @@ -114,6 +114,20 @@ softhsm2_two.conf: @echo "objectstore.backend = file" >> $@ @echo "slots.removable = true" >> $@ +softhsm2_2tokens: softhsm2_2tokens.conf + mkdir $@ + SOFTHSM2_CONF=./$< $(SOFTHSM2_UTIL) --init-token --label "SSSD Test Token" --pin 123456 --so-pin 123456 --free + GNUTLS_PIN=123456 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --no-mark-private --load-certificate=SSSD_test_cert_x509_0001.pem --login --label 'SSSD test cert 0001' --id 'C554C9F82C2A9D58B70921C143304153A8A42F17' pkcs11:token=SSSD%20Test%20Token + GNUTLS_PIN=123456 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --load-privkey=$(srcdir)/SSSD_test_cert_key_0001.pem --login --label 'SSSD test cert 0001' --id 'C554C9F82C2A9D58B70921C143304153A8A42F17' pkcs11:token=SSSD%20Test%20Token + SOFTHSM2_CONF=./$< $(SOFTHSM2_UTIL) --init-token --label "SSSD Test Token Number 2" --pin 654321 --so-pin 654321 --free + GNUTLS_PIN=654321 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --no-mark-private --load-certificate=SSSD_test_cert_x509_0002.pem --login --label 'SSSD test cert 0002' --id '5405842D56CF31F0BB025A695C5F3E907051C5B9' pkcs11:token=SSSD%20Test%20Token%20Number%202 + GNUTLS_PIN=654321 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --load-privkey=$(srcdir)/SSSD_test_cert_key_0002.pem --login --label 'SSSD test cert 0002' --id '5405842D56CF31F0BB025A695C5F3E907051C5B9' pkcs11:token=SSSD%20Test%20Token%20Number%202 + +softhsm2_2tokens.conf: + @echo "directories.tokendir = "$(abs_top_builddir)"/src/tests/test_CA/softhsm2_2tokens" > $@ + @echo "objectstore.backend = file" >> $@ + @echo "slots.removable = true" >> $@ + CLEANFILES = \ index.txt index.txt.attr \ index.txt.attr.old index.txt.old \
_______________________________________________ sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org Fedora Code of Conduct: https://getfedora.org/code-of-conduct.html List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedorahosted.org/archives/list/sssd-devel@lists.fedorahosted.org