Skip to content

Commit

Permalink
p11_child: enable more than one CRL PEM file
Browse files Browse the repository at this point in the history
Enable support for more than one CRL PEM file. p11_child parses the
crl_file list passed as argument, loads all the files and makes the
validation.

Finally, add a new test case in test_utils to check that the p11_child
crl_file argument has been parsed correctly. Add another three test
cases in test_oam_srv to check the validation process.

Resolves: SSSD#6086

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
  • Loading branch information
ikerexxe committed Apr 8, 2022
1 parent 30831cc commit 872dd70
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 30 deletions.
3 changes: 2 additions & 1 deletion src/p11_child/p11_child.h
Expand Up @@ -38,7 +38,8 @@ struct cert_verify_opts {
bool verification_partial_chain;
char *ocsp_default_responder;
char *ocsp_default_responder_signing_cert;
char *crl_file;
char **crl_files;
int num_files;
CK_MECHANISM_TYPE ocsp_dgst;
bool soft_ocsp;
bool soft_crl;
Expand Down
43 changes: 35 additions & 8 deletions src/p11_child/p11_child_common_utils.c
Expand Up @@ -43,7 +43,7 @@ static struct cert_verify_opts *init_cert_verify_opts(TALLOC_CTX *mem_ctx)
cert_verify_opts->verification_partial_chain = false;
cert_verify_opts->ocsp_default_responder = NULL;
cert_verify_opts->ocsp_default_responder_signing_cert = NULL;
cert_verify_opts->crl_file = NULL;
cert_verify_opts->crl_files = NULL;
cert_verify_opts->ocsp_dgst = CKM_SHA_1;
cert_verify_opts->soft_ocsp = false;
cert_verify_opts->soft_crl = false;
Expand All @@ -64,6 +64,38 @@ static struct cert_verify_opts *init_cert_verify_opts(TALLOC_CTX *mem_ctx)
#define OCSP_DGST "ocsp_dgst="
#define OCSP_DGST_LEN (sizeof(OCSP_DGST) -1)

static errno_t parse_crl_files(char **opts, size_t c, struct cert_verify_opts *_opts)
{
int ret;

if (_opts->num_files == 0) {
_opts->crl_files = talloc_array(_opts, char *, 1);
} else {
_opts->crl_files = talloc_realloc(_opts, _opts->crl_files,
char *, _opts->num_files + 1);
}
if (_opts->crl_files == NULL) {
ret = ENOMEM;
goto done;
}

_opts->crl_files[_opts->num_files] = talloc_strdup(_opts,
&opts[c][CRL_FILE_LEN]);
if (_opts->crl_files[_opts->num_files] == NULL
|| *_opts->crl_files[_opts->num_files] == '\0') {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to parse crl_file option [%s].\n", opts[c]);
ret = EINVAL;
goto done;
}

_opts->num_files++;
ret = EOK;

done:
return ret;
}

errno_t parse_cert_verify_opts(TALLOC_CTX *mem_ctx, const char *verify_opts,
struct cert_verify_opts **_cert_verify_opts)
{
Expand Down Expand Up @@ -155,13 +187,8 @@ errno_t parse_cert_verify_opts(TALLOC_CTX *mem_ctx, const char *verify_opts,
"Using OCSP default responder signing cert nickname [%s]\n",
cert_verify_opts->ocsp_default_responder_signing_cert);
} else if (strncasecmp(opts[c], CRL_FILE, CRL_FILE_LEN) == 0) {
cert_verify_opts->crl_file = talloc_strdup(cert_verify_opts,
&opts[c][CRL_FILE_LEN]);
if (cert_verify_opts->crl_file == NULL
|| *cert_verify_opts->crl_file == '\0') {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to parse crl_file option [%s].\n", opts[c]);
ret = EINVAL;
ret = parse_crl_files(opts, c, cert_verify_opts);
if (ret != EOK) {
goto done;
}
} else if (strncasecmp(opts[c], OCSP_DGST, OCSP_DGST_LEN) == 0) {
Expand Down
26 changes: 17 additions & 9 deletions src/p11_child/p11_child_openssl.c
Expand Up @@ -645,6 +645,7 @@ errno_t init_verification(struct p11_ctx *p11_ctx,
int ret;
X509_STORE *store = NULL;
unsigned long err;
int file_index = 0;
X509_LOOKUP *lookup = NULL;
X509_VERIFY_PARAM *verify_param = NULL;

Expand Down Expand Up @@ -688,22 +689,29 @@ errno_t init_verification(struct p11_ctx *p11_ctx,
X509_VERIFY_PARAM_set_flags(verify_param, X509_V_FLAG_PARTIAL_CHAIN);
}

if (cert_verify_opts->crl_file != NULL) {
if (cert_verify_opts->crl_files != NULL) {
if ((ret = ensure_verify_param (&verify_param)) != EOK) {
goto done;
}

X509_VERIFY_PARAM_set_flags(verify_param, (X509_V_FLAG_CRL_CHECK
| X509_V_FLAG_CRL_CHECK_ALL));

ret = X509_load_crl_file(lookup, cert_verify_opts->crl_file,
X509_FILETYPE_PEM);
if (ret == 0) {
err = ERR_get_error();
DEBUG(SSSDBG_OP_FAILURE, "X509_load_crl_file failed [%lu][%s].\n",
err, ERR_error_string(err, NULL));
ret = EIO;
goto done;
while (file_index < cert_verify_opts->num_files) {
ret = X509_load_crl_file(lookup,
cert_verify_opts->crl_files[file_index],
X509_FILETYPE_PEM);
if (ret == 0) {
err = ERR_get_error();
DEBUG(SSSDBG_OP_FAILURE,
"X509_load_crl_file for [%s] failed [%lu][%s].\n",
cert_verify_opts->crl_files[file_index],
err, ERR_error_string(err, NULL));
ret = EIO;
goto done;
}

file_index++;
}
}

Expand Down
131 changes: 131 additions & 0 deletions src/tests/cmocka/test_pam_srv.c
Expand Up @@ -3171,6 +3171,131 @@ void test_pam_preauth_ocsp_soft_ocsp(void **state)
assert_int_equal(ret, EOK);
}

/* With two valid CRL files a certificate should be returned */
void test_pam_preauth_valid_crl_files(void **state)
{
int ret;

struct sss_test_conf_param monitor_params[] = {
{ "certificate_verification",
"crl_file=" ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA_crl.pem,"
"crl_file=" ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA_expired_crl.pem" },
{ NULL, NULL }, /* Sentinel */
};

struct sss_test_conf_param pam_params[] = {
{ CONFDB_PAM_P11_URI, NULL },
{ NULL, NULL }, /* Sentinel */
};

ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb);
assert_int_equal(ret, EOK);

ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb);
assert_int_equal(ret, EOK);

set_cert_auth_param(pam_test_ctx->pctx, CA_DB);

mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
NULL, test_lookup_by_cert_cb, SSSD_TEST_CERT_0001);

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);
}

/* With one invalid CRL file at the beginning of the list
* no certificate should be returned */
void test_pam_preauth_invalid_crl_file_begin(void **state)
{
int ret;

struct sss_test_conf_param monitor_params[] = {
{ "certificate_verification",
"crl_file=" ABS_BUILD_DIR "/src/tests/test_ECC_CA/SSSD_test_ECC_crl.pem,"
"crl_file=" ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA_expired_crl.pem" },
{ NULL, NULL }, /* Sentinel */
};

struct sss_test_conf_param pam_params[] = {
{ CONFDB_PAM_P11_URI, NULL },
{ NULL, NULL }, /* Sentinel */
};

ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb);
assert_int_equal(ret, EOK);

ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb);
assert_int_equal(ret, EOK);

set_cert_auth_param(pam_test_ctx->pctx, CA_DB);

mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL);

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_simple_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);
}

