From c7bbf934a55016238614209216a6b9fc294718d4 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Tue, 12 Apr 2016 18:14:08 +0200 Subject: [PATCH 3/3] p11: add OCSP default responder options --- src/man/sssd.conf.5.xml | 28 +++++++++++++++ src/p11_child/p11_child_nss.c | 33 ++++++++++++++++++ src/tests/cmocka/test_utils.c | 80 +++++++++++++++++++++++++++++++++++++++++++ src/util/cert/nss/cert.c | 43 +++++++++++++++++++++-- src/util/util.c | 60 +++++++++++++++++++++++++++++++- src/util/util.h | 2 ++ 6 files changed, 242 insertions(+), 4 deletions(-) diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml index 5396a490a0b8793bb55e510896c57758d1f3fdb0..6cff0dc87c8eff21a0ea6405d741f33b8f7ecd65 100644 --- a/src/man/sssd.conf.5.xml +++ b/src/man/sssd.conf.5.xml @@ -391,6 +391,34 @@ testing. + + ocsp_default_responder=URL + + Sets the OCSP default responder + which should be used instead of the one + mentioned in the certificate. URL must + be replaced with the URL of the OCSP + default responder e.g. + http://example.com:80/ocsp. + This option must be used together + with + ocsp_default_responder_signing_cert. + + + + + + ocsp_default_responder_signing_cert=NAME + + The nickname of the cert to trust + (expected) to sign the OCSP responses. + The certificate with the given nickname + must be availble in the systems NSS + database. + This option must be used together + with ocsp_default_responder. + + diff --git a/src/p11_child/p11_child_nss.c b/src/p11_child/p11_child_nss.c index be3f33981e4540185d189f3d05e22fd598562f19..feae884360025b33c8090460e9e0a1f916365ab7 100644 --- a/src/p11_child/p11_child_nss.c +++ b/src/p11_child/p11_child_nss.c @@ -271,6 +271,27 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in, PR_GetError()); return EIO; } + + if (cert_verify_opts->ocsp_default_responder != NULL + && cert_verify_opts->ocsp_default_responder_signing_cert != NULL) { + rv = CERT_SetOCSPDefaultResponder(handle, + cert_verify_opts->ocsp_default_responder, + cert_verify_opts->ocsp_default_responder_signing_cert); + if (rv != SECSuccess) { + DEBUG(SSSDBG_OP_FAILURE, + "CERT_SetOCSPDefaultResponder failed: [%d].\n", + PR_GetError()); + return EIO; + } + + rv = CERT_EnableOCSPDefaultResponder(handle); + if (rv != SECSuccess) { + DEBUG(SSSDBG_OP_FAILURE, + "CERT_EnableOCSPDefaultResponder failed: [%d].\n", + PR_GetError()); + return EIO; + } + } } found_cert = NULL; @@ -309,6 +330,18 @@ int do_work(TALLOC_CTX *mem_ctx, const char *nss_db, const char *slot_name_in, } } + /* Disable OCSP default responder so that NSS can shutdown properly */ + if (cert_verify_opts->do_ocsp + && cert_verify_opts->ocsp_default_responder != NULL + && cert_verify_opts->ocsp_default_responder_signing_cert != NULL) { + rv = CERT_DisableOCSPDefaultResponder(handle); + if (rv != SECSuccess) { + DEBUG(SSSDBG_OP_FAILURE, + "CERT_DisableOCSPDefaultResponder failed: [%d].\n", + PR_GetError()); + } + } + if (found_cert == NULL) { DEBUG(SSSDBG_TRACE_ALL, "No certificate found.\n"); *cert = NULL; diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c index 3aea17f3610d436ea0ec8f58ec8b01000bc07d5c..aaba2df6d06d396bc88d7fa23edc8840eebeddb6 100644 --- a/src/tests/cmocka/test_utils.c +++ b/src/tests/cmocka/test_utils.c @@ -1487,6 +1487,83 @@ static void test_sss_unique_filename_destruct(void **state) sss_unique_filename_test(test_ctx, true); } +static void test_parse_cert_verify_opts(void **state) +{ + int ret; + struct cert_verify_opts *cv_opts; + + ret = parse_cert_verify_opts(global_talloc_context, NULL, &cv_opts); + assert_int_equal(ret, EOK); + assert_true(cv_opts->do_verification); + assert_true(cv_opts->do_ocsp); + assert_null(cv_opts->ocsp_default_responder); + assert_null(cv_opts->ocsp_default_responder_signing_cert); + talloc_free(cv_opts); + + ret = parse_cert_verify_opts(global_talloc_context, "wedfkwefjk", &cv_opts); + assert_int_equal(ret, EOK); + assert_true(cv_opts->do_verification); + assert_true(cv_opts->do_ocsp); + assert_null(cv_opts->ocsp_default_responder); + assert_null(cv_opts->ocsp_default_responder_signing_cert); + talloc_free(cv_opts); + + ret = parse_cert_verify_opts(global_talloc_context, "no_ocsp", &cv_opts); + assert_int_equal(ret, EOK); + assert_true(cv_opts->do_verification); + assert_false(cv_opts->do_ocsp); + assert_null(cv_opts->ocsp_default_responder); + assert_null(cv_opts->ocsp_default_responder_signing_cert); + talloc_free(cv_opts); + + ret = parse_cert_verify_opts(global_talloc_context, "no_verification", + &cv_opts); + assert_int_equal(ret, EOK); + assert_false(cv_opts->do_verification); + assert_true(cv_opts->do_ocsp); + assert_null(cv_opts->ocsp_default_responder); + assert_null(cv_opts->ocsp_default_responder_signing_cert); + talloc_free(cv_opts); + + ret = parse_cert_verify_opts(global_talloc_context, + "no_ocsp,no_verification", &cv_opts); + assert_int_equal(ret, EOK); + assert_false(cv_opts->do_verification); + assert_false(cv_opts->do_ocsp); + assert_null(cv_opts->ocsp_default_responder); + assert_null(cv_opts->ocsp_default_responder_signing_cert); + talloc_free(cv_opts); + + ret = parse_cert_verify_opts(global_talloc_context, + "ocsp_default_responder=", &cv_opts); + assert_int_equal(ret, EINVAL); + + ret = parse_cert_verify_opts(global_talloc_context, + "ocsp_default_responder_signing_cert=", + &cv_opts); + assert_int_equal(ret, EINVAL); + + ret = parse_cert_verify_opts(global_talloc_context, + "ocsp_default_responder=abc", &cv_opts); + assert_int_equal(ret, EINVAL); + + ret = parse_cert_verify_opts(global_talloc_context, + "ocsp_default_responder_signing_cert=def", + &cv_opts); + assert_int_equal(ret, EINVAL); + + ret = parse_cert_verify_opts(global_talloc_context, + "ocsp_default_responder=abc," + "ocsp_default_responder_signing_cert=def", + &cv_opts); + assert_int_equal(ret, EOK); + assert_true(cv_opts->do_verification); + 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"); + talloc_free(cv_opts); +} + int main(int argc, const char *argv[]) { poptContext pc; @@ -1566,6 +1643,9 @@ int main(int argc, const char *argv[]) cmocka_unit_test_setup_teardown(test_sss_unique_filename_destruct, unique_file_test_setup, unique_file_test_teardown), + cmocka_unit_test_setup_teardown(test_parse_cert_verify_opts, + setup_add_strings_lists, + teardown_add_strings_lists), }; /* Set debug level to invalid value so we can deside if -d 0 was used. */ diff --git a/src/util/cert/nss/cert.c b/src/util/cert/nss/cert.c index 9c1c965dd29d118092a264d315b392f74d905142..7bf9a8bfcb5d8da36698c50b8255ed50ca8b67cb 100644 --- a/src/util/cert/nss/cert.c +++ b/src/util/cert/nss/cert.c @@ -238,6 +238,7 @@ errno_t cert_to_ssh_key(TALLOC_CTX *mem_ctx, const char *ca_db, NSSInitParameters parameters = { 0 }; parameters.length = sizeof (parameters); SECStatus rv; + SECStatus rv_verify; if (der_blob == NULL || der_size == 0) { return EINVAL; @@ -266,6 +267,27 @@ errno_t cert_to_ssh_key(TALLOC_CTX *mem_ctx, const char *ca_db, PR_GetError()); return EIO; } + + if (cert_verify_opts->ocsp_default_responder != NULL + && cert_verify_opts->ocsp_default_responder_signing_cert != NULL) { + rv = CERT_SetOCSPDefaultResponder(handle, + cert_verify_opts->ocsp_default_responder, + cert_verify_opts->ocsp_default_responder_signing_cert); + if (rv != SECSuccess) { + DEBUG(SSSDBG_OP_FAILURE, + "CERT_SetOCSPDefaultResponder failed: [%d].\n", + PR_GetError()); + return EIO; + } + + rv = CERT_EnableOCSPDefaultResponder(handle); + if (rv != SECSuccess) { + DEBUG(SSSDBG_OP_FAILURE, + "CERT_EnableOCSPDefaultResponder failed: [%d].\n", + PR_GetError()); + return EIO; + } + } } der_item.len = der_size; @@ -279,9 +301,24 @@ errno_t cert_to_ssh_key(TALLOC_CTX *mem_ctx, const char *ca_db, } if (cert_verify_opts->do_verification) { - rv = CERT_VerifyCertificateNow(handle, cert, PR_TRUE, - certificateUsageSSLClient, NULL, NULL); - if (rv != SECSuccess) { + rv_verify = CERT_VerifyCertificateNow(handle, cert, PR_TRUE, + certificateUsageSSLClient, + NULL, NULL); + + /* Disable OCSP default responder so that NSS can shutdown properly */ + if (cert_verify_opts->do_ocsp + && cert_verify_opts->ocsp_default_responder != NULL + && cert_verify_opts->ocsp_default_responder_signing_cert + != NULL) { + rv = CERT_DisableOCSPDefaultResponder(handle); + if (rv != SECSuccess) { + DEBUG(SSSDBG_OP_FAILURE, + "CERT_DisableOCSPDefaultResponder failed: [%d].\n", + PR_GetError()); + } + } + + if (rv_verify != SECSuccess) { DEBUG(SSSDBG_CRIT_FAILURE, "CERT_VerifyCertificateNow failed [%d].\n", PR_GetError()); ret = EACCES; diff --git a/src/util/util.c b/src/util/util.c index 2449a0ff3a961e33a4397ce27bd441139bffb566..c1024c9739f1ed99d7224411592d0fd82f7ef5e9 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -1111,7 +1111,7 @@ static struct cert_verify_opts *init_cert_verify_opts(TALLOC_CTX *mem_ctx) { struct cert_verify_opts *cert_verify_opts; - cert_verify_opts = talloc(mem_ctx, struct cert_verify_opts); + cert_verify_opts = talloc_zero(mem_ctx, struct cert_verify_opts); if (cert_verify_opts == NULL) { DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); return NULL; @@ -1119,10 +1119,20 @@ static struct cert_verify_opts *init_cert_verify_opts(TALLOC_CTX *mem_ctx) cert_verify_opts->do_ocsp = true; cert_verify_opts->do_verification = true; + cert_verify_opts->ocsp_default_responder = NULL; + cert_verify_opts->ocsp_default_responder_signing_cert = NULL; return cert_verify_opts; } +#define OCSP_DEFAUL_RESPONDER "ocsp_default_responder=" +#define OCSP_DEFAUL_RESPONDER_LEN (sizeof(OCSP_DEFAUL_RESPONDER) - 1) + +#define OCSP_DEFAUL_RESPONDER_SIGNING_CERT \ + "ocsp_default_responder_signing_cert=" +#define OCSP_DEFAUL_RESPONDER_SIGNING_CERT_LEN \ + (sizeof(OCSP_DEFAUL_RESPONDER_SIGNING_CERT) - 1) + errno_t parse_cert_verify_opts(TALLOC_CTX *mem_ctx, const char *verify_opts, struct cert_verify_opts **_cert_verify_opts) { @@ -1168,6 +1178,41 @@ errno_t parse_cert_verify_opts(TALLOC_CTX *mem_ctx, const char *verify_opts, "disabling verification completely. " "This should not be used in production.\n"); cert_verify_opts->do_verification = false; + } else if (strncasecmp(opts[c], OCSP_DEFAUL_RESPONDER, + OCSP_DEFAUL_RESPONDER_LEN) == 0) { + cert_verify_opts->ocsp_default_responder = + talloc_strdup(cert_verify_opts, + &opts[c][OCSP_DEFAUL_RESPONDER_LEN]); + if (cert_verify_opts->ocsp_default_responder == NULL + || *cert_verify_opts->ocsp_default_responder == '\0') { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to parse ocsp_default_responder option [%s].\n", + opts[c]); + ret = EINVAL; + goto done; + } + + DEBUG(SSSDBG_TRACE_ALL, "Using OCSP default responder [%s]\n", + cert_verify_opts->ocsp_default_responder); + } else if (strncasecmp(opts[c], + OCSP_DEFAUL_RESPONDER_SIGNING_CERT, + OCSP_DEFAUL_RESPONDER_SIGNING_CERT_LEN) == 0) { + cert_verify_opts->ocsp_default_responder_signing_cert = + talloc_strdup(cert_verify_opts, + &opts[c][OCSP_DEFAUL_RESPONDER_SIGNING_CERT_LEN]); + if (cert_verify_opts->ocsp_default_responder_signing_cert == NULL + || *cert_verify_opts->ocsp_default_responder_signing_cert + == '\0') { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to parse ocsp_default_responder_signing_cert " + "option [%s].\n", opts[c]); + ret = EINVAL; + goto done; + } + + DEBUG(SSSDBG_TRACE_ALL, "Using OCSP default responder " + "signing cert nickname [%s]\n", + cert_verify_opts->ocsp_default_responder_signing_cert); } else { DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported certificate verification option [%s], " \ @@ -1175,6 +1220,19 @@ errno_t parse_cert_verify_opts(TALLOC_CTX *mem_ctx, const char *verify_opts, } } + if ((cert_verify_opts->ocsp_default_responder == NULL + && cert_verify_opts->ocsp_default_responder_signing_cert != NULL) + || (cert_verify_opts->ocsp_default_responder != NULL + && cert_verify_opts->ocsp_default_responder_signing_cert == NULL)) { + + DEBUG(SSSDBG_CRIT_FAILURE, + "ocsp_default_responder and ocsp_default_responder_signing_cert " + "must be used together.\n"); + + ret = EINVAL; + goto done; + } + ret = EOK; done: diff --git a/src/util/util.h b/src/util/util.h index 83738c973a5ac8ae7ea490d993df4ccff5f2d5a9..ed7274a6a3278431ad8469773c443cd6c4427ffe 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -316,6 +316,8 @@ char **parse_args(const char *str); struct cert_verify_opts { bool do_ocsp; bool do_verification; + char *ocsp_default_responder; + char *ocsp_default_responder_signing_cert; }; errno_t parse_cert_verify_opts(TALLOC_CTX *mem_ctx, const char *verify_opts, -- 2.1.0