URL: https://github.com/SSSD/sssd/pull/5852 Author: ikerexxe Title: #5852: ifp: new interface to validate a certificate Action: synchronized
To pull the PR as Git branch: git remote add ghsssd https://github.com/SSSD/sssd git fetch ghsssd pull/5852/head:pr5852 git checkout pr5852
From ce04009aae3ad37f1bb5a7f0a269beb512c3431f Mon Sep 17 00:00:00 2001 From: Iker Pedrosa <ipedr...@redhat.com> Date: Fri, 29 Oct 2021 10:56:58 +0200 Subject: [PATCH 1/2] ifp: new interface to validate a certificate New interface to validate a certificate. The input is the certificate to validate and the output the user path. Resolves: https://github.com/SSSD/sssd/issues/5224 Signed-off-by: Iker Pedrosa <ipedr...@redhat.com> --- src/responder/ifp/ifp_iface/ifp_iface.c | 3 +- src/responder/ifp/ifp_iface/ifp_iface.xml | 4 + .../ifp/ifp_iface/sbus_ifp_client_sync.c | 14 + .../ifp/ifp_iface/sbus_ifp_client_sync.h | 9 + .../ifp/ifp_iface/sbus_ifp_interface.h | 22 ++ .../ifp/ifp_iface/sbus_ifp_symbols.c | 12 + .../ifp/ifp_iface/sbus_ifp_symbols.h | 3 + src/responder/ifp/ifp_users.c | 346 ++++++++++++++++++ src/responder/ifp/ifp_users.h | 12 + 9 files changed, 424 insertions(+), 1 deletion(-) diff --git a/src/responder/ifp/ifp_iface/ifp_iface.c b/src/responder/ifp/ifp_iface/ifp_iface.c index 833cf6843a..16035de57a 100644 --- a/src/responder/ifp/ifp_iface/ifp_iface.c +++ b/src/responder/ifp/ifp_iface/ifp_iface.c @@ -153,7 +153,8 @@ ifp_register_sbus_interface(struct sbus_connection *conn, SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, ListByCertificate, ifp_users_list_by_cert_send, ifp_users_list_by_cert_recv, ctx), SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, FindByNameAndCertificate, ifp_users_find_by_name_and_cert_send, ifp_users_find_by_name_and_cert_recv, ctx), SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, ListByName, ifp_users_list_by_name_send, ifp_users_list_by_name_recv, ctx), - SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, ListByDomainAndName, ifp_users_list_by_domain_and_name_send, ifp_users_list_by_domain_and_name_recv, ctx) + SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, ListByDomainAndName, ifp_users_list_by_domain_and_name_send, ifp_users_list_by_domain_and_name_recv, ctx), + SBUS_ASYNC(METHOD, org_freedesktop_sssd_infopipe_Users, FindByValidCertificate, ifp_users_find_by_valid_cert_send, ifp_users_find_by_valid_cert_recv, ctx) ), SBUS_SIGNALS(SBUS_NO_SIGNALS), SBUS_PROPERTIES(SBUS_NO_PROPERTIES) diff --git a/src/responder/ifp/ifp_iface/ifp_iface.xml b/src/responder/ifp/ifp_iface/ifp_iface.xml index a4b2f861a8..555513c0d8 100644 --- a/src/responder/ifp/ifp_iface/ifp_iface.xml +++ b/src/responder/ifp/ifp_iface/ifp_iface.xml @@ -182,6 +182,10 @@ <arg name="limit" type="u" direction="in" key="3" /> <arg name="result" type="ao" direction="out"/> </method> + <method name="FindByValidCertificate"> + <arg name="pem_cert" type="s" direction="in" /> + <arg name="result" type="o" direction="out" /> + </method> </interface> <interface name="org.freedesktop.sssd.infopipe.Users.User"> diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.c b/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.c index 1f0a8e3679..03b668e78a 100644 --- a/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.c +++ b/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.c @@ -1161,6 +1161,20 @@ sbus_call_ifp_users_FindByNameAndCertificate _arg_result); } +errno_t +sbus_call_ifp_users_FindByValidCertificate + (TALLOC_CTX *mem_ctx, + struct sbus_sync_connection *conn, + const char *busname, + const char *object_path, + const char * arg_pem_cert, + const char ** _arg_result) +{ + return sbus_method_in_s_out_o(mem_ctx, conn, + busname, object_path, "org.freedesktop.sssd.infopipe.Users", "FindByValidCertificate", arg_pem_cert, + _arg_result); +} + errno_t sbus_call_ifp_users_ListByCertificate (TALLOC_CTX *mem_ctx, diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.h b/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.h index 41e329d0d6..e48a6ebd9f 100644 --- a/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.h +++ b/src/responder/ifp/ifp_iface/sbus_ifp_client_sync.h @@ -275,6 +275,15 @@ sbus_call_ifp_users_FindByNameAndCertificate const char * arg_pem_cert, const char ** _arg_result); +errno_t +sbus_call_ifp_users_FindByValidCertificate + (TALLOC_CTX *mem_ctx, + struct sbus_sync_connection *conn, + const char *busname, + const char *object_path, + const char * arg_pem_cert, + const char ** _arg_result); + errno_t sbus_call_ifp_users_ListByCertificate (TALLOC_CTX *mem_ctx, diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_interface.h b/src/responder/ifp/ifp_iface/sbus_ifp_interface.h index 214250e583..2e1fdb5d03 100644 --- a/src/responder/ifp/ifp_iface/sbus_ifp_interface.h +++ b/src/responder/ifp/ifp_iface/sbus_ifp_interface.h @@ -1557,6 +1557,28 @@ (handler_send), (handler_recv), (data)); \ }) +/* Method: org.freedesktop.sssd.infopipe.Users.FindByValidCertificate */ +#define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Users_FindByValidCertificate(handler, data) ({ \ + SBUS_CHECK_SYNC((handler), (data), const char *, const char **); \ + sbus_method_sync("FindByValidCertificate", \ + &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByValidCertificate, \ + NULL, \ + _sbus_ifp_invoke_in_s_out_o_send, \ + NULL, \ + (handler), (data)); \ +}) + +#define SBUS_METHOD_ASYNC_org_freedesktop_sssd_infopipe_Users_FindByValidCertificate(handler_send, handler_recv, data) ({ \ + SBUS_CHECK_SEND((handler_send), (data), const char *); \ + SBUS_CHECK_RECV((handler_recv), const char **); \ + sbus_method_async("FindByValidCertificate", \ + &_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByValidCertificate, \ + NULL, \ + _sbus_ifp_invoke_in_s_out_o_send, \ + NULL, \ + (handler_send), (handler_recv), (data)); \ +}) + /* Method: org.freedesktop.sssd.infopipe.Users.ListByCertificate */ #define SBUS_METHOD_SYNC_org_freedesktop_sssd_infopipe_Users_ListByCertificate(handler, data) ({ \ SBUS_CHECK_SYNC((handler), (data), const char *, uint32_t, const char ***); \ diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_symbols.c b/src/responder/ifp/ifp_iface/sbus_ifp_symbols.c index 6b0630f666..2d316064b4 100644 --- a/src/responder/ifp/ifp_iface/sbus_ifp_symbols.c +++ b/src/responder/ifp/ifp_iface/sbus_ifp_symbols.c @@ -359,6 +359,18 @@ _sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByNameAndCertificate = { } }; +const struct sbus_method_arguments +_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByValidCertificate = { + .input = (const struct sbus_argument[]){ + {.type = "s", .name = "pem_cert"}, + {NULL} + }, + .output = (const struct sbus_argument[]){ + {.type = "o", .name = "result"}, + {NULL} + } +}; + const struct sbus_method_arguments _sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByCertificate = { .input = (const struct sbus_argument[]){ diff --git a/src/responder/ifp/ifp_iface/sbus_ifp_symbols.h b/src/responder/ifp/ifp_iface/sbus_ifp_symbols.h index 53d303c4c4..e660dd8a9a 100644 --- a/src/responder/ifp/ifp_iface/sbus_ifp_symbols.h +++ b/src/responder/ifp/ifp_iface/sbus_ifp_symbols.h @@ -109,6 +109,9 @@ _sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByName; extern const struct sbus_method_arguments _sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByNameAndCertificate; +extern const struct sbus_method_arguments +_sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_FindByValidCertificate; + extern const struct sbus_method_arguments _sbus_ifp_args_org_freedesktop_sssd_infopipe_Users_ListByCertificate; diff --git a/src/responder/ifp/ifp_users.c b/src/responder/ifp/ifp_users.c index ac9330858f..77b7466cee 100644 --- a/src/responder/ifp/ifp_users.c +++ b/src/responder/ifp/ifp_users.c @@ -26,6 +26,8 @@ #include "util/util.h" #include "util/strtonum.h" #include "util/cert.h" +#include "util/child_common.h" +#include "util/crypto/sss_crypto.h" #include "responder/common/responder.h" #include "responder/common/cache_req/cache_req.h" #include "responder/ifp/ifp_users.h" @@ -1029,6 +1031,350 @@ ifp_users_list_by_domain_and_name_recv(TALLOC_CTX *mem_ctx, return EOK; } +struct ifp_users_cert_validation { + struct ifp_ctx *ifp_ctx; + struct tevent_context *ev; + const char *logfile; + time_t timeout; + char *ca_db; + char *verify_opts; + char *derb64; + const char **extra_args; + const char *path; + + struct sss_child_ctx_old *child_ctx; + struct tevent_timer *timeout_handler; + struct child_io_fds *io; +}; + +static errno_t p11_child_exec(struct tevent_req *req); +static void ifp_users_find_by_valid_cert_step(struct tevent_req *subreq); +static struct tevent_req * +ifp_users_find_by_valid_cert_done(int child_status, + struct tevent_signal *sige, + void *pvt); + +struct tevent_req * +ifp_users_find_by_valid_cert_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char *pem_cert) +{ + TALLOC_CTX *tmp_ctx = NULL; + struct tevent_req *req; + struct ifp_users_cert_validation *state; + size_t arg_c = 0; + int ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + req = tevent_req_create(mem_ctx, &state, struct ifp_users_cert_validation); + if (req == NULL) { + return NULL; + } + + state->ifp_ctx = ctx; + + ret = confdb_get_string(ctx->rctx->cdb, tmp_ctx, + CONFDB_IFP_CONF_ENTRY, CONFDB_SSH_CA_DB, + CONFDB_DEFAULT_SSH_CA_DB, &state->ca_db); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Error reading CA DB from confdb (%d) [%s]\n", + ret, strerror(ret)); + goto done; + } + + ret = confdb_get_int(ctx->rctx->cdb, CONFDB_IFP_CONF_ENTRY, + CONFDB_PAM_P11_CHILD_TIMEOUT, -1, + (int *) &state->timeout); + if (ret != EOK || state->timeout == -1) { + /* check pam configuration as well or use default */ + ret = confdb_get_int(ctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_P11_CHILD_TIMEOUT, + P11_CHILD_TIMEOUT_DEFAULT, + (int *) &state->timeout); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to read p11_child_timeout from confdb: [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + } + + ret = confdb_get_string(ctx->rctx->cdb, tmp_ctx, CONFDB_MONITOR_CONF_ENTRY, + CONFDB_MONITOR_CERT_VERIFICATION, NULL, + &state->verify_opts); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to read '"CONFDB_MONITOR_CERT_VERIFICATION"' from confdb: [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + state->ev = ev; + state->logfile = P11_CHILD_LOG_FILE; + state->io = talloc(state, struct child_io_fds); + if (state->io == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc failed.\n"); + ret = ENOMEM; + goto done; + } + state->io->write_to_child_fd = -1; + state->io->read_from_child_fd = -1; + talloc_set_destructor((void *) state->io, child_io_destructor); + + ret = sss_cert_pem_to_derb64(state, pem_cert, &state->derb64); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "sss_cert_pem_to_derb64 failed.\n"); + goto done; + } + + state->extra_args = talloc_zero_array(state, const char *, 8); + if (state->extra_args == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n"); + ret = ENOMEM; + goto done; + } + state->extra_args[arg_c++] = state->derb64; + state->extra_args[arg_c++] = "--certificate"; + state->extra_args[arg_c++] = state->ca_db; + state->extra_args[arg_c++] = "--ca_db"; + if (state->verify_opts != NULL) { + state->extra_args[arg_c++] = state->verify_opts; + state->extra_args[arg_c++] = "--verify"; + } + state->extra_args[arg_c++] = "--verification"; + + ret = p11_child_exec(req); + +done: + talloc_free(tmp_ctx); + + if (ret != EAGAIN) { + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, ev); + } + + return req; +} + +static void p11_child_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct ifp_users_cert_validation *state = + tevent_req_data(req, struct ifp_users_cert_validation); + + DEBUG(SSSDBG_MINOR_FAILURE, "Timeout reached for p11_child.\n"); + child_handler_destroy(state->child_ctx); + state->child_ctx = NULL; + tevent_req_error(req, ERR_P11_CHILD_TIMEOUT); +} + +static errno_t p11_child_exec(struct tevent_req *req) +{ + struct ifp_users_cert_validation *state = tevent_req_data(req, + struct ifp_users_cert_validation); + int ret; + int pipefd_from_child[2] = PIPE_INIT; + int pipefd_to_child[2] = PIPE_INIT; + pid_t child_pid; + struct timeval tv; + + ret = pipe(pipefd_from_child); + if (ret == -1) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "pipe failed [%d][%s].\n", ret, strerror(ret)); + goto done; + } + ret = pipe(pipefd_to_child); + if (ret == -1) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "pipe failed [%d][%s].\n", ret, strerror(ret)); + goto done; + } + + child_pid = fork(); + if (child_pid == 0) { /* child */ + exec_child_ex(state, pipefd_to_child, pipefd_from_child, P11_CHILD_PATH, + state->logfile, state->extra_args, false, + STDIN_FILENO, STDOUT_FILENO); + /* We should never get here */ + DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Could not exec p11 child\n"); + } else if (child_pid > 0) { /* parent */ + + state->io->read_from_child_fd = pipefd_from_child[0]; + PIPE_FD_CLOSE(pipefd_from_child[1]); + sss_fd_nonblocking(state->io->read_from_child_fd); + + state->io->write_to_child_fd = pipefd_to_child[1]; + PIPE_FD_CLOSE(pipefd_to_child[0]); + sss_fd_nonblocking(state->io->write_to_child_fd); + + /* Set up SIGCHLD handler */ + ret = child_handler_setup(state->ev, child_pid, ifp_users_find_by_valid_cert_done, + req, &state->child_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Could not set up child handlers [%d]: %s\n", + ret, sss_strerror(ret)); + ret = ERR_P11_CHILD; + goto done; + } + + /* Set up timeout handler */ + tv = tevent_timeval_current_ofs(state->timeout, 0); + state->timeout_handler = tevent_add_timer(state->ev, req, tv, + p11_child_timeout, + req); + if (state->timeout_handler == NULL) { + ret = ERR_P11_CHILD; + goto done; + } + /* Now either wait for the timeout to fire or the child to finish */ + } else { /* error */ + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, "fork failed [%d][%s].\n", + ret, sss_strerror(ret)); + goto done; + } + + return EAGAIN; + +done: + if (ret != EOK) { + PIPE_CLOSE(pipefd_from_child); + PIPE_CLOSE(pipefd_to_child); + } + + return ret; +} + +static struct tevent_req * +ifp_users_find_by_valid_cert_done(int child_status, + struct tevent_signal *sige, + void *pvt) +{ + struct tevent_req *subreq; + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct ifp_users_cert_validation *state = tevent_req_data(req, + struct ifp_users_cert_validation); + bool valid = false; + errno_t ret; + + PIPE_FD_CLOSE(state->io->read_from_child_fd); + PIPE_FD_CLOSE(state->io->write_to_child_fd); + + if (WIFEXITED(child_status)) { + if (WEXITSTATUS(child_status) != 0) { + DEBUG(SSSDBG_OP_FAILURE, + P11_CHILD_PATH " failed with status [%d]\n", child_status); + } else { + valid = true; + } + } + + if (WIFSIGNALED(child_status)) { + DEBUG(SSSDBG_OP_FAILURE, + P11_CHILD_PATH " was terminated by signal [%d]\n", + WTERMSIG(child_status)); + } + + if (valid) { + DEBUG(SSSDBG_TRACE_LIBS, "Certificate [%s] is valid.\n", + state->extra_args[0]); + + subreq = cache_req_user_by_cert_send(state, state->ifp_ctx->rctx->ev, + state->ifp_ctx->rctx, + state->ifp_ctx->rctx->ncache, 0, + CACHE_REQ_ANY_DOM, NULL, state->derb64); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, ifp_users_find_by_valid_cert_step, req); + } else { + DEBUG(SSSDBG_MINOR_FAILURE, "Certificate [%s] is not valid.\n", + state->extra_args[0]); + tevent_req_error(req, ERR_INVALID_CERT); + } + + ret = EAGAIN; + +done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, state->ifp_ctx->rctx->ev); + } + + return req; +} + +static void ifp_users_find_by_valid_cert_step(struct tevent_req *subreq) +{ + struct ifp_users_cert_validation *state; + struct cache_req_result *result; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ifp_users_cert_validation); + + ret = cache_req_user_by_cert_recv(state, subreq, &result); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to find user [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (result->count > 1) { + DEBUG(SSSDBG_CRIT_FAILURE, "More than one user found. " + "Use ListByCertificate to get all.\n"); + tevent_req_error(req, EINVAL); + return; + } + + state->path = ifp_users_build_path_from_msg(state, result->domain, + result->msgs[0]); + if (state->path == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + tevent_req_done(req); + return; +} + +errno_t +ifp_users_find_by_valid_cert_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char **_path) +{ + struct ifp_users_cert_validation *state = tevent_req_data(req, + struct ifp_users_cert_validation); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_path = talloc_steal(mem_ctx, state->path); + + return EOK; +} + static errno_t ifp_users_get_from_cache(TALLOC_CTX *mem_ctx, struct sss_domain_info *domain, diff --git a/src/responder/ifp/ifp_users.h b/src/responder/ifp/ifp_users.h index 5dcc44cd4b..8a970eddb4 100644 --- a/src/responder/ifp/ifp_users.h +++ b/src/responder/ifp/ifp_users.h @@ -120,6 +120,18 @@ ifp_users_list_by_domain_and_name_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, const char ***_paths); +struct tevent_req * +ifp_users_find_by_valid_cert_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct ifp_ctx *ctx, + const char *pem_cert); + +errno_t +ifp_users_find_by_valid_cert_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char **_path); + /* org.freedesktop.sssd.infopipe.Users.User */ struct tevent_req * From 7cc80d9c0d8f466f7080b7789f396b1cf03a2859 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa <ipedr...@redhat.com> Date: Fri, 29 Oct 2021 11:01:54 +0200 Subject: [PATCH 2/2] Tests: ifp interface to validate certificate Integration test to check the interface that validates the user certificate. Signed-off-by: Iker Pedrosa <ipedr...@redhat.com> --- src/tests/intg/Makefile.am | 2 + src/tests/intg/data/cert_schema.ldif | 11 ++ src/tests/intg/ds_openldap.py | 6 ++ src/tests/intg/ldap_ent.py | 2 +- src/tests/intg/test_infopipe.py | 102 ++++++++++++++++++- src/tests/test_CA/SSSD_test_cert_0001.config | 1 + 6 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 src/tests/intg/data/cert_schema.ldif diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am index ad02109c20..0ff45e5727 100644 --- a/src/tests/intg/Makefile.am +++ b/src/tests/intg/Makefile.am @@ -31,6 +31,7 @@ dist_noinst_DATA = \ test_pac_responder.py \ data/ad_data.ldif \ data/ad_schema.ldif \ + data/cert_schema.ldif \ data/sudo_schema.ldif \ test_pysss_nss_idmap.py \ test_infopipe.py \ @@ -208,6 +209,7 @@ intgcheck-installed: config.py passwd group pam_sss_service pam_sss_alt_service PAM_WRAPPER_SERVICE_DIR="$(abs_builddir)/$(PAM_SERVICE_DIR)" \ PAM_WRAPPER_PATH=$$(pkg-config --libs pam_wrapper) \ PAM_CERT_DB_PATH=$(PAM_CERT_DB_PATH) \ + ABS_SRCDIR=$(abs_srcdir) \ SOFTHSM2_CONF=$(SOFTHSM2_CONF) \ KCM_RENEW=$(KCM_RENEW) \ DBUS_SOCK_DIR="$(DESTDIR)$(runstatedir)/dbus/" \ diff --git a/src/tests/intg/data/cert_schema.ldif b/src/tests/intg/data/cert_schema.ldif new file mode 100644 index 0000000000..0003c66232 --- /dev/null +++ b/src/tests/intg/data/cert_schema.ldif @@ -0,0 +1,11 @@ +dn: cn=cert,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: cert +olcAttributeTypes: ( 1.2.840.113556.1.4.645 NAME 'userCert' + DESC 'MANDATORY: X.509 user certificate' + EQUALITY octetStringMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 ) +olcObjectClasses: ( 1.2.840.113556.1.3.46 NAME 'mailRecipient' SUP top AUXILIARY + DESC 'MANDATORY: X.509 objectclass' + MAY ( userCert $ uid ) + ) diff --git a/src/tests/intg/ds_openldap.py b/src/tests/intg/ds_openldap.py index fc3140722b..047b61512b 100644 --- a/src/tests/intg/ds_openldap.py +++ b/src/tests/intg/ds_openldap.py @@ -199,6 +199,12 @@ def _setup_config(self): "-l", "data/sudo_schema.ldif"], ) + # Import cert schema + subprocess.check_call( + ["slapadd", "-F", self.conf_slapd_d_dir, "-b", "cn=config", + "-l", "data/cert_schema.ldif"], + ) + def _start_daemon(self): """Start the instance.""" if subprocess.call(["slapd", "-F", self.conf_slapd_d_dir, diff --git a/src/tests/intg/ldap_ent.py b/src/tests/intg/ldap_ent.py index 5dff2ff2b1..2a56f3d9cb 100644 --- a/src/tests/intg/ldap_ent.py +++ b/src/tests/intg/ldap_ent.py @@ -36,7 +36,7 @@ def user(base_dn, uid, uidNumber, gidNumber, user = ( "uid=" + uid + ",ou=Users," + base_dn, [ - ('objectClass', [b'top', b'inetOrgPerson', + ('objectClass', [b'top', b'inetOrgPerson', b'mailRecipient', b'posixAccount', b'ldapPublicKey']), ('cn', [uidNumber if cn is None else cn.encode('utf-8')]), ('sn', [b'User' if sn is None else sn.encode('utf-8')]), diff --git a/src/tests/intg/test_infopipe.py b/src/tests/intg/test_infopipe.py index 5aefcac9e9..909cd9dc86 100644 --- a/src/tests/intg/test_infopipe.py +++ b/src/tests/intg/test_infopipe.py @@ -31,6 +31,7 @@ import ldap.modlist import pytest import dbus +import base64 import config import ds_openldap @@ -184,7 +185,7 @@ def create_ldap_fixture(request, ldap_conn, ent_list=None): SCHEMA_RFC2307_BIS = "rfc2307bis" -def format_basic_conf(ldap_conn, schema): +def format_basic_conf(ldap_conn, schema, config): """Format a basic SSSD configuration""" schema_conf = "ldap_schema = " + schema + "\n" if schema == SCHEMA_RFC2307_BIS: @@ -209,6 +210,7 @@ def format_basic_conf(ldap_conn, schema): # There is not such problem in 1st test. Just in following tests. command = {ifp_command} --uid 0 --gid 0 --logger=files user_attributes = +extraName + ca_db = {config.PAM_CERT_DB_PATH} [domain/LDAP] {schema_conf} @@ -216,6 +218,7 @@ def format_basic_conf(ldap_conn, schema): ldap_uri = {ldap_conn.ds_inst.ldap_url} ldap_search_base = {ldap_conn.ds_inst.base_dn} ldap_user_extra_attrs = extraName:uid + ldap_user_certificate = userCert [application/app] inherit_from = LDAP @@ -237,6 +240,16 @@ def format_interactive_conf(ldap_conn, schema): """).format(INTERACTIVE_TIMEOUT) +def format_certificate_conf(ldap_conn, schema, config): + """Format an SSSD configuration with all caches refreshing in 4 seconds""" + return \ + format_basic_conf(ldap_conn, schema, config) + \ + unindent(""" + [certmap/LDAP/user1] + matchrule = <SUBJECT>.*CN = SSSD test cert 0001.* + """).format(**locals()) + + def create_conf_file(contents): """Create sssd.conf with specified contents""" with open(config.CONF_PATH, "w") as conf: @@ -318,7 +331,8 @@ def sanity_rfc2307(request, ldap_conn): create_ldap_fixture(request, ldap_conn, ent_list) - conf = format_basic_conf(ldap_conn, SCHEMA_RFC2307) + config.PAM_CERT_DB_PATH = os.environ['PAM_CERT_DB_PATH'] + conf = format_basic_conf(ldap_conn, SCHEMA_RFC2307, config) create_conf_fixture(request, conf) create_sssd_fixture(request) return None @@ -330,7 +344,8 @@ def simple_rfc2307(request, ldap_conn): ent_list.add_user('usr\\\\001', 181818, 181818) ent_list.add_group("group1", 181818) create_ldap_fixture(request, ldap_conn, ent_list) - conf = format_basic_conf(ldap_conn, SCHEMA_RFC2307) + config.PAM_CERT_DB_PATH = os.environ['PAM_CERT_DB_PATH'] + conf = format_basic_conf(ldap_conn, SCHEMA_RFC2307, config) create_conf_fixture(request, conf) create_sssd_fixture(request) return None @@ -347,8 +362,9 @@ def auto_private_groups_rfc2307(request, ldap_conn): create_ldap_fixture(request, ldap_conn, ent_list) + config.PAM_CERT_DB_PATH = os.environ['PAM_CERT_DB_PATH'] conf = \ - format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \ + format_basic_conf(ldap_conn, SCHEMA_RFC2307, config) + \ unindent(""" [domain/LDAP] auto_private_groups = True @@ -358,6 +374,34 @@ def auto_private_groups_rfc2307(request, ldap_conn): return None +@pytest.fixture +def add_user_with_cert(request, ldap_conn): + config.PAM_CERT_DB_PATH = os.environ['PAM_CERT_DB_PATH'] + + ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn) + ent_list.add_user("user1", 1001, 2001) + + create_ldap_fixture(request, ldap_conn, ent_list) + + der_path = os.path.dirname(config.PAM_CERT_DB_PATH) + der_path += "/SSSD_test_cert_x509_0001.der" + with open(der_path, 'rb') as f: + val = f.read() + dn = "uid=user1,ou=Users," + LDAP_BASE_DN + ''' + Using 'userCert' instead of 'userCertificate' to hold the user certificate + because the default OpenLDAP has syntax and matching rules which are not + used in other LDAP servers. + ''' + ldap_conn.modify_s(dn, [(ldap.MOD_ADD, 'userCert', val)]) + + conf = format_certificate_conf(ldap_conn, SCHEMA_RFC2307_BIS, config) + create_conf_fixture(request, conf) + create_sssd_fixture(request) + + return None + + def test_ping_raw(dbus_system_bus, ldap_conn, simple_rfc2307): # test with disabled introspection sssd_obj = dbus_system_bus.get_object('org.freedesktop.sssd.infopipe', @@ -669,3 +713,53 @@ def test_update_member_list_and_get_all(dbus_system_bus, 'org.freedesktop.DBus.Properties') res = prop_interface.GetAll('org.freedesktop.sssd.infopipe.Groups.Group') assert not res.get("users") + + +def test_find_by_valid_certificate(dbus_system_bus, + ldap_conn, + add_user_with_cert): + users_obj = dbus_system_bus.get_object( + 'org.freedesktop.sssd.infopipe', + '/org/freedesktop/sssd/infopipe/Users') + users_iface = dbus.Interface(users_obj, + 'org.freedesktop.sssd.infopipe.Users') + cert_path = os.path.dirname(config.PAM_CERT_DB_PATH) + + # Valid certificate with user + cert_file = cert_path + "/SSSD_test_cert_x509_0001.pem" + with open(cert_file, "r") as f: + cert = f.read() + res = users_iface.FindByValidCertificate(cert) + assert res == "/org/freedesktop/sssd/infopipe/Users/app/user1_40app" + + # Valid certificate without user + cert_file = cert_path + "/SSSD_test_cert_x509_0002.pem" + with open(cert_file, "r") as f: + cert = f.read() + try: + res = users_iface.FindByValidCertificate(cert) + assert False, "Previous call should raise an exception" + except dbus.exceptions.DBusException as ex: + assert str(ex) == "sbus.Error.NotFound: No such file or directory" + + # Valid certificate from another CA + print(os.environ['ABS_SRCDIR']) + cert_file = os.environ['ABS_SRCDIR'] + \ + "/../test_ECC_CA/SSSD_test_ECC_cert_key_0001.pem" + with open(cert_file, "r") as f: + cert = f.read() + try: + res = users_iface.FindByValidCertificate(cert) + assert False, "Previous call should raise an exception" + except dbus.exceptions.DBusException as ex: + assert str(ex) == \ + "org.freedesktop.DBus.Error.IOError: Input/output error" + + # Invalid certificate + cert = "Invalid cert" + try: + res = users_iface.FindByValidCertificate(cert) + assert False, "Previous call should raise an exception" + except dbus.exceptions.DBusException as ex: + error = "org.freedesktop.DBus.Error.IOError: Input/output error" + assert str(ex) == error diff --git a/src/tests/test_CA/SSSD_test_cert_0001.config b/src/tests/test_CA/SSSD_test_cert_0001.config index ce593ae5ea..d59023ca66 100644 --- a/src/tests/test_CA/SSSD_test_cert_0001.config +++ b/src/tests/test_CA/SSSD_test_cert_0001.config @@ -1,6 +1,7 @@ # This certificate is used in # - src/tests/cmocka/test_cert_utils.c # - src/tests/cmocka/test_pam_srv.c +# - src/tests/intg/test_infopipe.py [ req ] distinguished_name = req_distinguished_name prompt = no
_______________________________________________ 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://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedorahosted.org/archives/list/sssd-devel@lists.fedorahosted.org Do not reply to spam on the list, report it: https://pagure.io/fedora-infrastructure