From 0bdd8800c16f39b8fe308d20694ad905c669dff3 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Nov 13 2017 15:37:44 +0000 Subject: PAM: handled multiple certs in the responder This patch refactors the handling of the certificate and the attributes to address the certificate on the Smartcard (module name, token name and key id). Instead of using individual variables the values are put into a new struct cert_auth_info. Since the new struct can be used as a list the PAM responder can now handle multiple certificates on the Smartcard and can send the needed data to pam_sss with multiple SSS_PAM_CERT_INFO messages. Unit tests are added to confirm the expected behavior. Related to https://pagure.io/SSSD/sssd/issue/3560 Reviewed-by: Fabiano FidĂȘncio Tested-by: Scott Poore --- diff --git a/Makefile.am b/Makefile.am index bbc90d9..4ed872a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -481,6 +481,8 @@ dist_noinst_DATA = \ contrib/ci/sssd.supp \ src/tests/cmocka/p11_nssdb/cert9.db \ src/tests/cmocka/p11_nssdb/key4.db \ + src/tests/cmocka/p11_nssdb_2certs/cert9.db \ + src/tests/cmocka/p11_nssdb_2certs/key4.db \ $(SYSTEMTAP_PROBES) \ $(NULL) diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h index 896f71b..f15f7f1 100644 --- a/src/responder/pam/pamsrv.h +++ b/src/responder/pam/pamsrv.h @@ -73,10 +73,8 @@ struct pam_auth_req { struct pam_auth_dp_req *dpreq_spy; struct ldb_message *user_obj; - struct ldb_result *cert_user_objs; - char *token_name; - char *module_name; - char *key_id; + struct cert_auth_info *cert_list; + struct cert_auth_info *current_cert; bool cert_auth_local; }; @@ -89,6 +87,16 @@ int LOCAL_pam_handler(struct pam_auth_req *preq); errno_t p11_child_init(struct pam_ctx *pctx); struct cert_auth_info; +const char *sss_cai_get_cert(struct cert_auth_info *i); +const char *sss_cai_get_token_name(struct cert_auth_info *i); +const char *sss_cai_get_module_name(struct cert_auth_info *i); +const char *sss_cai_get_key_id(struct cert_auth_info *i); +struct cert_auth_info *sss_cai_get_next(struct cert_auth_info *i); +struct ldb_result *sss_cai_get_cert_user_objs(struct cert_auth_info *i); +void sss_cai_set_cert_user_objs(struct cert_auth_info *i, + struct ldb_result *cert_user_objs); +void sss_cai_check_users(struct cert_auth_info **list, size_t *_cert_count, + size_t *_cert_user_count); struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -98,12 +106,11 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx, const char *verify_opts, struct pam_data *pd); errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, - char **cert, char **token_name, char **module_name, - char **key_id); + struct cert_auth_info **cert_list); -errno_t add_pam_cert_response(struct pam_data *pd, const char *user, - const char *token_name, const char *module_name, - const char *key_id, enum response_type type); +errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username, + struct cert_auth_info *cert_info, + enum response_type type); bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd); diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c index 51d8185..8b2c086 100644 --- a/src/responder/pam/pamsrv_cmd.c +++ b/src/responder/pam/pamsrv_cmd.c @@ -1389,21 +1389,17 @@ done: return pam_check_user_done(preq, ret); } +static errno_t pam_user_by_cert_step(struct pam_auth_req *preq); static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req); static void pam_forwarder_cert_cb(struct tevent_req *req) { struct pam_auth_req *preq = tevent_req_callback_data(req, struct pam_auth_req); - struct cli_ctx *cctx = preq->cctx; struct pam_data *pd; errno_t ret = EOK; - char *cert; - struct pam_ctx *pctx = - talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx); + const char *cert; - ret = pam_check_cert_recv(req, preq, &cert, &preq->token_name, - &preq->module_name, - &preq->key_id); + ret = pam_check_cert_recv(req, preq, &preq->cert_list); talloc_free(req); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "get_cert request failed.\n"); @@ -1412,6 +1408,8 @@ static void pam_forwarder_cert_cb(struct tevent_req *req) pd = preq->pd; + cert = sss_cai_get_cert(preq->cert_list); + if (cert == NULL) { if (pd->logon_name == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, @@ -1431,21 +1429,42 @@ static void pam_forwarder_cert_cb(struct tevent_req *req) goto done; } + preq->current_cert = preq->cert_list; + ret = pam_user_by_cert_step(preq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "pam_user_by_cert_step failed.\n"); + goto done; + } + + return; + +done: + pam_check_user_done(preq, ret); +} + +static errno_t pam_user_by_cert_step(struct pam_auth_req *preq) +{ + struct cli_ctx *cctx = preq->cctx; + struct tevent_req *req; + struct pam_ctx *pctx = + talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx); + + if (preq->current_cert == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Missing certificate data.\n"); + return EINVAL; + } req = cache_req_user_by_cert_send(preq, cctx->ev, cctx->rctx, pctx->rctx->ncache, 0, preq->req_dom_type, NULL, - cert); + sss_cai_get_cert(preq->current_cert)); if (req == NULL) { DEBUG(SSSDBG_OP_FAILURE, "cache_req_user_by_cert_send failed.\n"); - ret = ENOMEM; - goto done; + return ENOMEM; } - tevent_req_set_callback(req, pam_forwarder_lookup_by_cert_done, preq); - return; -done: - pam_check_user_done(preq, ret); + tevent_req_set_callback(req, pam_forwarder_lookup_by_cert_done, preq); + return EOK; } static errno_t get_results_from_all_domains(TALLOC_CTX *mem_ctx, @@ -1511,6 +1530,9 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) struct pam_auth_req *preq = tevent_req_callback_data(req, struct pam_auth_req); const char *cert_user = NULL; + size_t cert_count = 0; + size_t cert_user_count = 0; + struct ldb_result *cert_user_objs; ret = cache_req_recv(preq, req, &results); talloc_zfree(req); @@ -1521,12 +1543,39 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) if (ret == EOK) { ret = get_results_from_all_domains(preq, results, - &preq->cert_user_objs); + &cert_user_objs); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "get_results_from_all_domains failed.\n"); goto done; } + sss_cai_set_cert_user_objs(preq->current_cert, cert_user_objs); + } + + preq->current_cert = sss_cai_get_next(preq->current_cert); + if (preq->current_cert != NULL) { + ret = pam_user_by_cert_step(preq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "pam_user_by_cert_step failed.\n"); + goto done; + } + return; + } + + sss_cai_check_users(&preq->cert_list, &cert_count, &cert_user_count); + DEBUG(SSSDBG_TRACE_ALL, + "Found [%zu] certificates and [%zu] related users.\n", + cert_count, cert_user_count); + + if (cert_user_count == 0) { + if (preq->pd->logon_name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Missing logon name and no certificate user found.\n"); + ret = ENOENT; + goto done; + } + } else { + if (preq->pd->logon_name == NULL) { if (preq->pd->cmd != SSS_PAM_PREAUTH) { DEBUG(SSSDBG_CRIT_FAILURE, @@ -1535,9 +1584,39 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) goto done; } - if (preq->cert_user_objs->count == 1) { + if (cert_count > 1) { + for (preq->current_cert = preq->cert_list; + preq->current_cert != NULL; + preq->current_cert = sss_cai_get_next(preq->current_cert)) { + + ret = add_pam_cert_response(preq->pd, "", + preq->current_cert, + preq->cctx->rctx->domains->user_name_hint + ? SSS_PAM_CERT_INFO_WITH_HINT + : SSS_PAM_CERT_INFO); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "add_pam_cert_response failed.\n"); + preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL; + } + } + + ret = EOK; + preq->pd->pam_status = PAM_SUCCESS; + pam_reply(preq); + goto done; + } + + if (cert_user_count == 1) { + cert_user_objs = sss_cai_get_cert_user_objs(preq->cert_list); + if (cert_user_objs == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Missing certificate user.\n"); + ret = ENOENT; + goto done; + } + cert_user = ldb_msg_find_attr_as_string( - preq->cert_user_objs->msgs[0], + cert_user_objs->msgs[0], SYSDB_NAME, NULL); if (cert_user == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, @@ -1564,9 +1643,7 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) if (preq->cctx->rctx->domains->user_name_hint) { ret = add_pam_cert_response(preq->pd, cert_user, - preq->token_name, - preq->module_name, - preq->key_id, + preq->cert_list, SSS_PAM_CERT_INFO_WITH_HINT); preq->pd->pam_status = PAM_SUCCESS; if (ret != EOK) { @@ -1596,13 +1673,6 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req) goto done; } } - } else { - if (preq->pd->logon_name == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Missing logon name and no certificate user found.\n"); - ret = ENOENT; - goto done; - } } if (preq->user_obj == NULL) { @@ -1884,7 +1954,9 @@ static void pam_dom_forwarder(struct pam_auth_req *preq) struct pam_ctx *pctx = talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx); const char *cert_user; + struct ldb_result *cert_user_objs; size_t c; + bool found = false; if (!preq->pd->domain) { preq->pd->domain = preq->domain->name; @@ -1921,76 +1993,87 @@ static void pam_dom_forwarder(struct pam_auth_req *preq) return; } - if (may_do_cert_auth(pctx, preq->pd) && preq->cert_user_objs != NULL) { + if (may_do_cert_auth(pctx, preq->pd) && preq->cert_list != NULL) { /* Check if user matches certificate user */ - for (c = 0; c < preq->cert_user_objs->count; c++) { - cert_user = ldb_msg_find_attr_as_string( - preq->cert_user_objs->msgs[c], - SYSDB_NAME, - NULL); - if (cert_user == NULL) { - /* Even if there might be other users mapped to the - * certificate a missing SYSDB_NAME indicates some critical - * condition which justifies that the whole request is aborted - * */ - DEBUG(SSSDBG_CRIT_FAILURE, - "Certificate user object has no name.\n"); - preq->pd->pam_status = PAM_USER_UNKNOWN; - pam_reply(preq); - return; + found = false; + for (preq->current_cert = preq->cert_list; + preq->current_cert != NULL; + preq->current_cert = sss_cai_get_next(preq->current_cert)) { + + cert_user_objs = sss_cai_get_cert_user_objs(preq->current_cert); + if (cert_user_objs == NULL) { + DEBUG(SSSDBG_OP_FAILURE, + "Unexpteced missing certificate user, " + "trying next certificate.\n"); + continue; } - /* pam_check_user_search() calls pd_set_primary_name() is the search - * was successful, so pd->user contains the canonical sysdb name - * as well */ - if (ldb_dn_compare(preq->cert_user_objs->msgs[c]->dn, - preq->user_obj->dn) == 0) { - - if (preq->pd->cmd == SSS_PAM_PREAUTH) { - ret = sss_authtok_set_sc(preq->pd->authtok, - SSS_AUTHTOK_TYPE_SC_PIN, NULL, 0, - preq->token_name, 0, - preq->module_name, 0, - preq->key_id, 0); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_set_sc failed, " - "Smartcard authentication " - "detection might fail in the " - "backend.\n"); - } + for (c = 0; c < cert_user_objs->count; c++) { + cert_user = ldb_msg_find_attr_as_string(cert_user_objs->msgs[c], + SYSDB_NAME, NULL); + if (cert_user == NULL) { + /* Even if there might be other users mapped to the + * certificate a missing SYSDB_NAME indicates some critical + * condition which justifies that the whole request is aborted + * */ + DEBUG(SSSDBG_CRIT_FAILURE, + "Certificate user object has no name.\n"); + preq->pd->pam_status = PAM_USER_UNKNOWN; + pam_reply(preq); + return; + } - ret = add_pam_cert_response(preq->pd, cert_user, - preq->token_name, - preq->module_name, - preq->key_id, - SSS_PAM_CERT_INFO); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n"); - preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL; + if (ldb_dn_compare(cert_user_objs->msgs[c]->dn, + preq->user_obj->dn) == 0) { + found = true; + if (preq->pd->cmd == SSS_PAM_PREAUTH) { + ret = sss_authtok_set_sc(preq->pd->authtok, + SSS_AUTHTOK_TYPE_SC_PIN, NULL, 0, + sss_cai_get_token_name(preq->current_cert), 0, + sss_cai_get_module_name(preq->current_cert), 0, + sss_cai_get_key_id(preq->current_cert), 0); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sss_authtok_set_sc failed, Smartcard " + "authentication detection might fail in " + "the backend.\n"); + } + + /* FIXME: use the right cert info */ + ret = add_pam_cert_response(preq->pd, cert_user, + preq->current_cert, + SSS_PAM_CERT_INFO); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n"); + preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL; + } } - } - /* We are done if we do not have to call the backend */ - if (preq->pd->cmd == SSS_PAM_AUTHENTICATE - && preq->cert_auth_local) { - preq->pd->pam_status = PAM_SUCCESS; - preq->callback = pam_reply; - pam_reply(preq); - return; } } } - if (preq->pd->cmd == SSS_PAM_PREAUTH) { - DEBUG(SSSDBG_TRACE_FUNC, - "User and certificate user do not match, " - "continue with other authentication methods.\n"); + if (found) { + /* We are done if we do not have to call the backend */ + if (preq->pd->cmd == SSS_PAM_AUTHENTICATE + && preq->cert_auth_local) { + preq->pd->pam_status = PAM_SUCCESS; + preq->callback = pam_reply; + pam_reply(preq); + return; + } } else { - DEBUG(SSSDBG_CRIT_FAILURE, - "User and certificate user do not match.\n"); - preq->pd->pam_status = PAM_AUTH_ERR; - pam_reply(preq); - return; + if (preq->pd->cmd == SSS_PAM_PREAUTH) { + DEBUG(SSSDBG_TRACE_FUNC, + "User and certificate user do not match, " + "continue with other authentication methods.\n"); + } else { + DEBUG(SSSDBG_CRIT_FAILURE, + "User and certificate user do not match.\n"); + preq->pd->pam_status = PAM_AUTH_ERR; + pam_reply(preq); + return; + } } } diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c index ff32d1e..57c8e1e 100644 --- a/src/responder/pam/pamsrv_p11.c +++ b/src/responder/pam/pamsrv_p11.c @@ -40,10 +40,80 @@ struct cert_auth_info { char *token_name; char *module_name; char *key_id; + struct ldb_result *cert_user_objs; struct cert_auth_info *prev; struct cert_auth_info *next; }; +const char *sss_cai_get_cert(struct cert_auth_info *i) +{ + return i != NULL ? i->cert : NULL; +} + +const char *sss_cai_get_token_name(struct cert_auth_info *i) +{ + return i != NULL ? i->token_name : NULL; +} + +const char *sss_cai_get_module_name(struct cert_auth_info *i) +{ + return i != NULL ? i->module_name : NULL; +} + +const char *sss_cai_get_key_id(struct cert_auth_info *i) +{ + return i != NULL ? i->key_id : NULL; +} + +struct cert_auth_info *sss_cai_get_next(struct cert_auth_info *i) +{ + return i != NULL ? i->next : NULL; +} + +struct ldb_result *sss_cai_get_cert_user_objs(struct cert_auth_info *i) +{ + return i != NULL ? i->cert_user_objs : NULL; +} + +void sss_cai_set_cert_user_objs(struct cert_auth_info *i, + struct ldb_result *cert_user_objs) +{ + if (i->cert_user_objs != NULL) { + talloc_free(i->cert_user_objs); + } + i->cert_user_objs = talloc_steal(i, cert_user_objs); +} + +void sss_cai_check_users(struct cert_auth_info **list, size_t *_cert_count, + size_t *_cert_user_count) +{ + struct cert_auth_info *c; + struct cert_auth_info *tmp; + size_t cert_count = 0; + size_t cert_user_count = 0; + struct ldb_result *user_objs; + + DLIST_FOR_EACH_SAFE(c, tmp, *list) { + user_objs = sss_cai_get_cert_user_objs(c); + if (user_objs != NULL) { + cert_count++; + cert_user_count += user_objs->count; + } else { + DLIST_REMOVE(*list, c); + } + } + + if (_cert_count != NULL) { + *_cert_count = cert_count; + } + + if (_cert_user_count != NULL) { + *_cert_user_count = cert_user_count; + } + + return; +} + errno_t p11_child_init(struct pam_ctx *pctx) { return child_debug_init(P11_CHILD_LOG_FILE, &pctx->p11_child_debug_fd); @@ -566,39 +636,71 @@ static void p11_child_timeout(struct tevent_context *ev, } errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, - char **cert, char **token_name, char **module_name, - char **key_id) + struct cert_auth_info **cert_list) { + struct cert_auth_info *tmp_cert_auth_info; struct pam_check_cert_state *state = tevent_req_data(req, struct pam_check_cert_state); TEVENT_REQ_RETURN_ON_ERROR(req); - if (state->cert_list == NULL) { - *token_name = NULL; - *cert = NULL; - *module_name = NULL; - *key_id = NULL; + if (cert_list != NULL) { + DLIST_FOR_EACH(tmp_cert_auth_info, state->cert_list) { + talloc_steal(mem_ctx, tmp_cert_auth_info); + } + + *cert_list = state->cert_list; } - if (cert != NULL) { - *cert = (state->cert_list == NULL) ? NULL - : talloc_steal(mem_ctx, state->cert_list->cert); + return EOK; +} + +static errno_t pack_cert_data(TALLOC_CTX *mem_ctx, const char *sysdb_username, + struct cert_auth_info *cert_info, + uint8_t **_msg, size_t *_msg_len) +{ + uint8_t *msg = NULL; + size_t msg_len; + const char *token_name; + const char *module_name; + const char *key_id; + size_t user_len; + size_t token_len; + size_t module_len; + size_t key_id_len; + const char *username = ""; + + if (sysdb_username != NULL) { + username = sysdb_username; } - if (token_name != NULL) { - *token_name = (state->cert_list == NULL) ? NULL - : talloc_steal(mem_ctx, state->cert_list->token_name); + token_name = sss_cai_get_token_name(cert_info); + module_name = sss_cai_get_module_name(cert_info); + key_id = sss_cai_get_key_id(cert_info); + + user_len = strlen(username) + 1; + token_len = strlen(token_name) + 1; + module_len = strlen(module_name) + 1; + key_id_len = strlen(key_id) + 1; + msg_len = user_len + token_len + module_len + key_id_len; + + msg = talloc_zero_size(mem_ctx, msg_len); + if (msg == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n"); + return ENOMEM; } - if (module_name != NULL) { - *module_name = (state->cert_list == NULL) ? NULL - : talloc_steal(mem_ctx, state->cert_list->module_name); + memcpy(msg, username, user_len); + memcpy(msg + user_len, token_name, token_len); + memcpy(msg + user_len + token_len, module_name, module_len); + memcpy(msg + user_len + token_len + module_len, key_id, key_id_len); + + if (_msg != NULL) { + *_msg = msg; } - if (key_id != NULL) { - *key_id = (state->cert_list == NULL) ? NULL - : talloc_steal(mem_ctx, state->cert_list->key_id); + if (_msg_len != NULL) { + *_msg_len = msg_len; } return EOK; @@ -613,18 +715,13 @@ errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, #define PKCS11_LOGIN_TOKEN_ENV_NAME "PKCS11_LOGIN_TOKEN_NAME" errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username, - const char *token_name, const char *module_name, - const char *key_id, enum response_type type) + struct cert_auth_info *cert_info, + enum response_type type) { uint8_t *msg = NULL; char *env = NULL; - size_t user_len; size_t msg_len; - size_t slot_len; - size_t module_len; - size_t key_id_len; int ret; - const char *username = ""; if (type != SSS_PAM_CERT_INFO && type != SSS_PAM_CERT_INFO_WITH_HINT) { DEBUG(SSSDBG_CRIT_FAILURE, "Invalid response type [%d].\n", type); @@ -632,26 +729,14 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username, } if ((type == SSS_PAM_CERT_INFO && sysdb_username == NULL) - || token_name == NULL || module_name == NULL || key_id == NULL) { + || cert_info == NULL + || sss_cai_get_token_name(cert_info) == NULL + || sss_cai_get_module_name(cert_info) == NULL + || sss_cai_get_key_id(cert_info) == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Missing mandatory user or slot name.\n"); return EINVAL; } - if (sysdb_username != NULL) { - username = sysdb_username; - } - user_len = strlen(username) + 1; - slot_len = strlen(token_name) + 1; - module_len = strlen(module_name) + 1; - key_id_len = strlen(key_id) + 1; - msg_len = user_len + slot_len + module_len + key_id_len; - - msg = talloc_zero_size(pd, msg_len); - if (msg == NULL) { - DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n"); - return ENOMEM; - } - /* sysdb_username is a fully-qualified name which is used by pam_sss when * prompting the user for the PIN and as login name if it wasn't set by * the PAM caller but has to be determined based on the inserted @@ -659,10 +744,12 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username, * re_expression config option was set in a way that user@domain cannot be * handled anymore some more logic has to be added here. But for the time * being I think using sysdb_username is fine. */ - memcpy(msg, username, user_len); - memcpy(msg + user_len, token_name, slot_len); - memcpy(msg + user_len + slot_len, module_name, module_len); - memcpy(msg + user_len + slot_len + module_len, key_id, key_id_len); + + ret = pack_cert_data(pd, sysdb_username, cert_info, &msg, &msg_len); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "pack_cert_data failed.\n"); + return ret; + } ret = pam_add_response(pd, type, msg_len, msg); talloc_free(msg); @@ -674,7 +761,7 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username, if (strcmp(pd->service, "gdm-smartcard") == 0) { env = talloc_asprintf(pd, "%s=%s", PKCS11_LOGIN_TOKEN_ENV_NAME, - token_name); + sss_cai_get_token_name(cert_info)); if (env == NULL) { DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n"); return ENOMEM; diff --git a/src/tests/cmocka/p11_nssdb_2certs/cert9.db b/src/tests/cmocka/p11_nssdb_2certs/cert9.db new file mode 100644 index 0000000..f8220c3 Binary files /dev/null and b/src/tests/cmocka/p11_nssdb_2certs/cert9.db differ diff --git a/src/tests/cmocka/p11_nssdb_2certs/key4.db b/src/tests/cmocka/p11_nssdb_2certs/key4.db new file mode 100644 index 0000000..e4f8a98 Binary files /dev/null and b/src/tests/cmocka/p11_nssdb_2certs/key4.db differ diff --git a/src/tests/cmocka/p11_nssdb_2certs/pkcs11.txt b/src/tests/cmocka/p11_nssdb_2certs/pkcs11.txt new file mode 100644 index 0000000..73f5279 --- /dev/null +++ b/src/tests/cmocka/p11_nssdb_2certs/pkcs11.txt @@ -0,0 +1,4 @@ +library= +name=NSS Internal PKCS #11 Module +parameters=configdir='sql:../src/tests/cmocka/p11_nssdb' certPrefix='' keyPrefix='' secmod='secmod.db' flags= updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription='' +NSS=Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={slotFlags=[RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512] askpw=any timeout=30}) diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c index 351067e..7f0ed70 100644 --- a/src/tests/cmocka/test_pam_srv.c +++ b/src/tests/cmocka/test_pam_srv.c @@ -47,6 +47,9 @@ #define NSS_DB_PATH TESTS_PATH #define NSS_DB "sql:"NSS_DB_PATH +#define NSS_DB_PATH_2CERTS TESTS_PATH "_2certs" +#define NSS_DB_2CERTS "sql:"NSS_DB_PATH_2CERTS + #define TEST_TOKEN_NAME "SSSD Test Token" #define TEST_MODULE_NAME "NSS-Internal" #define TEST_KEY_ID "A5EF7DEE625CA5996C8D1BA7D036708161FD49E7" @@ -74,6 +77,28 @@ "8Z+9gqZhCa7FEKJOPNR9RVtJs0qUUutMZrp1zpyx0GTmXQBA7LbgPxy8L68uymEQ" \ "XyQBwOYRORlnfGyu+Yc9c3E0Wx8Tlznz0lqPR9g=" +#define TEST2_KEY_ID "C8D60E009EB195D01A7083EE1D5419251AA87C2C" +#define TEST_TOKEN_2ND_CERT \ +"MIIDazCCAlOgAwIBAgIBBzANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlJUEEu" \ +"REVWRUwxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjA1MjMx" \ +"NDEzMDFaFw0xODA1MTMxNDEzMDFaMCUxEjAQBgNVBAoMCUlQQS5ERVZFTDEPMA0G" \ +"A1UEAwwGSVBBIFJBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3abE" \ +"8LmIc6QN16VVxsMlN/rrCOoZKyyJolSzpP4+K66t+KZUiW/1j1MZogjyYyD39U1F" \ +"zpa2H+pID74XYrdiqP7sp+uE9/k2XOv/nN3FobXDt+fSINLDriCmxNhUZqpgo2uq" \ +"Mmka+yx2iJZwkntEoJTcd3aynoa2Sa2ZZbkMBy5p6/pUQKwnD6scOwe6mUDppIBK" \ +"+ZZRm+u/NDdIRFI5wfKLRR1r/ONaJA9nz1TxSEsgLsjG/1m+Zbb6lGG4pePIFkQ9" \ +"Iotpi64obBh93oIxzQR29lBG/FMjQVHlPIbx+xuGx11Vtp5pAomgFz0HRrj0leI7" \ +"bROE+jnC/VGPLQD2aQIDAQABo4GWMIGTMB8GA1UdIwQYMBaAFPci/0Km5D/L5z7Y" \ +"qwEc7E1/GwgcMEEGCCsGAQUFBwEBBDUwMzAxBggrBgEFBQcwAYYlaHR0cDovL2lw" \ +"YS1kZXZlbC5pcGEuZGV2ZWw6ODAvY2Evb2NzcDAOBgNVHQ8BAf8EBAMCBPAwHQYD" \ +"VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBg" \ +"4Sppx2C3eXPJ4Pd9XElkQPOaBReXf1vV0uk/GlK+rG+aAqAkA2Lryx5PK/iAuzAU" \ +"M6JUpELuQYgqugoCgBXMgsMlpAO/0C3CFq4ZH3KgIsRlRngKPrt6RG0UPMRD1CE2" \ +"tSVkwUWvyK83lDiu2BbWDXyMyz5eZOlp7uHusf5BKvob8jEndHj1YzaNTmVSsDM5" \ +"kiIwf8qgFhsO1HCq08PtAnbVHhqkcvnmIJN98eNWNfTKodDmFVbN8gB0wK+WB5ii" \ +"WVOw7+3/zF1QgqnYX3t+kPLRryip/wvTZkzXWwMNj/W6UHgjNF/4gWGoBgCHu+u3" \ +"EvjMmbVSrEkesibpGQS5" + static char CACHED_AUTH_TIMEOUT_STR[] = "4"; static const int CACHED_AUTH_TIMEOUT = 4; @@ -111,6 +136,13 @@ static errno_t setup_nss_db(void) return ret; } + ret = mkdir(NSS_DB_PATH_2CERTS, 0775); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to create " NSS_DB_PATH_2CERTS ".\n"); + return ret; + } + child_pid = fork(); if (child_pid == 0) { /* child */ ret = execlp("certutil", "certutil", "-N", "--empty-password", "-d", @@ -127,6 +159,22 @@ static errno_t setup_nss_db(void) return ret; } + child_pid = fork(); + if (child_pid == 0) { /* child */ + ret = execlp("certutil", "certutil", "-N", "--empty-password", "-d", + NSS_DB_2CERTS, NULL); + if (ret == -1) { + DEBUG(SSSDBG_FATAL_FAILURE, "execl() failed.\n"); + exit(-1); + } + } else if (child_pid > 0) { + wait(&status); + } else { + ret = errno; + DEBUG(SSSDBG_FATAL_FAILURE, "fork() failed\n"); + return ret; + } + fp = fopen(NSS_DB_PATH"/pkcs11.txt", "w"); if (fp == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "fopen() failed.\n"); @@ -148,6 +196,27 @@ static errno_t setup_nss_db(void) return ret; } + fp = fopen(NSS_DB_PATH_2CERTS"/pkcs11.txt", "w"); + if (fp == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "fopen() failed.\n"); + return ret; + } + ret = fprintf(fp, "library=libsoftokn3.so\nname=soft\n"); + if (ret < 0) { + DEBUG(SSSDBG_FATAL_FAILURE, "fprintf() failed.\n"); + return ret; + } + ret = fprintf(fp, "parameters=configdir='sql:%s/src/tests/cmocka/p11_nssdb_2certs' dbSlotDescription='SSSD Test Slot' dbTokenDescription='SSSD Test Token' secmod='secmod.db' flags=readOnly \n\n", ABS_SRC_DIR); + if (ret < 0) { + DEBUG(SSSDBG_FATAL_FAILURE, "fprintf() failed.\n"); + return ret; + } + ret = fclose(fp); + if (ret != 0) { + DEBUG(SSSDBG_FATAL_FAILURE, "fclose() failed.\n"); + return ret; + } + return EOK; } @@ -174,6 +243,26 @@ static void cleanup_nss_db(void) if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Failed to remove " NSS_DB_PATH "\n"); } + + ret = unlink(NSS_DB_PATH_2CERTS"/cert9.db"); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to remove cert9.db.\n"); + } + + ret = unlink(NSS_DB_PATH_2CERTS"/key4.db"); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to remove key4.db.\n"); + } + + ret = unlink(NSS_DB_PATH_2CERTS"/pkcs11.txt"); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to remove pkcs11.db.\n"); + } + + ret = rmdir(NSS_DB_PATH_2CERTS); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to remove " NSS_DB_PATH "\n"); + } } struct pam_ctx *mock_pctx(TALLOC_CTX *mem_ctx) @@ -749,7 +838,8 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body, } static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen, - enum response_type type, const char *name) + enum response_type type, const char *name, + const char *name2) { size_t rp = 0; uint32_t val; @@ -763,7 +853,11 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen, if (name == NULL || *name == '\0') { assert_int_equal(val, 1); } else { - assert_int_equal(val, 2); + if (name2 == NULL || *name2 == '\0') { + assert_int_equal(val, 2); + } else { + assert_int_equal(val, 3); + } SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); assert_int_equal(val, SSS_PAM_DOMAIN_NAME); @@ -801,6 +895,33 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen, assert_string_equal(body + rp, TEST_KEY_ID); rp += sizeof(TEST_KEY_ID); + if (name2 != NULL && *name2 != '\0') { + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, type); + + SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); + assert_int_equal(val, (strlen(name) + 1 + + sizeof(TEST_TOKEN_NAME) + + sizeof(TEST_MODULE_NAME) + + sizeof(TEST2_KEY_ID))); + + assert_int_equal(*(body + rp + strlen(name)), 0); + assert_string_equal(body + rp, name); + rp += strlen(name) + 1; + + assert_int_equal(*(body + rp + sizeof(TEST_TOKEN_NAME) - 1), 0); + assert_string_equal(body + rp, TEST_TOKEN_NAME); + rp += sizeof(TEST_TOKEN_NAME); + + assert_int_equal(*(body + rp + sizeof(TEST_MODULE_NAME) - 1), 0); + assert_string_equal(body + rp, TEST_MODULE_NAME); + rp += sizeof(TEST_MODULE_NAME); + + assert_int_equal(*(body + rp + sizeof(TEST2_KEY_ID) - 1), 0); + assert_string_equal(body + rp, TEST2_KEY_ID); + rp += sizeof(TEST2_KEY_ID); + } + assert_int_equal(rp, blen); return EOK; @@ -809,7 +930,8 @@ static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen, static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen) { return test_pam_cert_check_ex(status, body, blen, - SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME); + SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME, + NULL); } static int test_pam_cert_check_with_hint(uint32_t status, uint8_t *body, @@ -817,14 +939,22 @@ static int test_pam_cert_check_with_hint(uint32_t status, uint8_t *body, { return test_pam_cert_check_ex(status, body, blen, SSS_PAM_CERT_INFO_WITH_HINT, - "pamuser@"TEST_DOM_NAME); + "pamuser@"TEST_DOM_NAME, NULL); } static int test_pam_cert_check_with_hint_no_user(uint32_t status, uint8_t *body, size_t blen) { return test_pam_cert_check_ex(status, body, blen, - SSS_PAM_CERT_INFO_WITH_HINT, ""); + SSS_PAM_CERT_INFO_WITH_HINT, "", NULL); +} + +static int test_pam_cert_check_2certs(uint32_t status, uint8_t *body, + size_t blen) +{ + return test_pam_cert_check_ex(status, body, blen, + SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME, + "pamuser@"TEST_DOM_NAME); } static int test_pam_offline_chauthtok_check(uint32_t status, @@ -1737,6 +1867,33 @@ static int test_lookup_by_cert_cb(void *pvt) return EOK; } +static int test_lookup_by_cert_cb_2nd_cert_same_user(void *pvt) +{ + int ret; + struct sysdb_attrs *attrs; + unsigned char *der = NULL; + size_t der_size; + + test_lookup_by_cert_cb(pvt); + + attrs = sysdb_new_attrs(pam_test_ctx); + assert_non_null(attrs); + + der = sss_base64_decode(pam_test_ctx, TEST_TOKEN_2ND_CERT, &der_size); + assert_non_null(der); + + ret = sysdb_attrs_add_mem(attrs, SYSDB_USER_MAPPED_CERT, der, der_size); + talloc_free(der); + assert_int_equal(ret, EOK); + + ret = sysdb_set_user_attr(pam_test_ctx->tctx->dom, + pam_test_ctx->pam_user_fqdn, + attrs, + LDB_FLAG_MOD_ADD); + assert_int_equal(ret, EOK); + + return EOK; +} static int test_lookup_by_cert_double_cb(void *pvt) { @@ -2094,6 +2251,51 @@ void test_pam_cert_auth_double_cert(void **state) assert_int_equal(ret, EOK); } +void test_pam_cert_preauth_2certs_one_mapping(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, NSS_DB_2CERTS); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, + test_lookup_by_cert_cb, TEST_TOKEN_CERT, 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_2certs_two_mappings(void **state) +{ + int ret; + + set_cert_auth_param(pam_test_ctx->pctx, NSS_DB_2CERTS); + + mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, + test_lookup_by_cert_cb_2nd_cert_same_user, + TEST_TOKEN_CERT, 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_2certs); + 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) { int ret; @@ -2523,6 +2725,10 @@ int main(int argc, const char *argv[]) pam_test_teardown), cmocka_unit_test_setup_teardown(test_pam_cert_auth_double_cert, pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_preauth_2certs_one_mapping, + pam_test_setup, pam_test_teardown), + cmocka_unit_test_setup_teardown(test_pam_cert_preauth_2certs_two_mappings, + pam_test_setup, pam_test_teardown), #endif /* HAVE_NSS */ cmocka_unit_test_setup_teardown(test_filter_response, diff --git a/src/tests/whitespace_test b/src/tests/whitespace_test index 799e353..f055ed4 100755 --- a/src/tests/whitespace_test +++ b/src/tests/whitespace_test @@ -39,7 +39,7 @@ fi declare found_file=false while read file; do [[ $file == "src/config/testconfigs/noparse.api.conf" ]] && continue - [[ $file =~ ^src/tests/cmocka/p11_nssdb/.*db ]] && continue + [[ $file =~ ^src/tests/cmocka/p11_nssdb.*/.*db ]] && continue test `tail -c 1 $ABS_TOP_SRCDIR/$file` && \ echo "Missing new line at the eof: $file" && \ found_file=true