URL: https://github.com/SSSD/sssd/pull/5558
Author: 3v1n0
 Title: #5558: p11_child: Add partial verification support
Action: opened

PR body:
"""
From the main commit:

<blockquote>
As per the switch to libcrypto by default, the CA certificates DB needs
to contain the whole certificates key-chain in order to verify a leaf
certificate. This means that if an intermediate CA authority signed a
leaf certificate the CA DB we provide to SSSD needs to contain the whole
key-chain, up to the root CA cert in order to verify the leaf one.

Now, while this is indeed more secure, it may break previous
configurations that were based on an NSS database that contained only
trusted intermediate CA certificates.

To allow such setups to continue working (once the NSS db is migrated)
we need to permit a "weaker" setup where an x509 certificate is verified
when the CA database we test against contains only the intermediate CA
certificate that was used to sign it.

As per this, support `partial_chain` value to be used as
`certification_verification` parameter that will add the
`X509_V_FLAG_PARTIAL_CHAIN` verify param flag to the store, as the
openssl's verify `-partial-chain` parameter works.

This setup can still be considered secure as it's still needed to have
configured the SSSD ca db to contain the trusted certs.

Add tests to check that we can verify a leaf certificate against its
parent (only) when using such option.
</blockquote>

In particular in Ubuntu we [switched to use libcrypto by default in our current 
LTS](https://bugs.launchpad.net/ubuntu/focal/+source/sssd/+bug/1905790), even 
if we never supported properly the usage of NSS system DB, it was possible to 
setup one and so we did a [simple migration 
tool](https://github.com/3v1n0/nss-database-pem-exporter) to export all the 
trusted NSS certificates to the SSSD's `ca_db`.

However, there are still some custom setups in which [may break when using 
openssl based 
implementation](https://bugs.launchpad.net/ubuntu/+source/sssd/+bug/1919563), 
because their NSS db was only containing the issuing CA certificates (and no 
their parent certs) and so it's not possible to verify their certificate.

So using `pam_cert_verification = partial_chain` on upgrades (only and only if 
migrated) we can ensure that no such system will be broken.
"""

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/5558/head:pr5558
git checkout pr5558
From e611a0a6155bc2af6e8409a295dbe6a1ff2a3dd8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= <m...@3v1n0.net>
Date: Sun, 28 Mar 2021 05:00:08 +0200
Subject: [PATCH 1/4] test_pam_srv: Add test for CA certificate check using
 intermediate CA

Since the switch to libcrypto as security backend SSSD enforces that all
the CAs in the key chain must be trusted, so add a test that ensures
that this is true and that an intermediate certificate doesn't verify a
leaf one if we're missing the whole chain.

To build the certificates we use the test_CA main certificate
(SSSD_test_CA.pem) as the root CA authority while we create a new CA
intermediate certificate used to create new leaf certificates.
---
 Makefile.am                                   |  1 +
 configure.ac                                  |  1 +
 src/tests/cmocka/test_pam_srv.c               | 87 ++++++++++++++++
 src/tests/test_CA/Makefile.am                 |  4 +-
 src/tests/test_CA/SSSD_test_CA.config         |  6 ++
 src/tests/test_CA/intermediate_CA/Makefile.am | 98 +++++++++++++++++++
 .../SSSD_test_intermediate_CA.config          | 53 ++++++++++
 ...SSSD_test_intermediate_CA_cert_0001.config | 20 ++++
 ...SSD_test_intermediate_CA_cert_key_0001.pem | 52 ++++++++++
 .../SSSD_test_intermediate_CA_key.pem         | 52 ++++++++++
 10 files changed, 373 insertions(+), 1 deletion(-)
 create mode 100644 src/tests/test_CA/intermediate_CA/Makefile.am
 create mode 100644 src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA.config
 create mode 100644 src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_cert_0001.config
 create mode 100644 src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_cert_key_0001.pem
 create mode 100644 src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_key.pem