/* With one invalid CRL file at the end of the list
* no certificate should be returned */
void test_pam_preauth_invalid_crl_file_end(void **state)
{
int ret;

struct sss_test_conf_param monitor_params[] = {
{ "certificate_verification",
"crl_file=" ABS_BUILD_DIR "/src/tests/test_CA/SSSD_test_CA_expired_crl.pem,"
"crl_file=" ABS_BUILD_DIR "/src/tests/test_ECC_CA/SSSD_test_ECC_crl.pem" },
{ NULL, NULL }, /* Sentinel */
};

struct sss_test_conf_param pam_params[] = {
{ CONFDB_PAM_P11_URI, NULL },
{ NULL, NULL }, /* Sentinel */
};

ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb);
assert_int_equal(ret, EOK);

ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb);
assert_int_equal(ret, EOK);

set_cert_auth_param(pam_test_ctx->pctx, CA_DB);

mock_input_pam_cert(pam_test_ctx, "pamuser", NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL);

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_simple_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)
{
int ret;
Expand Down Expand Up @@ -3737,13 +3862,19 @@ int main(int argc, const char *argv[])
pam_test_setup, pam_test_teardown),
cmocka_unit_test_setup_teardown(test_pam_preauth_expired_crl_file_soft,
pam_test_setup, pam_test_teardown),
cmocka_unit_test_setup_teardown(test_pam_preauth_valid_crl_files,
pam_test_setup, pam_test_teardown),
#endif /* HAVE_FAKETIME */
cmocka_unit_test_setup_teardown(test_pam_preauth_ocsp,
pam_test_setup, pam_test_teardown),
cmocka_unit_test_setup_teardown(test_pam_preauth_ocsp_no_ocsp,
pam_test_setup, pam_test_teardown),
cmocka_unit_test_setup_teardown(test_pam_preauth_ocsp_soft_ocsp,
pam_test_setup, pam_test_teardown),
cmocka_unit_test_setup_teardown(test_pam_preauth_invalid_crl_file_begin,
pam_test_setup, pam_test_teardown),
cmocka_unit_test_setup_teardown(test_pam_preauth_invalid_crl_file_end,
pam_test_setup, pam_test_teardown),
#endif /* HAVE_TEST_CA */