diff --git a/Makefile.am b/Makefile.am
index cae9eae838..20b7ace450 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5530,6 +5530,7 @@ test_CA: test_CA.stamp
 
 test_CA.stamp: $(srcdir)/src/tests/test_CA/* $(srcdir)/src/tests/test_ECC_CA/*
 	$(MAKE) -C src/tests/test_CA ca_all
+	$(MAKE) -C src/tests/test_CA/intermediate_CA ca_all
 	$(MAKE) -C src/tests/test_ECC_CA ca_all
 	touch $@
 
diff --git a/configure.ac b/configure.ac
index c591410fdb..ec58f0d99e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -527,6 +527,7 @@ AC_CONFIG_FILES([Makefile contrib/sssd.spec src/examples/rwtab src/doxy.config
                  src/sysv/sssd src/sysv/gentoo/sssd src/sysv/SUSE/sssd
                  po/Makefile.in src/man/Makefile src/tests/cwrap/Makefile
                  src/tests/intg/Makefile src/tests/test_CA/Makefile
+                 src/tests/test_CA/intermediate_CA/Makefile
                  src/tests/test_ECC_CA/Makefile
                  src/lib/ipa_hbac/ipa_hbac.pc src/lib/ipa_hbac/ipa_hbac.doxy
                  src/lib/idmap/sss_idmap.pc src/lib/idmap/sss_idmap.doxy
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
index d41f5e78a5..4fbd1e2c36 100644
--- a/src/tests/cmocka/test_pam_srv.c
+++ b/src/tests/cmocka/test_pam_srv.c
@@ -42,6 +42,7 @@
 #include "tests/test_CA/SSSD_test_cert_x509_0005.h"
 #include "tests/test_CA/SSSD_test_cert_x509_0006.h"
 #include "tests/test_CA/SSSD_test_cert_x509_0007.h"
+#include "tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_cert_x509_0001.h"
 #include "tests/test_ECC_CA/SSSD_test_ECC_cert_x509_0001.h"
 #else
 #define SSSD_TEST_CERT_0001 ""
@@ -49,6 +50,7 @@
 #define SSSD_TEST_CERT_0005 ""
 #define SSSD_TEST_CERT_0006 ""
 #define SSSD_TEST_CERT_0007 ""
+#define SSSD_TEST_INTERMEDIATE_CA_CERT_0001 ""
 #define SSSD_TEST_ECC_CERT_0001 ""
 #endif
 
@@ -59,6 +61,10 @@
 #define TEST_ID_PROVIDER "ldap"
 
 #define CA_DB ABS_BUILD_DIR"/src/tests/test_CA/SSSD_test_CA.pem"
+#define INTERMEDIATE_CA_DB \
+    ABS_BUILD_DIR"/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA.pem"
+#define INTERMEDIATE_FULL_CA_DB \
+    ABS_BUILD_DIR"/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_full_db.pem"
 #define ECC_CA_DB ABS_BUILD_DIR"/src/tests/test_ECC_CA/SSSD_test_ECC_CA.pem"
 
 #define TEST_TOKEN_NAME "SSSD Test Token"
@@ -2368,6 +2374,81 @@ void test_pam_ecc_cert_auth(void **state)
     assert_int_equal(ret, EOK);
 }
 
+void test_pam_intermediate_ca_cert_auth_with_full_certs(void **state)
+{
+    int ret;
+
+    putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/intermediate_CA/softhsm2_intermediate_one.conf"));
+
+    set_cert_auth_param(pam_test_ctx->pctx, INTERMEDIATE_FULL_CA_DB);
+
+    /* Here the last option must be set to true because the backend is only
+     * connected once. During authentication the backend is connected first to
+     * see if it can handle Smartcard authentication, but before that the user
+     * is looked up. Since the first mocked reply already adds the certificate
+     * to the user entry the lookup by certificate will already find the user
+     * in the cache and no second request to the backend is needed. */
+    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456",
+                        "SSSD Test intermediate CA Token",
+                        TEST_MODULE_NAME,
+                        "190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB",
+                        "SSSD test intermediate cert 0001", NULL,
+                        test_lookup_by_cert_cb,
+                        SSSD_TEST_INTERMEDIATE_CA_CERT_0001);
+
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+
+    /* Assume backend cannot handle Smartcard credentials */
+    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
+
+    set_cmd_cb(test_pam_simple_check_success);
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
+                          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_intermediate_ca_cert_auth_fails_with_incomplete_db(void **state)
+{
+    int ret;
+
+    putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/intermediate_CA/softhsm2_intermediate_one.conf"));
+
+    set_cert_auth_param(pam_test_ctx->pctx, INTERMEDIATE_CA_DB);
+
+    /* Here the last option must be set to true because the backend is only
+     * connected once. During authentication the backend is connected first to
+     * see if it can handle Smartcard authentication, but before that the user
+     * is looked up. Since the first mocked reply already adds the certificate
+     * to the user entry the lookup by certificate will already find the user
+     * in the cache and no second request to the backend is needed. */
+    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456",
+                        "SSSD Test intermediate CA Token",
+                        TEST_MODULE_NAME,
+                        "1234567890",
+                        NULL, NULL, NULL,
+                        SSSD_TEST_INTERMEDIATE_CA_CERT_0001);
+
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+
+    /* Assume backend cannot handle Smartcard credentials */
+    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
+
+    set_cmd_cb(test_pam_auth_err_check);
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
+                          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_auth_no_logon_name(void **state)
 {
     int ret;
@@ -3319,6 +3400,12 @@ int main(int argc, const char *argv[])
                                         pam_test_setup, pam_test_teardown),
         cmocka_unit_test_setup_teardown(test_pam_ecc_cert_auth,
                                         pam_test_setup, pam_test_teardown),
+        cmocka_unit_test_setup_teardown(
+                             test_pam_intermediate_ca_cert_auth_with_full_certs,
+                             pam_test_setup, pam_test_teardown),
+        cmocka_unit_test_setup_teardown(
+                    test_pam_intermediate_ca_cert_auth_fails_with_incomplete_db,
+                    pam_test_setup, 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,
diff --git a/src/tests/test_CA/Makefile.am b/src/tests/test_CA/Makefile.am
index f0bced6021..0a2d997195 100644
--- a/src/tests/test_CA/Makefile.am
+++ b/src/tests/test_CA/Makefile.am
@@ -192,8 +192,10 @@ clean-local:
 	rm -rf newcerts
 	rm -rf softhsm*
 
-serial: clean
+serial:
 	touch index.txt
 	touch index.txt.attr
 	mkdir newcerts
 	echo -n 01 > serial
+
+SUBDIRS = intermediate_CA
diff --git a/src/tests/test_CA/SSSD_test_CA.config b/src/tests/test_CA/SSSD_test_CA.config
index 90ae2233cc..7b485b45e5 100644
--- a/src/tests/test_CA/SSSD_test_CA.config
+++ b/src/tests/test_CA/SSSD_test_CA.config
@@ -31,6 +31,12 @@ authorityKeyIdentifier = keyid:always,issuer:always
 basicConstraints       = CA:true
 keyUsage               = critical, digitalSignature, cRLSign, keyCertSign
 
+[ v3_intermediate_ca ]
+subjectKeyIdentifier   = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+basicConstraints       = CA:true
+keyUsage               = critical, digitalSignature, cRLSign, keyCertSign
+
 [ policy_any ]
 organizationName       = supplied
 organizationalUnitName = supplied
diff --git a/src/tests/test_CA/intermediate_CA/Makefile.am b/src/tests/test_CA/intermediate_CA/Makefile.am
new file mode 100644
index 0000000000..a15bc01d44
--- /dev/null
+++ b/src/tests/test_CA/intermediate_CA/Makefile.am
@@ -0,0 +1,98 @@
+dist_noinst_DATA = \
+    SSSD_test_intermediate_CA.config \
+    SSSD_test_intermediate_CA_key.pem \
+    SSSD_test_intermediate_CA_cert_0001.config  \
+    SSSD_test_intermediate_CA_cert_key_0001.pem
+
+openssl_root_ca_config = $(abs_srcdir)/../SSSD_test_CA.config
+openssl_root_ca_key = $(abs_srcdir)/../SSSD_test_CA_key.pem
+openssl_intermediate_ca_config = $(srcdir)/SSSD_test_intermediate_CA.config
+openssl_intermediate_ca_key = $(abs_srcdir)/SSSD_test_intermediate_CA_key.pem
+pwdfile = pwdfile
+
+configs := $(notdir $(wildcard $(srcdir)/SSSD_test_intermediate_CA_cert_*.config))
+ids := $(subst SSSD_test_intermediate_CA_cert_,,$(basename $(configs)))
+certs = $(addprefix SSSD_test_intermediate_CA_cert_x509_,$(addsuffix .pem,$(ids)))
+certs_h = $(addprefix SSSD_test_intermediate_CA_cert_x509_,$(addsuffix .h,$(ids)))
+pubkeys = $(addprefix SSSD_test_intermediate_CA_cert_pubsshkey_,$(addsuffix .pub,$(ids)))
+pubkeys_h = $(addprefix SSSD_test_intermediate_CA_cert_pubsshkey_,$(addsuffix .h,$(ids)))
+pkcs12 = $(addprefix SSSD_test_intermediate_CA_cert_pkcs12_,$(addsuffix .pem,$(ids)))
+
+extra = softhsm2_intermediate_one
+
+# If openssl is run in parallel there might be conflicts with the serial
+.NOTPARALLEL:
+
+ca_all: clean SSSD_test_intermediate_CA.pem SSSD_test_intermediate_CA_full_db.pem $(certs) $(certs_h) $(pubkeys) $(pubkeys_h) $(pkcs12) $(extra)
+
+$(pwdfile):
+	@echo "123456" > $@
+
+SSSD_test_CA.pem:
+	$(MAKE) -C $(builddir)/.. SSSD_test_CA.pem
+	ln -s $(builddir)/../$@
+
+SSSD_test_intermediate_CA_req.pem: $(openssl_intermediate_ca_key) $(openssl_intermediate_ca_config) SSSD_test_CA.pem
+	$(OPENSSL) req -batch -config ${openssl_intermediate_ca_config} -new -nodes -key $< -sha256 -extensions v3_ca -out $@
+
+SSSD_test_intermediate_CA.pem: SSSD_test_intermediate_CA_req.pem $(openssl_root_ca_config) $(openssl_root_ca_key)
+	cd .. && $(OPENSSL) ca -config ${openssl_root_ca_config} -batch -notext -keyfile $(openssl_root_ca_key) -in $(abs_builddir)/$< -days 200 -extensions v3_intermediate_ca -out $(abs_builddir)/$@
+
+SSSD_test_intermediate_CA_full_db.pem: SSSD_test_CA.pem SSSD_test_intermediate_CA.pem
+	cat $^ > $@
+
+SSSD_test_intermediate_CA_cert_req_%.pem: $(srcdir)/SSSD_test_intermediate_CA_cert_key_%.pem $(srcdir)/SSSD_test_intermediate_CA_cert_%.config
+	$(OPENSSL) req -new -nodes -key $< -reqexts req_exts -config $(srcdir)/SSSD_test_intermediate_CA_cert_$*.config -out $@
+
+SSSD_test_intermediate_CA_cert_x509_%.pem: SSSD_test_intermediate_CA_cert_req_%.pem $(openssl_intermediate_ca_config) SSSD_test_intermediate_CA.pem serial
+	$(OPENSSL) ca -config ${openssl_intermediate_ca_config} -batch -notext -keyfile $(openssl_intermediate_ca_key) -in $< -days 200 -extensions usr_cert -out $@
+
+SSSD_test_intermediate_CA_cert_pkcs12_%.pem: SSSD_test_intermediate_CA_cert_x509_%.pem $(srcdir)/SSSD_test_intermediate_CA_cert_key_%.pem $(pwdfile)
+	$(OPENSSL) pkcs12 -export -in SSSD_test_intermediate_CA_cert_x509_$*.pem -inkey $(srcdir)/SSSD_test_intermediate_CA_cert_key_$*.pem -nodes -passout file:$(pwdfile) -out $@
+
+SSSD_test_intermediate_CA_cert_pubkey_%.pem: SSSD_test_intermediate_CA_cert_x509_%.pem
+	$(OPENSSL) x509 -in $< -pubkey -noout > $@
+
+SSSD_test_intermediate_CA_cert_pubsshkey_%.pub: SSSD_test_intermediate_CA_cert_pubkey_%.pem
+	$(SSH_KEYGEN) -i -m PKCS8 -f $< > $@
+
+SSSD_test_intermediate_CA_cert_x509_%.h: SSSD_test_intermediate_CA_cert_x509_%.pem
+	@echo "#define SSSD_TEST_INTERMEDIATE_CA_CERT_$* \""$(shell cat $< |openssl x509 -outform der | base64 -w 0)"\"" > $@
+
+SSSD_test_intermediate_CA_cert_pubsshkey_%.h: SSSD_test_intermediate_CA_cert_pubsshkey_%.pub
+	@echo "#define SSSD_TEST_INTERMEDIATE_CA_CERT_SSH_KEY_$* \""$(shell cut -d' ' -f2 $<)"\"" > $@
+
+
+softhsm2_intermediate_one: softhsm2_intermediate_one.conf
+	mkdir $@
+	SOFTHSM2_CONF=./$< $(SOFTHSM2_UTIL) --init-token  --label "SSSD Test intermediate CA Token" --pin 123456 --so-pin 123456 --free
+	GNUTLS_PIN=123456 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --no-mark-private --load-certificate=SSSD_test_intermediate_CA_cert_x509_0001.pem --login  --label 'SSSD test intermediate cert 0001' --id '190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB'
+	GNUTLS_PIN=123456 SOFTHSM2_CONF=./$< $(P11TOOL) --provider=$(SOFTHSM2_PATH) --write --load-privkey=$(srcdir)/SSSD_test_intermediate_CA_cert_key_0001.pem --login  --label 'SSSD test intermediate cert 0001' --id '190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB'
+
+softhsm2_intermediate_one.conf:
+	@echo "directories.tokendir = "$(abs_top_builddir)"/src/tests/test_CA/intermediate_CA/softhsm2_intermediate_one" > $@
+	@echo "objectstore.backend = file" >> $@
+	@echo "slots.removable = true" >> $@
+
+CLEANFILES = \
+    index.txt  index.txt.attr \
+    index.txt.attr.old  index.txt.old \
+    SSSD_test_intermediate_CA.pem \
+	SSSD_test_intermediate_CA_req.pem \
+	SSSD_test_CA_intermediates_full_certs.pem \
+	SSSD_test_CA.pem \
+	$(pwdfile) \
+    $(certs) $(certs_h) $(pubkeys) $(pubkeys_h) $(pkcs12) \
+    softhsm2_*.conf \
+    $(NULL)
+
+clean-local:
+	rm -rf newcerts
+	rm -rf softhsm*
+	rm -rf serial*
+
+serial:
+	mkdir -p newcerts
+	touch index.txt
+	touch index.txt.attr
+	echo -n 01 > serial
diff --git a/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA.config b/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA.config
new file mode 100644
index 0000000000..38033fc257
--- /dev/null
+++ b/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA.config
@@ -0,0 +1,53 @@
+[ ca ]
+default_ca = CA_default
+
+[ CA_default ]
+dir              = .
+database         = $dir/index.txt
+new_certs_dir    = $dir/newcerts
+
+certificate      = $dir/SSSD_test_intermediate_CA.pem
+serial           = $dir/serial
+private_key      = $dir/SSSD_test_intermediate_CA_key.pem
+RANDFILE         = $dir/rand
+
+default_days     = 365
+default_crl_days = 30
+default_md       = sha256
+
+policy           = policy_any
+email_in_dn      = no
+
+name_opt         = ca_default
+cert_opt         = ca_default
+copy_extensions  = copy
+
+[ usr_cert ]
+authorityKeyIdentifier = keyid, issuer
+
+[ v3_ca ]
+subjectKeyIdentifier   = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+basicConstraints       = CA:true
+keyUsage               = critical, digitalSignature, cRLSign, keyCertSign
+
+[ v3_intermediate_ca ]
+subjectKeyIdentifier   = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+basicConstraints       = CA:true
+keyUsage               = critical, digitalSignature, cRLSign, keyCertSign
+
+[ policy_any ]
+organizationName       = supplied
+organizationalUnitName = supplied
+commonName             = supplied
+emailAddress           = optional
+
+[ req ]
+distinguished_name = req_distinguished_name
+prompt             = no
+
+[ req_distinguished_name ]
+O  = SSSD
+OU = SSSD test
+CN = SSSD test intermediate CA
diff --git a/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_cert_0001.config b/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_cert_0001.config
new file mode 100644
index 0000000000..5797839f6d
--- /dev/null
+++ b/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_cert_0001.config
@@ -0,0 +1,20 @@
+# This certificate is used in
+# - src/tests/cmocka/test_cert_utils.c
+# - src/tests/cmocka/test_pam_srv.c
+[ req ]
+distinguished_name = req_distinguished_name
+prompt = no
+
+[ req_distinguished_name ]
+O = SSSD
+OU = SSSD test intermediate CA
+CN = SSSD test intermediate cert 0001
+
+[ req_exts ]
+basicConstraints = CA:FALSE
+nsCertType = client, email
+nsComment = "SSSD test intermediate CA trusted Certificate"
+subjectKeyIdentifier = hash
+keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = clientAuth, emailProtection
+subjectAltName = email:sssd-devel@lists.fedorahosted.org,URI:https://github.com/SSSD/sssd/
diff --git a/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_cert_key_0001.pem b/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_cert_key_0001.pem
new file mode 100644
index 0000000000..ca8a5a0ae4
--- /dev/null
+++ b/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_cert_key_0001.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+R3E0tlcatxgHoASu3vVk7OwL0QhTuEXz
+-----END PRIVATE KEY-----
diff --git a/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_key.pem b/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_key.pem
new file mode 100644
index 0000000000..a6a9b675f0
--- /dev/null
+++ b/src/tests/test_CA/intermediate_CA/SSSD_test_intermediate_CA_key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+91Ewk/mDtQkg086y4gCQIcaL04jE
+-----END PRIVATE KEY-----

From 44fae0f5584be956ed373673b218a7a465e2c58e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= <m...@3v1n0.net>
Date: Sun, 28 Mar 2021 20:16:26 +0200
Subject: [PATCH 2/4] p11_child_openssl: Free X509_VERIFY_PARAM if initialized

---
 src/p11_child/p11_child_openssl.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c
index 0e0c0a79e9..2b2fd3cc47 100644
--- a/src/p11_child/p11_child_openssl.c
+++ b/src/p11_child/p11_child_openssl.c
@@ -686,6 +686,10 @@ errno_t init_verification(struct p11_ctx *p11_ctx,
         X509_STORE_free(store);
     }
 
+    if (verify_param != NULL) {
+        X509_VERIFY_PARAM_free(verify_param);
+    }
+
     return ret;
 }
 