cmocka_unit_test_setup_teardown(test_filter_response,
Expand Down
29 changes: 21 additions & 8 deletions src/tests/cmocka/test_utils.c
Expand Up @@ -1764,7 +1764,7 @@ static void test_parse_cert_verify_opts(void **state)
assert_true(cv_opts->do_ocsp);
assert_null(cv_opts->ocsp_default_responder);
assert_null(cv_opts->ocsp_default_responder_signing_cert);
assert_null(cv_opts->crl_file);
assert_null(cv_opts->crl_files);
talloc_free(cv_opts);

ret = parse_cert_verify_opts(global_talloc_context, "wedfkwefjk", &cv_opts);
Expand All @@ -1774,7 +1774,7 @@ static void test_parse_cert_verify_opts(void **state)
assert_true(cv_opts->do_ocsp);
assert_null(cv_opts->ocsp_default_responder);
assert_null(cv_opts->ocsp_default_responder_signing_cert);
assert_null(cv_opts->crl_file);
assert_null(cv_opts->crl_files);
talloc_free(cv_opts);

ret = parse_cert_verify_opts(global_talloc_context, "no_ocsp", &cv_opts);
Expand All @@ -1784,7 +1784,7 @@ static void test_parse_cert_verify_opts(void **state)
assert_false(cv_opts->do_ocsp);
assert_null(cv_opts->ocsp_default_responder);
assert_null(cv_opts->ocsp_default_responder_signing_cert);
assert_null(cv_opts->crl_file);
assert_null(cv_opts->crl_files);
talloc_free(cv_opts);

ret = parse_cert_verify_opts(global_talloc_context, "no_verification",
Expand All @@ -1795,7 +1795,7 @@ static void test_parse_cert_verify_opts(void **state)
assert_true(cv_opts->do_ocsp);
assert_null(cv_opts->ocsp_default_responder);
assert_null(cv_opts->ocsp_default_responder_signing_cert);
assert_null(cv_opts->crl_file);
assert_null(cv_opts->crl_files);
talloc_free(cv_opts);

ret = parse_cert_verify_opts(global_talloc_context,
Expand All @@ -1806,7 +1806,7 @@ static void test_parse_cert_verify_opts(void **state)
assert_false(cv_opts->do_ocsp);
assert_null(cv_opts->ocsp_default_responder);
assert_null(cv_opts->ocsp_default_responder_signing_cert);
assert_null(cv_opts->crl_file);
assert_null(cv_opts->crl_files);
talloc_free(cv_opts);

ret = parse_cert_verify_opts(global_talloc_context,
Expand All @@ -1828,7 +1828,7 @@ static void test_parse_cert_verify_opts(void **state)
assert_true(cv_opts->do_ocsp);
assert_string_equal(cv_opts->ocsp_default_responder, "abc");
assert_string_equal(cv_opts->ocsp_default_responder_signing_cert, "def");
assert_null(cv_opts->crl_file);
assert_null(cv_opts->crl_files);
talloc_free(cv_opts);

ret = parse_cert_verify_opts(global_talloc_context, "crl_file=hij",
Expand All @@ -1839,7 +1839,20 @@ static void test_parse_cert_verify_opts(void **state)
assert_true(cv_opts->do_ocsp);
assert_null(cv_opts->ocsp_default_responder);
assert_null(cv_opts->ocsp_default_responder_signing_cert);
assert_string_equal(cv_opts->crl_file, "hij");
assert_string_equal(cv_opts->crl_files[0], "hij");
talloc_free(cv_opts);

ret = parse_cert_verify_opts(global_talloc_context,
"crl_file=file1.pem,crl_file=file2.pem",
&cv_opts);
assert_int_equal(ret, EOK);
assert_true(cv_opts->do_verification);
assert_false(cv_opts->verification_partial_chain);
assert_true(cv_opts->do_ocsp);
assert_null(cv_opts->ocsp_default_responder);
assert_null(cv_opts->ocsp_default_responder_signing_cert);
assert_string_equal(cv_opts->crl_files[0], "file1.pem");
assert_string_equal(cv_opts->crl_files[1], "file2.pem");
talloc_free(cv_opts);

ret = parse_cert_verify_opts(global_talloc_context, "partial_chain", &cv_opts);
Expand All @@ -1849,7 +1862,7 @@ static void test_parse_cert_verify_opts(void **state)
assert_true(cv_opts->do_ocsp);
assert_null(cv_opts->ocsp_default_responder);
assert_null(cv_opts->ocsp_default_responder_signing_cert);
assert_null(cv_opts->crl_file);
assert_null(cv_opts->crl_files);
talloc_free(cv_opts);
}

Expand Down
7 changes: 6 additions & 1 deletion src/tests/test_CA/Makefile.am
Expand Up @@ -30,14 +30,15 @@ pkcs12 = $(addprefix SSSD_test_cert_pkcs12_,$(addsuffix .pem,$(ids)))


extra = softhsm2_none softhsm2_one softhsm2_two softhsm2_2tokens softhsm2_ocsp softhsm2_2certs_same_id softhsm2_pss_one SSSD_test_cert_x509_0001.der SSSD_test_cert_x509_0007.der
extra += SSSD_test_CA_crl.pem
if HAVE_FAKETIME
extra += SSSD_test_CA_expired_crl.pem
endif

# If openssl is run in parallel there might be conflicts with the serial
.NOTPARALLEL:

ca_all: clean serial SSSD_test_CA.pem $(certs) $(certs_h) $(pubkeys) $(pubkeys_h) $(pkcs12) $(extra)
ca_all: clean serial SSSD_test_CA.pem SSSD_test_CA_crl.pem $(certs) $(certs_h) $(pubkeys) $(pubkeys_h) $(pkcs12) $(extra)

$(pwdfile):
@echo "123456" > $@
Expand Down Expand Up @@ -97,6 +98,9 @@ SSSD_test_cert_pubsshkey_%.h: SSSD_test_cert_pubsshkey_%.pub
SSSD_test_CA_expired_crl.pem:
$(FAKETIME) -f '-7d' $(OPENSSL) ca -gencrl -out $@ -keyfile $(openssl_ca_key) -config ${openssl_ca_config} -crlhours 1

SSSD_test_CA_crl.pem: $(openssl_ca_key) SSSD_test_CA.pem
$(OPENSSL) ca -gencrl -out $@ -keyfile $(openssl_ca_key) -config $(openssl_ca_config) -crldays 99999

# The softhsm2 PKCS#11 setups are used in
# - src/tests/cmocka/test_pam_srv.c
softhsm2_none: softhsm2_none.conf
Expand Down Expand Up @@ -206,6 +210,7 @@ CLEANFILES = \
index.txt.attr.old index.txt.old \
serial serial.old \
SSSD_test_CA.pem $(pwdfile) SSSD_test_CA_expired_crl.pem \
SSSD_test_CA_crl.pem \
$(certs) $(certs_h) $(pubkeys) $(pubkeys_h) $(pkcs12) \
softhsm2_*.conf \
SSSD_test_*.der \
Expand Down

0 comments on commit 872dd70

Please sign in to comment.