From 704ba297f67d42f5e980f4003528e04691fc7092 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= <m...@3v1n0.net>
Date: Sun, 28 Mar 2021 20:24:09 +0200
Subject: [PATCH 3/4] p11_child: Add support for 'partial_chain'
 certificate_verification option

As per the switch to libcrypto by default, the CA certificates DB needs
to contain the whole certificates key-chain in order to verify a leaf
certificate. This means that if an intermediate CA authority signed a
leaf certificate the CA DB we provide to SSSD needs to contain the whole
key-chain, up to the root CA cert in order to verify the leaf one.

Now, while this is indeed more secure, it may break previous
configurations that were based on an NSS database that contained only
trusted intermediate CA certificates.

To allow such setups to continue working (once the NSS db is migrated)
we need to permit a "weaker" setup where an x509 certificate is verified
when the CA database we test against contains only the intermediate CA
certificate that was used to sign it.

As per this, support `partial_chain` value to be used as
`certification_verification` parameter that will add the
`X509_V_FLAG_PARTIAL_CHAIN` verify param flag to the store, as the
openssl's verify `-partial-chain` parameter works.

This setup can still be considered secure as it's still needed to have
configured the SSSD ca db to contain the trusted certs.

Add tests to check that we can verify a leaf certificate against its
parent (only) when using such option.
---
 src/man/sssd.conf.5.xml                | 11 +++
 src/p11_child/p11_child.h              |  1 +
 src/p11_child/p11_child_common_utils.c |  8 +++
 src/p11_child/p11_child_openssl.c      | 37 ++++++++--
 src/tests/cmocka/test_pam_srv.c        | 97 ++++++++++++++++++++++++++
 src/tests/cmocka/test_utils.c          | 17 +++++
 6 files changed, 165 insertions(+), 6 deletions(-)

diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 42659dffe6..140caa1098 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -518,6 +518,17 @@
                                         testing.</para>
                                     </listitem>
                                 </varlistentry>
+                                <varlistentry>
+                                    <term>partial_chain</term>
+                                    <listitem>
+                                        <para>Allow verification to succeed even
+                                        if a <replaceable>complete</replaceable>
+                                        chain cannot be built to a self-signed
+                                        trust-anchor, provided it is possible to
+                                        construct a chain to a trusted certificate
+                                        that might not be self-signed.</para>
+                                    </listitem>
+                                </varlistentry>
                                 <varlistentry>
                                     <term>ocsp_default_responder=URL</term>
                                     <listitem>
diff --git a/src/p11_child/p11_child.h b/src/p11_child/p11_child.h
index 9c0cefe053..cbc5a9df95 100644
--- a/src/p11_child/p11_child.h
+++ b/src/p11_child/p11_child.h
@@ -36,6 +36,7 @@ struct p11_ctx;
 struct cert_verify_opts {
     bool do_ocsp;
     bool do_verification;
+    bool verification_partial_chain;
     char *ocsp_default_responder;
     char *ocsp_default_responder_signing_cert;
     char *crl_file;
diff --git a/src/p11_child/p11_child_common_utils.c b/src/p11_child/p11_child_common_utils.c
index c5f3246259..927606365f 100644
--- a/src/p11_child/p11_child_common_utils.c
+++ b/src/p11_child/p11_child_common_utils.c
@@ -40,6 +40,7 @@ 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->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;
@@ -111,6 +112,13 @@ errno_t parse_cert_verify_opts(TALLOC_CTX *mem_ctx, const char *verify_opts,
                     "Smart card certificate verification disabled completely. "
                     "This should not be used in production.");
             cert_verify_opts->do_verification = false;
+        } else if (strcasecmp(opts[c], "partial_chain") == 0) {
+            DEBUG(SSSDBG_TRACE_ALL,
+                  "Found 'partial_chain' option, verification will not fail if "
+                  "a complete chain cannot be built to a self-signed "
+                  "trust-anchor, provided it is possible to construct a chain "
+                  "to a trusted certificate that might not be self-signed.\n");
+            cert_verify_opts->verification_partial_chain = true;
         } else if (strncasecmp(opts[c], OCSP_DEFAUL_RESPONDER,
                                OCSP_DEFAUL_RESPONDER_LEN) == 0) {
             cert_verify_opts->ocsp_default_responder =
diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c
index 2b2fd3cc47..cee44df51c 100644
--- a/src/p11_child/p11_child_openssl.c
+++ b/src/p11_child/p11_child_openssl.c
@@ -617,6 +617,25 @@ static int talloc_free_x509_store(struct p11_ctx *p11_ctx)
     return 0;
 }
 
+static int ensure_verify_param(X509_VERIFY_PARAM **verify_param_out)
+{
+    if (verify_param_out == NULL) {
+        return EINVAL;
+    }
+
+    if (*verify_param_out != NULL) {
+        return EOK;
+    }
+
+    *verify_param_out = X509_VERIFY_PARAM_new();
+    if (*verify_param_out == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "X509_VERIFY_PARAM_new failed.\n");
+        return ENOMEM;
+    }
+
+    return EOK;
+}
+
 errno_t init_verification(struct p11_ctx *p11_ctx,
                           struct cert_verify_opts *cert_verify_opts)
 {
@@ -651,19 +670,21 @@ errno_t init_verification(struct p11_ctx *p11_ctx,
         goto done;
     }
 
+    if (cert_verify_opts->verification_partial_chain) {
+        if ((ret = ensure_verify_param (&verify_param)) != EOK) {
+            goto done;
+        }
+        X509_VERIFY_PARAM_set_flags(verify_param, X509_V_FLAG_PARTIAL_CHAIN);
+    }
+
     if (cert_verify_opts->crl_file != NULL) {
-        verify_param = X509_VERIFY_PARAM_new();
-        if (verify_param == NULL) {
-            DEBUG(SSSDBG_OP_FAILURE, "X509_VERIFY_PARAM_new failed.\n");
-            ret = ENOMEM;
+        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));
 
-        X509_STORE_set1_param(store, verify_param);
-
         ret = X509_load_crl_file(lookup, cert_verify_opts->crl_file,
                                  X509_FILETYPE_PEM);
         if (ret == 0) {
@@ -675,6 +696,10 @@ errno_t init_verification(struct p11_ctx *p11_ctx,
         }
     }
 
+    if (verify_param != NULL) {
+        X509_STORE_set1_param(store, verify_param);
+    }
+
     p11_ctx->x509_store = store;
     p11_ctx->cert_verify_opts = cert_verify_opts;
     talloc_set_destructor(p11_ctx, talloc_free_x509_store);
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
index 4fbd1e2c36..513572ff6b 100644
--- a/src/tests/cmocka/test_pam_srv.c
+++ b/src/tests/cmocka/test_pam_srv.c
@@ -2449,6 +2449,97 @@ void test_pam_intermediate_ca_cert_auth_fails_with_incomplete_db(void **state)
     assert_int_equal(ret, EOK);
 }
 
+void test_pam_intermediate_ca_cert_auth_with_partial_chain(void **state)
+{
+    int ret;
+
+    struct sss_test_conf_param monitor_params[] = {
+        { "certificate_verification", "partial_chain" },
+        { NULL, NULL }, /* Sentinel */
+    };
+
+    ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb);
+    assert_int_equal(ret, EOK);
+
+    putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/intermediate_CA/softhsm2_intermediate_one.conf"));
+
+    set_cert_auth_param(pam_test_ctx->pctx, INTERMEDIATE_CA_DB);
+
+    /* Here the last option must be set to true because the backend is only
+     * connected once. During authentication the backend is connected first to
+     * see if it can handle Smartcard authentication, but before that the user
+     * is looked up. Since the first mocked reply already adds the certificate
+     * to the user entry the lookup by certificate will already find the user
+     * in the cache and no second request to the backend is needed. */
+    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456",
+                        "SSSD Test intermediate CA Token",
+                        TEST_MODULE_NAME,
+                        "190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB",
+                        "SSSD test intermediate cert 0001", NULL,
+                        test_lookup_by_cert_cb,
+                        SSSD_TEST_INTERMEDIATE_CA_CERT_0001);
+
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+
+    /* Assume backend cannot handle Smartcard credentials */
+    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
+
+    set_cmd_cb(test_pam_simple_check_success);
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
+                          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_intermediate_ca_cert_auth_fails_with_root_and_partial_chain(void **state)
+{
+    int ret;
+
+    struct sss_test_conf_param monitor_params[] = {
+        { "certificate_verification", "partial_chain" },
+        { NULL, NULL }, /* Sentinel */
+    };
+
+    ret = add_monitor_params(monitor_params, pam_test_ctx->rctx->cdb);
+    assert_int_equal(ret, EOK);
+
+    putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/intermediate_CA/softhsm2_intermediate_one.conf"));
+
+    set_cert_auth_param(pam_test_ctx->pctx, CA_DB);
+
+    /* Here the last option must be set to true because the backend is only
+     * connected once. During authentication the backend is connected first to
+     * see if it can handle Smartcard authentication, but before that the user
+     * is looked up. Since the first mocked reply already adds the certificate
+     * to the user entry the lookup by certificate will already find the user
+     * in the cache and no second request to the backend is needed. */
+    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456",
+                        "SSSD Test intermediate CA Token",
+                        TEST_MODULE_NAME,
+                        "9876543210",
+                        NULL, NULL, NULL,
+                        SSSD_TEST_INTERMEDIATE_CA_CERT_0001);
+
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+
+    /* Assume backend cannot handle Smartcard credentials */
+    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
+
+    set_cmd_cb(test_pam_auth_err_check);
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
+                          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_auth_no_logon_name(void **state)
 {
     int ret;
@@ -3406,6 +3497,12 @@ int main(int argc, const char *argv[])
         cmocka_unit_test_setup_teardown(
                     test_pam_intermediate_ca_cert_auth_fails_with_incomplete_db,
                     pam_test_setup, pam_test_teardown),
+        cmocka_unit_test_setup_teardown(
+                          test_pam_intermediate_ca_cert_auth_with_partial_chain,
+                          pam_test_setup, pam_test_teardown),
+        cmocka_unit_test_setup_teardown(
+           test_pam_intermediate_ca_cert_auth_fails_with_root_and_partial_chain,
+           pam_test_setup, 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,
diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
index d258622fb5..120a2276de 100644
--- a/src/tests/cmocka/test_utils.c
+++ b/src/tests/cmocka/test_utils.c
@@ -1682,6 +1682,7 @@ static void test_parse_cert_verify_opts(void **state)
     ret = parse_cert_verify_opts(global_talloc_context, NULL, &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);
@@ -1691,6 +1692,7 @@ static void test_parse_cert_verify_opts(void **state)
     ret = parse_cert_verify_opts(global_talloc_context, "wedfkwefjk", &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);
@@ -1700,6 +1702,7 @@ static void test_parse_cert_verify_opts(void **state)
     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->verification_partial_chain);
     assert_false(cv_opts->do_ocsp);
     assert_null(cv_opts->ocsp_default_responder);
     assert_null(cv_opts->ocsp_default_responder_signing_cert);
@@ -1710,6 +1713,7 @@ static void test_parse_cert_verify_opts(void **state)
                                  &cv_opts);
     assert_int_equal(ret, EOK);
     assert_false(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);
@@ -1720,6 +1724,7 @@ static void test_parse_cert_verify_opts(void **state)
                                  "no_ocsp,no_verification", &cv_opts);
     assert_int_equal(ret, EOK);
     assert_false(cv_opts->do_verification);
+    assert_false(cv_opts->verification_partial_chain);
     assert_false(cv_opts->do_ocsp);
     assert_null(cv_opts->ocsp_default_responder);
     assert_null(cv_opts->ocsp_default_responder_signing_cert);
@@ -1741,6 +1746,7 @@ static void test_parse_cert_verify_opts(void **state)
                                  &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_string_equal(cv_opts->ocsp_default_responder, "abc");
     assert_string_equal(cv_opts->ocsp_default_responder_signing_cert, "def");
@@ -1751,11 +1757,22 @@ static void test_parse_cert_verify_opts(void **state)
                                  &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_file, "hij");
     talloc_free(cv_opts);
+
+    ret = parse_cert_verify_opts(global_talloc_context, "partial_chain", &cv_opts);
+    assert_int_equal(ret, EOK);
+    assert_true(cv_opts->do_verification);
+    assert_true(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_null(cv_opts->crl_file);
+    talloc_free(cv_opts);
 }
 
 static void assert_parse_fqname(const char *fqname,

From 6cd33901355462ae61c61a1c55b08842207e2da6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= <m...@3v1n0.net>
Date: Sun, 28 Mar 2021 21:52:05 +0200
Subject: [PATCH 4/4] pam: Add custom pam_cert_verification setting to override
 default

PAM uses by default the certificate_verification parameter, however it
we may want to set specific settings to be used for PAM auth only.

So add pam_cert_verification setting option that will be used to define
the verification options.

If this value is unset, we'll fallback to default.
---
 src/confdb/confdb.h                  |  1 +
 src/config/SSSDConfig/sssdoptions.py |  1 +
 src/config/cfg_rules.ini             |  1 +
 src/config/etc/sssd.api.conf         |  1 +
 src/man/sssd.conf.5.xml              | 25 ++++++++++++++
 src/responder/pam/pamsrv_cmd.c       | 20 +++++++++---
 src/tests/cmocka/test_pam_srv.c      | 49 ++++++++++++++++++++++++++++
 7 files changed, 94 insertions(+), 4 deletions(-)

diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index a2be227ddd..d5059c521c 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -138,6 +138,7 @@
 #define CONFDB_PAM_ACCOUNT_LOCKED_MESSAGE "pam_account_locked_message"
 #define CONFDB_PAM_CERT_AUTH "pam_cert_auth"
 #define CONFDB_PAM_CERT_DB_PATH "pam_cert_db_path"
+#define CONFDB_PAM_CERT_VERIFICATION "pam_cert_verification"
 #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"
diff --git a/src/config/SSSDConfig/sssdoptions.py b/src/config/SSSDConfig/sssdoptions.py
index 5d9946ba8f..03636fa261 100644
--- a/src/config/SSSDConfig/sssdoptions.py
+++ b/src/config/SSSDConfig/sssdoptions.py
@@ -98,6 +98,7 @@ def __init__(self):
         'pam_account_locked_message': _('Message printed when user account is locked.'),
         'pam_cert_auth': _('Allow certificate based/Smartcard authentication.'),
         'pam_cert_db_path': _('Path to certificate database with PKCS#11 modules.'),
+        'pam_cert_verification': _('Tune certificate verification for PAM authentication.'),
         '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'),
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
index bf2d03b824..c6ece4bd90 100644
--- a/src/config/cfg_rules.ini
+++ b/src/config/cfg_rules.ini
@@ -133,6 +133,7 @@ option = pam_account_expired_message
 option = pam_account_locked_message
 option = pam_cert_auth
 option = pam_cert_db_path
+option = pam_cert_verification
 option = p11_child_timeout
 option = pam_app_services
 option = pam_p11_allowed_services
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 49ced63859..7176fcb79d 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -74,6 +74,7 @@ pam_account_expired_message = str, None, false
 pam_account_locked_message = str, None, false
 pam_cert_auth = bool, None, false
 pam_cert_db_path = str, None, false
+pam_cert_verification = str, None, false
 p11_child_timeout = int, None, false
 pam_app_services = str, None, false
 pam_p11_allowed_services = str, None, false
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 140caa1098..b3aa927aaa 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -1550,6 +1550,31 @@ pam_account_locked_message = Account locked, please contact help desk.
                         </para>
                     </listitem>
                 </varlistentry>
+                <varlistentry>
+                    <term>pam_cert_verification (string)</term>
+                    <listitem>
+                        <para>
+                            With this parameter the PAM certificate verification
+                            can be tuned with a comma separated list of
+                            options that override the
+                            <quote>certificate_verification</quote> value in
+                            <quote>[sssd]</quote> section.
+                            Supported options are the same of
+                            <quote>certificate_verification</quote>.
+                        </para>
+                        <para>
+                            example:
+                            <programlisting>
+pam_cert_verification = partial_chain
+                            </programlisting>
+                        </para>
+                        <para>
+                            Default: not set, i.e. use default
+                            <quote>certificate_verification</quote> option defined
+                            in <quote>[sssd]</quote> section.
+                        </para>
+                    </listitem>
+                </varlistentry>
                 <varlistentry>
                     <term>p11_child_timeout (integer)</term>
                     <listitem>
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index c526f665b5..d052ca7528 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -1383,16 +1383,28 @@ static errno_t check_cert(TALLOC_CTX *mctx,
         p11_child_timeout += wait_for_card_timeout;
     }
 
-    ret = confdb_get_string(pctx->rctx->cdb, mctx, CONFDB_MONITOR_CONF_ENTRY,
-                            CONFDB_MONITOR_CERT_VERIFICATION, NULL,
-                            &cert_verification_opts);
+    ret = confdb_get_string(pctx->rctx->cdb, mctx, CONFDB_PAM_CONF_ENTRY,
+                            CONFDB_PAM_CERT_VERIFICATION,
+                            NULL, &cert_verification_opts);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE,
-              "Failed to read '"CONFDB_MONITOR_CERT_VERIFICATION"' from confdb: [%d]: %s\n",
+              "Failed to read '"CONFDB_PAM_CERT_VERIFICATION"' from confdb: [%d]: %s\n",
               ret, sss_strerror(ret));
         return ret;
     }
 
+    if (cert_verification_opts == NULL) {
+        ret = confdb_get_string(pctx->rctx->cdb, mctx, CONFDB_MONITOR_CONF_ENTRY,
+                                CONFDB_MONITOR_CERT_VERIFICATION, NULL,
+                                &cert_verification_opts);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                "Failed to read '"CONFDB_MONITOR_CERT_VERIFICATION"' from confdb: [%d]: %s\n",
+                ret, sss_strerror(ret));
+            return ret;
+        }
+    }
+
     ret = confdb_get_string(pctx->rctx->cdb, mctx, CONFDB_PAM_CONF_ENTRY,
                             CONFDB_PAM_P11_URI, NULL, &uri);
     if (ret != EOK) {
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
index 513572ff6b..62d5304c96 100644
--- a/src/tests/cmocka/test_pam_srv.c
+++ b/src/tests/cmocka/test_pam_srv.c
@@ -2540,6 +2540,52 @@ void test_pam_intermediate_ca_cert_auth_fails_with_root_and_partial_chain(void *
     assert_int_equal(ret, EOK);
 }
 
+void test_pam_intermediate_ca_cert_auth_with_partial_chain_pam_option(void **state)
+{
+    int ret;
+
+    struct sss_test_conf_param pam_params[] = {
+        { "pam_cert_verification", "no_ocsp, partial_chain" },
+        { NULL, NULL }, /* Sentinel */
+    };
+
+    ret = add_pam_params(pam_params, pam_test_ctx->rctx->cdb);
+    assert_int_equal(ret, EOK);
+
+    putenv(discard_const("SOFTHSM2_CONF=" ABS_BUILD_DIR "/src/tests/test_CA/intermediate_CA/softhsm2_intermediate_one.conf"));
+
+    set_cert_auth_param(pam_test_ctx->pctx, INTERMEDIATE_CA_DB);
+
+    /* Here the last option must be set to true because the backend is only
+     * connected once. During authentication the backend is connected first to
+     * see if it can handle Smartcard authentication, but before that the user
+     * is looked up. Since the first mocked reply already adds the certificate
+     * to the user entry the lookup by certificate will already find the user
+     * in the cache and no second request to the backend is needed. */
+    mock_input_pam_cert(pam_test_ctx, "pamuser", "123456",
+                        "SSSD Test intermediate CA Token",
+                        TEST_MODULE_NAME,
+                        "190E513C9A3DFAACDE5D2D0592F0FDFF559C10CB",
+                        "SSSD test intermediate cert 0001", NULL,
+                        test_lookup_by_cert_cb,
+                        SSSD_TEST_INTERMEDIATE_CA_CERT_0001);
+
+    will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+
+    /* Assume backend cannot handle Smartcard credentials */
+    pam_test_ctx->exp_pam_status = PAM_BAD_ITEM;
+
+    set_cmd_cb(test_pam_simple_check_success);
+    ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE,
+                          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_auth_no_logon_name(void **state)
 {
     int ret;
@@ -3503,6 +3549,9 @@ int main(int argc, const char *argv[])
         cmocka_unit_test_setup_teardown(
            test_pam_intermediate_ca_cert_auth_fails_with_root_and_partial_chain,
            pam_test_setup, pam_test_teardown),
+        cmocka_unit_test_setup_teardown(
+               test_pam_intermediate_ca_cert_auth_with_partial_chain_pam_option,
+               pam_test_setup, 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,
_______________________________________________
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

Reply via email to