summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am55
-rw-r--r--configure.ac1
-rw-r--r--contrib/sssd.spec.in32
-rw-r--r--src/lib/certmap/sss_cert_content_nss.c1014
-rw-r--r--src/lib/certmap/sss_certmap.c993
-rw-r--r--src/lib/certmap/sss_certmap.doxy.in3
-rw-r--r--src/lib/certmap/sss_certmap.exports13
-rw-r--r--src/lib/certmap/sss_certmap.h155
-rw-r--r--src/lib/certmap/sss_certmap.pc.in11
-rw-r--r--src/lib/certmap/sss_certmap_attr_names.c107
-rw-r--r--src/lib/certmap/sss_certmap_int.h187
-rw-r--r--src/lib/certmap/sss_certmap_krb5_match.c558
-rw-r--r--src/lib/certmap/sss_certmap_ldap_mapping.c367
-rw-r--r--src/man/Makefile.am2
-rw-r--r--src/man/po/po4a.cfg1
-rw-r--r--src/man/sss-certmap.5.xml600
-rw-r--r--src/tests/cmocka/test_certmap.c1443
-rw-r--r--src/tests/dlopen-tests.c1
18 files changed, 5542 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index 6dae4f2dd..8ca12c10d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -278,6 +278,7 @@ if HAVE_CMOCKA
simple-access-tests \
krb5_common_test \
test_iobuf \
+ sss_certmap_test \
$(NULL)
if HAVE_LIBRESOLV
@@ -1074,6 +1075,7 @@ SSSD_INTERNAL_LTLIBS = \
lib_LTLIBRARIES = libipa_hbac.la \
libsss_idmap.la \
libsss_nss_idmap.la \
+ libsss_certmap.la \
$(NULL)
pkgconfig_DATA += src/lib/ipa_hbac/ipa_hbac.pc
@@ -1128,6 +1130,7 @@ include_HEADERS = \
src/lib/ipa_hbac/ipa_hbac.h \
src/lib/idmap/sss_idmap.h \
src/sss_client/idmap/sss_nss_idmap.h \
+ src/lib/certmap/sss_certmap.h \
$(NULL)
if BUILD_LIBWBCLIENT
@@ -1712,6 +1715,38 @@ sssd_check_socket_activated_responders_LDADD = \
$(NULL)
endif
+if HAVE_NSS
+pkgconfig_DATA += src/lib/certmap/sss_certmap.pc
+libsss_certmap_la_DEPENDENCIES = src/lib/certmap/sss_certmap.exports
+libsss_certmap_la_SOURCES = \
+ src/lib/certmap/sss_certmap.c \
+ src/lib/certmap/sss_certmap_attr_names.c \
+ src/lib/certmap/sss_cert_content_nss.c \
+ src/lib/certmap/sss_certmap_krb5_match.c \
+ src/lib/certmap/sss_certmap_ldap_mapping.c \
+ src/util/util_ext.c \
+ src/util/cert/cert_common.c \
+ src/util/crypto/nss/nss_base64.c \
+ src/util/cert/nss/cert.c \
+ src/util/crypto/nss/nss_util.c \
+ $(NULL)
+libsss_certmap_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(TALLOC_CFLAGS) \
+ $(NSS_CFLAGS) \
+ $(NULL)
+libsss_certmap_la_LIBADD = \
+ $(TALLOC_LIBS) \
+ $(NSS_LIBS) \
+ $(NULL)
+libsss_certmap_la_LDFLAGS = \
+ -Wl,--version-script,$(srcdir)/src/lib/certmap/sss_certmap.exports \
+ -version-info 0:0:0
+
+dist_noinst_DATA += src/lib/certmap/sss_certmap.exports
+dist_noinst_HEADERS += src/lib/certmap/sss_certmap_int.h
+endif
+
#################
# Feature Tests #
#################
@@ -3245,6 +3280,25 @@ test_inotify_LDADD = \
libsss_test_common.la \
$(NULL)
+if HAVE_NSS
+sss_certmap_test_SOURCES = \
+ src/tests/cmocka/test_certmap.c \
+ src/lib/certmap/sss_certmap_attr_names.c \
+ $(NULL)
+sss_certmap_test_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(NSS_CFLAGS) \
+ $(NULL)
+sss_certmap_test_LDADD = \
+ $(CMOCKA_LIBS) \
+ $(POPT_LIBS) \
+ $(TALLOC_LIBS) \
+ $(NSS_LIBS) \
+ $(SSSD_INTERNAL_LTLIBS) \
+ libsss_test_common.la \
+ libsss_certmap.la \
+ $(NULL)
+endif
endif # HAVE_CMOCKA
noinst_PROGRAMS = pam_test_client
@@ -4404,6 +4458,7 @@ docs:
$(DOXYGEN) src/lib/ipa_hbac/ipa_hbac.doxy
$(DOXYGEN) src/lib/idmap/sss_idmap.doxy
$(DOXYGEN) src/sss_client/idmap/sss_nss_idmap.doxy
+ $(DOXYGEN) src/lib/certmap/sss_certmap.doxy
if BUILD_IFP
$(DOXYGEN) src/lib/sifp/sss_simpleifp.doxy
endif
diff --git a/configure.ac b/configure.ac
index e6a3b4e85..dd1012015 100644
--- a/configure.ac
+++ b/configure.ac
@@ -483,6 +483,7 @@ AC_CONFIG_FILES([Makefile contrib/sssd.spec src/examples/rwtab src/doxy.config
src/tests/intg/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
+ src/lib/certmap/sss_certmap.pc src/lib/certmap/sss_certmap.doxy
src/sss_client/idmap/sss_nss_idmap.pc
src/sss_client/idmap/sss_nss_idmap.doxy
src/sss_client/libwbclient/wbclient_sssd.pc
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 5bd2beb89..28ebe07a2 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -658,6 +658,25 @@ The libnfsidmap sssd module provides a way for rpc.idmapd to call SSSD to map
UIDs/GIDs to names and vice versa. It can be also used for mapping principal
(user) name to IDs(UID or GID) or to obtain groups which user are member of.
+%package -n libsss_certmap
+Summary: SSSD Certficate Mapping Library
+Group: Development/Libraries
+License: LGPLv3+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description -n libsss_certmap
+Library to map certificates to users based on rules
+
+%package -n libsss_certmap-devel
+Summary: SSSD Certficate Mapping Library
+Group: Development/Libraries
+License: LGPLv3+
+Requires: libsss_certmap = %{version}-%{release}
+
+%description -n libsss_certmap-devel
+Library to map certificates to users based on rules
+
%prep
%setup -q -n %{name}-%{version}
@@ -888,6 +907,7 @@ done
%{_datadir}/sssd/sssd.api.d
%{_mandir}/man1/sss_ssh_authorizedkeys.1*
%{_mandir}/man1/sss_ssh_knownhostsproxy.1*
+%{_mandir}/man5/sss-certmap.5*
%{_mandir}/man5/sssd.conf.5*
%{_mandir}/man5/sssd-simple.5*
%{_mandir}/man5/sssd-sudo.5*
@@ -1146,6 +1166,18 @@ done
%files nfs-idmap
%{_libdir}/libnfsidmap/sss.so
+%files -n libsss_certmap
+%defattr(-,root,root,-)
+%doc src/sss_client/COPYING src/sss_client/COPYING.LESSER
+%{_libdir}/libsss_certmap.so.*
+
+%files -n libsss_certmap-devel
+%defattr(-,root,root,-)
+%doc certmap_doc/html
+%{_includedir}/sss_certmap.h
+%{_libdir}/libsss_certmap.so
+%{_libdir}/pkgconfig/sss_certmap.pc
+
%pre common
getent group sssd >/dev/null || groupadd -r sssd
getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "User for sssd" sssd
diff --git a/src/lib/certmap/sss_cert_content_nss.c b/src/lib/certmap/sss_cert_content_nss.c
new file mode 100644
index 000000000..d31828954
--- /dev/null
+++ b/src/lib/certmap/sss_cert_content_nss.c
@@ -0,0 +1,1014 @@
+/*
+ SSSD - certificate handling utils - NSS version
+ The calls defined here should be useable outside of SSSD as well, e.g. in
+ libsss_certmap.
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2017
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <nss.h>
+#include <cert.h>
+#include <base64.h>
+#include <prerror.h>
+#include <secport.h>
+#include <secerr.h>
+#include <prprf.h>
+#include <prnetdb.h>
+#include <talloc.h>
+
+#include "util/crypto/sss_crypto.h"
+#include "util/crypto/nss/nss_util.h"
+#include "util/cert.h"
+#include "lib/certmap/sss_certmap.h"
+#include "lib/certmap/sss_certmap_int.h"
+
+
+/* The following two functions are copied from NSS's lib/certdb/secname.c
+ * becasue CERT_AddAVA is not exported. I just renamed it and made it static
+ * to avoid issues if the call gets exported some time in future. */
+
+static void **
+AddToArray(PLArenaPool *arena, void **array, void *element)
+{
+ unsigned count;
+ void **ap;
+
+ /* Count up number of slots already in use in the array */
+ count = 0;
+ ap = array;
+ if (ap) {
+ while (*ap++) {
+ count++;
+ }
+ }
+
+ if (array) {
+ array = (void**) PORT_ArenaGrow(arena, array,
+ (count + 1) * sizeof(void *),
+ (count + 2) * sizeof(void *));
+ } else {
+ array = (void**) PORT_ArenaAlloc(arena, (count + 2) * sizeof(void *));
+ }
+ if (array) {
+ array[count] = element;
+ array[count+1] = 0;
+ }
+ return array;
+}
+
+
+static SECStatus
+sss_CERT_AddAVA(PLArenaPool *arena, CERTRDN *rdn, CERTAVA *ava)
+{
+ rdn->avas = (CERTAVA**) AddToArray(arena, (void**) rdn->avas, ava);
+ return rdn->avas ? SECSuccess : SECFailure;
+}
+
+static SECItem *
+cert_get_ext_by_tag(CERTCertificate *cert, SECOidTag tag)
+{
+ SECOidData *oid;
+ int i;
+
+ oid = SECOID_FindOIDByTag(tag);
+ for (i = 0;
+ (cert->extensions != NULL) && (cert->extensions[i] != NULL);
+ i++)
+ if (SECITEM_ItemsAreEqual(&cert->extensions[i]->id, &oid->oid))
+ return &cert->extensions[i]->value;
+ return NULL;
+}
+
+static int get_extended_key_usage_oids(TALLOC_CTX *mem_ctx,
+ CERTCertificate *cert,
+ const char ***_oids)
+{
+ PLArenaPool *pool;
+ SECItem *ext;
+ SECItem **oids = NULL;
+ const char **oids_list = NULL;
+ size_t c;
+ SECStatus rv;
+ char *tmp_str;
+ int ret;
+
+ pool = PORT_NewArena(sizeof(double));
+ ext = cert_get_ext_by_tag(cert, SEC_OID_X509_EXT_KEY_USAGE);
+ if (ext != NULL) {
+ rv = SEC_ASN1DecodeItem(pool, &oids,
+ SEC_ASN1_GET(SEC_SequenceOfObjectIDTemplate),
+ ext);
+ if (rv != SECSuccess) {
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ for (c = 0; (oids != NULL && oids[c] != NULL); c++);
+ oids_list = talloc_zero_array(mem_ctx, const char *, c + 1);
+ if (oids_list == NULL) {
+ return ENOMEM;
+ }
+
+ for (c = 0; (oids != NULL && oids[c] != NULL); c++) {
+ tmp_str = CERT_GetOidString(oids[c]);
+ /* is it expexted that NSS OID strings start with "OID." but we
+ * prefer the plain dotted-decimal version so the prefix is skipped */
+ if (tmp_str == NULL || strncmp(tmp_str, "OID.", 4) != 0) {
+ PR_smprintf_free(tmp_str);
+ ret = EINVAL;
+ goto done;
+ }
+
+ oids_list[c] = talloc_strdup(oids_list, tmp_str + 4);
+ PR_smprintf_free(tmp_str);
+ if(oids_list[c] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ PORT_FreeArena(pool, PR_TRUE);
+ if (ret == 0) {
+ *_oids = oids_list;
+ } else {
+ talloc_free(oids_list);
+ }
+
+ return ret;
+
+}
+
+static int get_rdn_str(TALLOC_CTX *mem_ctx, CERTAVA **avas,
+ const char **rdn_str)
+{
+ size_t c;
+ char *tmp_name = NULL;
+ const char *tmp_str = NULL;
+ int ret;
+ SECStatus rv;
+ CERTRDN rdn = { 0 };
+ CERTName *name = NULL;
+ PLArenaPool *arena = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+
+ /* Multiple AVAs should be avoided becasue there is no general ordering
+ * rule and the RDN strings are not reproducible */
+ for (c = 0; avas[c] != NULL; c++) {
+ rv = sss_CERT_AddAVA(arena, &rdn, avas[c]);
+ if (rv != SECSuccess) {
+ ret = EIO;
+ goto done;
+ }
+ }
+
+ name = CERT_CreateName(&rdn, NULL);
+ if (name == NULL) {
+ ret = EIO;
+ goto done;
+ }
+
+ tmp_name = CERT_NameToAscii(name);
+ CERT_DestroyName(name);
+ if (tmp_name == NULL) {
+ ret = EIO;
+ goto done;
+ }
+
+ tmp_str = talloc_strdup(mem_ctx, tmp_name);
+ PORT_Free(tmp_name);
+ if (tmp_str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *rdn_str = tmp_str;
+ } else {
+ talloc_free(discard_const(tmp_str));
+ }
+ PORT_FreeArena(arena, PR_FALSE);
+
+ return ret;
+}
+
+static int get_rdn_list(TALLOC_CTX *mem_ctx, CERTRDN **rdns,
+ const char ***rdn_list)
+{
+ int ret;
+ size_t c;
+ const char **list = NULL;
+
+ for (c = 0; rdns[c] != NULL; c++);
+ list = talloc_zero_array(mem_ctx, const char *, c + 1);
+ if (list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ for (c = 0; rdns[c] != NULL; c++) {
+ ret = get_rdn_str(list, rdns[c]->avas,
+ &(list[c]));
+ if (ret != 0) {
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *rdn_list = list;
+ } else {
+ talloc_free(list);
+ }
+
+ return ret;
+}
+
+enum san_opt nss_name_type_to_san_opt(CERTGeneralNameType type)
+{
+ switch (type) {
+ case certOtherName:
+ return SAN_OTHER_NAME;
+ case certRFC822Name:
+ return SAN_RFC822_NAME;
+ case certDNSName:
+ return SAN_DNS_NAME;
+ case certX400Address:
+ return SAN_X400_ADDRESS;
+ case certDirectoryName:
+ return SAN_DIRECTORY_NAME;
+ case certEDIPartyName:
+ return SAN_EDIPART_NAME;
+ case certURI:
+ return SAN_URI;
+ case certIPAddress:
+ return SAN_IP_ADDRESS;
+ case certRegisterID:
+ return SAN_REGISTERED_ID;
+ default:
+ return SAN_INVALID;
+ }
+}
+
+static int add_to_san_list(TALLOC_CTX *mem_ctx, bool is_bin,
+ enum san_opt san_opt, uint8_t *data, size_t len,
+ struct san_list **item)
+{
+ struct san_list *i;
+
+ if (data == NULL || len == 0 || san_opt == SAN_INVALID) {
+ return EINVAL;
+ }
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ return ENOMEM;
+ }
+
+ i->san_opt = san_opt;
+ if (is_bin) {
+ i->bin_val = talloc_memdup(i, data, len);
+ i->bin_val_len = len;
+ } else {
+ i->val = talloc_strndup(i, (char *) data, len);
+ }
+ if (i->val == NULL) {
+ talloc_free(i);
+ return ENOMEM;
+ }
+
+ *item = i;
+
+ return 0;
+}
+
+/* taken from pkinit_crypto_nss.c of MIT Kerberos */
+/* KerberosString: RFC 4120, 5.2.1. */
+static const SEC_ASN1Template kerberos_string_template[] = {
+ {
+ SEC_ASN1_GENERAL_STRING,
+ 0,
+ NULL,
+ sizeof(SECItem),
+ }
+};
+
+/* Realm: RFC 4120, 5.2.2. */
+struct realm {
+ SECItem name;
+};
+static const SEC_ASN1Template realm_template[] = {
+ {
+ SEC_ASN1_GENERAL_STRING,
+ 0,
+ NULL,
+ sizeof(SECItem),
+ }
+};
+
+/* PrincipalName: RFC 4120, 5.2.2. */
+static const SEC_ASN1Template sequence_of_kerberos_string_template[] = {
+ {
+ SEC_ASN1_SEQUENCE_OF,
+ 0,
+ &kerberos_string_template,
+ 0,
+ }
+};
+
+struct principal_name {
+ SECItem name_type;
+ SECItem **name_string;
+};
+static const SEC_ASN1Template principal_name_template[] = {
+ {
+ SEC_ASN1_SEQUENCE,
+ 0,
+ NULL,
+ sizeof(struct principal_name),
+ },
+ {
+ SEC_ASN1_CONTEXT_SPECIFIC | 0 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
+ offsetof(struct principal_name, name_type),
+ &SEC_IntegerTemplate,
+ sizeof(SECItem),
+ },
+ {
+ SEC_ASN1_CONTEXT_SPECIFIC | 1 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
+ offsetof(struct principal_name, name_string),
+ sequence_of_kerberos_string_template,
+ sizeof(struct SECItem **),
+ },
+ {0, 0, NULL, 0},
+};
+
+/* KRB5PrincipalName: RFC 4556, 3.2.2. */
+struct kerberos_principal_name {
+ SECItem realm;
+ struct principal_name principal_name;
+};
+static const SEC_ASN1Template kerberos_principal_name_template[] = {
+ {
+ SEC_ASN1_SEQUENCE,
+ 0,
+ NULL,
+ sizeof(struct kerberos_principal_name),
+ },
+ {
+ SEC_ASN1_CONTEXT_SPECIFIC | 0 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
+ offsetof(struct kerberos_principal_name, realm),
+ &realm_template,
+ sizeof(struct realm),
+ },
+ {
+ SEC_ASN1_CONTEXT_SPECIFIC | 1 | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT,
+ offsetof(struct kerberos_principal_name, principal_name),
+ &principal_name_template,
+ sizeof(struct principal_name),
+ },
+ {0, 0, NULL, 0}
+};
+
+#define PKINIT_OID "1.3.6.1.5.2.2"
+#define NT_PRINCIPAL_OID "1.3.6.1.4.1.311.20.2.3"
+
+static int add_string_other_name_to_san_list(TALLOC_CTX *mem_ctx,
+ enum san_opt san_opt,
+ CERTGeneralName *current,
+ struct san_list **item)
+{
+ struct san_list *i = NULL;
+ int ret;
+ char *tmp_str;
+
+ tmp_str = CERT_GetOidString(&(current->name.OthName.oid));
+ /* is it expexted that NSS OID strings start with "OID." but we
+ * prefer the plain dotted-decimal version so the prefix is skipped */
+ if (tmp_str == NULL || strncmp(tmp_str, "OID.", 4) != 0) {
+ PR_smprintf_free(tmp_str);
+ return EINVAL;
+ }
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ PR_smprintf_free(tmp_str);
+ return ENOMEM;
+ }
+ i->san_opt = san_opt;
+
+ i->other_name_oid = talloc_strdup(i, tmp_str + 4);
+ PR_smprintf_free(tmp_str);
+ if (i->other_name_oid == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ i->bin_val = talloc_memdup(i, current->name.OthName.name.data,
+ current->name.OthName.name.len);
+ if (i->bin_val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ i->bin_val_len = current->name.OthName.name.len;
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *item = i;
+ } else {
+ talloc_free(i);
+ }
+
+ return ret;
+}
+
+static int get_short_name(TALLOC_CTX *mem_ctx, const char *full_name,
+ char delim, char **short_name)
+{
+ char *at;
+ char *s;
+
+ if (full_name == NULL || delim == '\0' || short_name == NULL) {
+ return EINVAL;
+ }
+
+ at = strchr(full_name, delim);
+ if (at != NULL) {
+ s = talloc_strndup(mem_ctx, full_name, (at - full_name));
+ } else {
+ s = talloc_strdup(mem_ctx, full_name);
+ }
+ if (s == NULL) {
+ return ENOMEM;
+ }
+
+ *short_name = s;
+
+ return 0;
+}
+
+static int add_nt_princ_to_san_list(TALLOC_CTX *mem_ctx,
+ PLArenaPool *pool,
+ enum san_opt san_opt,
+ CERTGeneralName *current,
+ struct san_list **item)
+{
+ struct san_list *i = NULL;
+ SECStatus rv;
+ SECItem tmp_secitem = { 0 };
+ int ret;
+
+ rv = SEC_ASN1DecodeItem(pool, &tmp_secitem,
+ SEC_ASN1_GET(SEC_UTF8StringTemplate),
+ &(current->name.OthName.name));
+ if (rv != SECSuccess) {
+ return EINVAL;
+ }
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ return ENOMEM;
+ }
+ i->san_opt = san_opt;
+
+ i->val = talloc_strndup(i, (char *) tmp_secitem.data,
+ tmp_secitem.len);
+ if (i->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = get_short_name(i, i->val, '@', &(i->short_name));
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *item = i;
+ } else {
+ talloc_free(i);
+ }
+
+ return ret;
+}
+
+static int add_pkinit_princ_to_san_list(TALLOC_CTX *mem_ctx,
+ PLArenaPool *pool,
+ enum san_opt san_opt,
+ CERTGeneralName *current,
+ struct san_list **item)
+{
+ struct san_list *i = NULL;
+ SECStatus rv;
+ struct kerberos_principal_name kname;
+ int ret;
+ size_t c;
+
+ rv = SEC_ASN1DecodeItem(pool, &kname,
+ kerberos_principal_name_template,
+ &(current->name.OthName.name));
+ if (rv != SECSuccess) {
+ return EINVAL;
+ }
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ return ENOMEM;
+ }
+ i->san_opt = san_opt;
+
+ if (kname.principal_name.name_string != NULL) {
+ i->val = talloc_strdup(i, "");
+ if (i->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ for (c = 0; kname.principal_name.name_string[c] != NULL; c++) {
+ if (c > 0) {
+ i->val = talloc_strdup_append(i->val, "/");
+ if (i->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ i->val = talloc_strndup_append(i->val,
+ (char *) kname.principal_name.name_string[c]->data,
+ kname.principal_name.name_string[c]->len);
+ if (i->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ i->val = talloc_strndup_append(i->val,
+ (char *) kname.realm.data,
+ kname.realm.len);
+ if (i->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = get_short_name(i, i->val, '@', &(i->short_name));
+ if (ret != 0) {
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *item = i;
+ } else {
+ talloc_free(i);
+ }
+
+ return ret;
+}
+
+static int add_oid_to_san_list(TALLOC_CTX *mem_ctx,
+ enum san_opt san_opt,
+ SECItem oid,
+ struct san_list **item)
+{
+ struct san_list *i = NULL;
+ char *tmp_str;
+
+ tmp_str = CERT_GetOidString(&oid);
+ /* is it expexted that NSS OID strings start with "OID." but we
+ * prefer the plain dotted-decimal version so the prefix is skipped */
+ if (tmp_str == NULL || strncmp(tmp_str, "OID.", 4) != 0) {
+ PR_smprintf_free(tmp_str);
+ return EINVAL;
+ }
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ PR_smprintf_free(tmp_str);
+ return ENOMEM;
+ }
+ i->san_opt = san_opt;
+
+ i->val = talloc_strdup(i, tmp_str + 4);
+ PR_smprintf_free(tmp_str);
+ if (i->val == NULL) {
+ talloc_free(i);
+ return ENOMEM;
+ }
+
+ *item = i;
+ return 0;
+}
+
+static int add_rdn_list_to_san_list(TALLOC_CTX *mem_ctx,
+ enum san_opt san_opt,
+ CERTName name,
+ struct san_list **item)
+{
+ struct san_list *i = NULL;
+ int ret;
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ return ENOMEM;
+ }
+ i->san_opt = san_opt;
+
+ ret = get_rdn_list(i, name.rdns, &(i->rdn_list));
+ if (ret != 0) {
+ talloc_free(i);
+ return ret;
+ }
+
+ *item = i;
+ return 0;
+}
+
+static int add_ip_to_san_list(TALLOC_CTX *mem_ctx, enum san_opt san_opt,
+ uint8_t *data, size_t len,
+ struct san_list **item)
+{
+ struct san_list *i;
+ PRStatus st;
+ PRNetAddr addr;
+ char addrBuf[80];
+
+ if (data == NULL || len == 0 || san_opt == SAN_INVALID) {
+ return EINVAL;
+ }
+
+ /* taken from secu_PrintIPAddress() */
+ memset(&addr, 0, sizeof addr);
+ if (len == 4) {
+ addr.inet.family = PR_AF_INET;
+ memcpy(&addr.inet.ip, data, len);
+ } else if (len == 16) {
+ addr.ipv6.family = PR_AF_INET6;
+ memcpy(addr.ipv6.ip.pr_s6_addr, data, len);
+ if (PR_IsNetAddrType(&addr, PR_IpAddrV4Mapped)) {
+ /* convert to IPv4. */
+ addr.inet.family = PR_AF_INET;
+ memcpy(&addr.inet.ip, &addr.ipv6.ip.pr_s6_addr[12], 4);
+ memset(&addr.inet.pad[0], 0, sizeof addr.inet.pad);
+ }
+ } else {
+ return EINVAL;
+ }
+
+ st = PR_NetAddrToString(&addr, addrBuf, sizeof addrBuf);
+ if (st != PR_SUCCESS) {
+ return EIO;
+ }
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ return ENOMEM;
+ }
+
+ i->san_opt = san_opt;
+ i->val = talloc_strdup(i, addrBuf);
+ if (i->val == NULL) {
+ talloc_free(i);
+ return ENOMEM;
+ }
+
+ *item = i;
+ return 0;
+}
+static int add_principal_to_san_list(TALLOC_CTX *mem_ctx,
+ enum san_opt san_opt,
+ const char *princ,
+ struct san_list **item)
+{
+ struct san_list *i = NULL;
+ int ret;
+
+ i = talloc_zero(mem_ctx, struct san_list);
+ if (i == NULL) {
+ return ENOMEM;
+ }
+ i->san_opt = san_opt;
+
+ i->val = talloc_strdup(i, princ);
+ if (i->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = get_short_name(i, i->val, '@', &(i->short_name));
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *item = i;
+ } else {
+ talloc_free(i);
+ }
+
+ return ret;
+}
+
+static int get_san(TALLOC_CTX *mem_ctx, CERTCertificate *cert,
+ struct san_list **san_list)
+{
+
+ SECItem subAltName = { 0 };
+ SECStatus rv;
+ CERTGeneralName *name_list = NULL;
+ CERTGeneralName *current;
+ PLArenaPool *pool = NULL;
+ int ret;
+ struct san_list *list = NULL;
+ struct san_list *item = NULL;
+ struct san_list *item_s = NULL;
+ struct san_list *item_p = NULL;
+ struct san_list *item_pb = NULL;
+
+ rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME,
+ &subAltName);
+ if (rv != SECSuccess) {
+ if (rv == SECFailure
+ && PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND) {
+ ret = EOK;
+ } else {
+ ret = EIO;
+ }
+ goto done;
+ }
+
+ pool = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (pool == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ name_list = CERT_DecodeAltNameExtension(pool, &subAltName);
+ if (name_list == NULL ) {
+ ret = EIO;
+ goto done;
+ }
+
+ current = name_list;
+ do {
+ switch (current->type) {
+ case certOtherName:
+ ret = add_string_other_name_to_san_list(mem_ctx,
+ SAN_STRING_OTHER_NAME,
+ current, &item_s);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item_s);
+
+ item_p = NULL;
+ if (strcmp(item_s->other_name_oid, NT_PRINCIPAL_OID) == 0) {
+ ret = add_nt_princ_to_san_list(mem_ctx, pool, SAN_NT, current,
+ &item_p);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item_p);
+ } else if (strcmp(item_s->other_name_oid, PKINIT_OID) == 0) {
+ ret = add_pkinit_princ_to_san_list(mem_ctx, pool, SAN_PKINIT,
+ current, &item_p);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item_p);
+ }
+
+ if (item_p != NULL) {
+ ret = add_principal_to_san_list(mem_ctx, SAN_PRINCIPAL,
+ item_p->val, &item_pb);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item_pb);
+ }
+
+ break;
+ case certRFC822Name:
+ case certDNSName:
+ case certURI:
+ ret = add_to_san_list(mem_ctx, false,
+ nss_name_type_to_san_opt(current->type),
+ current->name.other.data,
+ current->name.other.len, &item);
+ if (ret != 0) {
+ goto done;
+ }
+
+ if (current->type == certRFC822Name
+ || current->type == certDNSName) {
+ ret = get_short_name(item, item->val,
+ (current->type == certRFC822Name
+ ? '@' : '.'),
+ &(item->short_name));
+ if (ret != 0) {
+ goto done;
+ }
+ }
+
+ DLIST_ADD(list, item);
+ break;
+ case certIPAddress:
+ ret = add_ip_to_san_list(mem_ctx,
+ nss_name_type_to_san_opt(current->type),
+ current->name.other.data,
+ current->name.other.len, &item);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item);
+ break;
+ case certDirectoryName:
+ ret = add_rdn_list_to_san_list(mem_ctx,
+ nss_name_type_to_san_opt(current->type),
+ current->name.directoryName, &item);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item);
+ break;
+ case certRegisterID:
+ ret = add_oid_to_san_list(mem_ctx,
+ nss_name_type_to_san_opt(current->type),
+ current->name.other, &item);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item);
+ break;
+ case certX400Address:
+ case certEDIPartyName:
+ ret = add_to_san_list(mem_ctx, true,
+ nss_name_type_to_san_opt(current->type),
+ current->name.other.data,
+ current->name.other.len, &item);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(list, item);
+ break;
+ default:
+ ret = EINVAL;
+ }
+
+ current = CERT_GetNextGeneralName(current);
+ if (current == NULL) {
+ ret = EIO;
+ goto done;
+ }
+ } while (current != name_list);
+
+done:
+
+ /* Don't free nameList, it's part of the arena. */
+
+ if (pool != NULL) {
+ PORT_FreeArena(pool, PR_FALSE);
+ }
+
+ if (subAltName.data != NULL) {
+ SECITEM_FreeItem(&subAltName, PR_FALSE);
+ }
+
+ if (ret == EOK) {
+ *san_list = list;
+ }
+ return ret;
+}
+
+int sss_cert_get_content(TALLOC_CTX *mem_ctx,
+ const uint8_t *der_blob, size_t der_size,
+ struct sss_cert_content **content)
+{
+ int ret;
+ struct sss_cert_content *cont = NULL;
+ CERTCertDBHandle *handle;
+ CERTCertificate *cert = NULL;
+ SECItem der_item;
+ NSSInitContext *nss_ctx;
+
+ if (der_blob == NULL || der_size == 0) {
+ return EINVAL;
+ }
+
+ nss_ctx = NSS_InitContext("", "", "", "", NULL, NSS_INIT_READONLY
+ | NSS_INIT_NOCERTDB
+ | NSS_INIT_NOMODDB
+ | NSS_INIT_FORCEOPEN
+ | NSS_INIT_NOROOTINIT
+ | NSS_INIT_OPTIMIZESPACE);
+ if (nss_ctx == NULL) {
+ return EIO;
+ }
+
+ cont = talloc_zero(mem_ctx, struct sss_cert_content);
+ if (cont == NULL) {
+ return ENOMEM;
+ }
+
+ handle = CERT_GetDefaultCertDB();
+ der_item.len = der_size;
+ der_item.data = discard_const(der_blob);
+
+ cert = CERT_NewTempCertificate(handle, &der_item, NULL, PR_FALSE, PR_TRUE);
+ if (cert == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ cont->issuer_str = talloc_strdup(cont, cert->issuerName);
+ if (cont->issuer_str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = get_rdn_list(cont, cert->issuer.rdns, &cont->issuer_rdn_list);
+ if (ret != 0) {
+ goto done;
+ }
+
+ cont->subject_str = talloc_strdup(cont, cert->subjectName);
+ if (cont->subject_str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = get_rdn_list(cont, cert->subject.rdns, &cont->subject_rdn_list);
+ if (ret != 0) {
+ goto done;
+ }
+
+
+ cont->key_usage = cert->keyUsage;
+
+ ret = get_extended_key_usage_oids(cont, cert,
+ &(cont->extended_key_usage_oids));
+ if (ret != 0) {
+ goto done;
+ }
+
+ ret = get_san(cont, cert, &(cont->san_list));
+ if (ret != 0) {
+ goto done;
+ }
+
+ cont->cert_der = talloc_memdup(cont, der_blob, der_size);
+ if (cont->cert_der == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cont->cert_der_size = der_size;
+ ret = EOK;
+
+done:
+
+ CERT_DestroyCertificate(cert);
+ NSS_ShutdownContext(nss_ctx);
+
+ if (ret == EOK) {
+ *content = cont;
+ } else {
+ talloc_free(cont);
+ }
+
+ return ret;
+}
diff --git a/src/lib/certmap/sss_certmap.c b/src/lib/certmap/sss_certmap.c
new file mode 100644
index 000000000..080cab8ab
--- /dev/null
+++ b/src/lib/certmap/sss_certmap.c
@@ -0,0 +1,993 @@
+/*
+ SSSD
+
+ Library for rule based certificate to user mapping
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "util/cert.h"
+#include "util/crypto/sss_crypto.h"
+#include "lib/certmap/sss_certmap.h"
+#include "lib/certmap/sss_certmap_int.h"
+
+int debug_level;
+void sss_debug_fn(const char *file,
+ long line,
+ const char *function,
+ int level,
+ const char *format, ...)
+{
+ return;
+}
+
+static int get_type_prefix(TALLOC_CTX *mem_ctx, const char *match_rule,
+ char **type, const char **rule_start)
+{
+ const char *c;
+ char *delim;
+
+ *type = NULL;
+ *rule_start = match_rule;
+
+ delim = strchr(match_rule, ':');
+ if (delim == NULL) {
+ /* no type prefix found */
+ return 0;
+ }
+
+ /* rule starts with ':', empty type */
+ if (delim == match_rule) {
+ *rule_start = delim + 1;
+ return EOK;
+ }
+
+ for (c = match_rule; c < delim; c++) {
+ /* type prefix may only contain digits and upper-case ASCII characters */
+ if (!(isascii(*c) && (isdigit(*c) || isupper(*c)))) {
+ /* no type prefix found */
+ return 0;
+ }
+ }
+
+ *rule_start = delim + 1;
+ *type = talloc_strndup(mem_ctx, match_rule, (delim - match_rule));
+ if (*type == NULL) {
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+static int parse_match_rule(struct sss_certmap_ctx *ctx, const char *match_rule,
+ struct krb5_match_rule **parsed_match_rule)
+{
+ int ret;
+ char *type;
+ const char *rule_start;
+
+ ret = get_type_prefix(ctx, match_rule, &type, &rule_start);
+ if (ret != EOK) {
+ CM_DEBUG(ctx, "Failed to read rule type.");
+ goto done;
+ }
+
+ if (type == NULL || strcmp(type, "KRB5") == 0) {
+ ret = parse_krb5_match_rule(ctx, rule_start, parsed_match_rule);
+ if (ret != EOK) {
+ CM_DEBUG(ctx, "Failed to parse KRB5 matching rule.");
+ goto done;
+ }
+ } else {
+ CM_DEBUG(ctx, "Unsupported matching rule type.");
+ ret = ESRCH;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(type);
+
+ return ret;
+}
+
+static int parse_mapping_rule(struct sss_certmap_ctx *ctx,
+ const char *mapping_rule,
+ struct ldap_mapping_rule **parsed_mapping_rule)
+{
+ int ret;
+ char *type;
+ const char *rule_start;
+
+ ret = get_type_prefix(ctx, mapping_rule, &type, &rule_start);
+ if (ret != EOK) {
+ CM_DEBUG(ctx, "Failed to read rule type.");
+ goto done;
+ }
+
+ if (type == NULL || strcmp(type, "LDAP") == 0) {
+ ret = parse_ldap_mapping_rule(ctx, rule_start, parsed_mapping_rule);
+ if (ret != EOK) {
+ CM_DEBUG(ctx, "Failed to parse LDAP mapping rule.");
+ goto done;
+ }
+ } else {
+ CM_DEBUG(ctx, "Unsupported mapping rule type.");
+ ret = ESRCH;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(type);
+
+ return ret;
+}
+
+int sss_certmap_add_rule(struct sss_certmap_ctx *ctx,
+ uint32_t priority, const char *match_rule,
+ const char *map_rule, const char **domains)
+{
+ size_t c;
+ int ret;
+ struct match_map_rule *rule;
+ struct TALLOC_CTX *tmp_ctx;
+ struct priority_list *p;
+ struct priority_list *p_new;
+ struct krb5_match_rule *parsed_match_rule;
+ struct ldap_mapping_rule *parsed_mapping_rule;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ rule = talloc_zero(tmp_ctx, struct match_map_rule);
+ if (rule == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ rule->priority = priority;
+
+ if (match_rule == NULL) {
+ match_rule = DEFAULT_MATCH_RULE;
+ }
+ ret = parse_match_rule(ctx, match_rule, &parsed_match_rule);
+ if (ret == 0) {
+ rule->parsed_match_rule = talloc_steal(rule, parsed_match_rule);
+ rule->match_rule = talloc_strdup(rule, match_rule);
+ if (rule->match_rule == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else if (ret == ESRCH) {
+ /* report unsupported rules */
+ goto done;
+ } else {
+ goto done;
+ }
+
+ if (map_rule == NULL) {
+ map_rule = DEFAULT_MAP_RULE;
+ }
+ ret = parse_mapping_rule(ctx, map_rule, &parsed_mapping_rule);
+ if (ret == 0) {
+ rule->parsed_mapping_rule = talloc_steal(rule, parsed_mapping_rule);
+ rule->map_rule = talloc_strdup(rule, map_rule);
+ if (rule->map_rule == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else if (ret == ESRCH) {
+ /* report unsupported rules */
+ goto done;
+ } else {
+ goto done;
+ }
+
+ if (domains != NULL && *domains != NULL) {
+ for (c = 0; domains[c] != NULL; c++);
+ rule->domains = talloc_zero_array(rule, char *, c + 1);
+ if (rule->domains == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ for (c = 0; domains[c] != NULL; c++) {
+ rule->domains[c] = talloc_strdup(rule->domains, domains[c]);
+ if (rule->domains[c] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ if (ctx->prio_list == NULL) {
+ ctx->prio_list = talloc_zero(ctx, struct priority_list);
+ if (ctx->prio_list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ctx->prio_list->priority = rule->priority;
+ ctx->prio_list->rule_list = rule;
+ } else {
+ for (p = ctx->prio_list; p != NULL && p->priority < rule->priority;
+ p = p->next);
+ if (p != NULL && p->priority == priority) {
+ DLIST_ADD(p->rule_list, rule);
+ } else {
+ p_new = talloc_zero(ctx, struct priority_list);
+ if (p_new == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ p_new->priority = rule->priority;
+ p_new->rule_list = rule;
+
+ if (p == NULL) {
+ DLIST_ADD_END(ctx->prio_list, p_new, struct priority_list *);
+ } else if (p->prev == NULL) {
+ DLIST_ADD(ctx->prio_list, p_new);
+ } else {
+ DLIST_ADD_AFTER(ctx->prio_list, p_new, p->prev);
+ }
+ }
+ }
+
+ talloc_steal(ctx, rule);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static int expand_cert(struct sss_certmap_ctx *ctx,
+ struct parsed_template *parsed_template,
+ struct sss_cert_content *cert_content,
+ char **expanded)
+{
+ int ret;
+ char *tmp_str = NULL;
+
+ if (parsed_template->conversion == NULL
+ || strcmp(parsed_template->conversion, "bin") == 0) {
+ ret = bin_to_ldap_filter_value(ctx, cert_content->cert_der,
+ cert_content->cert_der_size, &tmp_str);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "bin conversion failed.");
+ goto done;
+ }
+ } else if (strcmp(parsed_template->conversion, "base64") == 0) {
+ tmp_str = sss_base64_encode(ctx, cert_content->cert_der,
+ cert_content->cert_der_size);
+ if (tmp_str == NULL) {
+ CM_DEBUG(ctx, "base64 conversion failed.");
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ CM_DEBUG(ctx, "Unsupported conversion.");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *expanded = tmp_str;
+ } else {
+ talloc_free(tmp_str);
+ }
+
+ return ret;
+}
+
+static int get_dn_str(struct sss_certmap_ctx *ctx, const char *conversion,
+ const char **rdn_list, char **result)
+{
+ char *str = NULL;
+ size_t c;
+ int ret;
+ char *conv = NULL;
+
+ str = talloc_strdup(ctx, "");
+ if (str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ if (conversion == NULL || strcmp(conversion, "nss_ldap") == 0
+ || strcmp(conversion, "nss") == 0) {
+ for (c = 0; rdn_list[c] != NULL; c++);
+ while (c != 0) {
+ c--;
+ str = talloc_asprintf_append(str, "%s%s",
+ (rdn_list[c + 1] == NULL) ? "" : ",",
+ rdn_list[c]);
+ if (str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ };
+ } else if (strcmp(conversion, "ad_ldap") == 0) {
+ for (c = 0; rdn_list[c] != NULL; c++);
+ while (c != 0) {
+ c--;
+ conv = check_ad_attr_name(str, rdn_list[c]);
+ str = talloc_asprintf_append(str, "%s%s",
+ (rdn_list[c + 1] == NULL) ? "" : ",",
+ conv == NULL ? rdn_list[c] : conv);
+ talloc_free(conv);
+ conv = NULL;
+ if (str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ };
+ } else if (strcmp(conversion, "nss_x500") == 0) {
+ for (c = 0; rdn_list[c] != NULL; c++) {
+ str = talloc_asprintf_append(str, "%s%s", (c == 0) ? "" : ",",
+ rdn_list[c]);
+ if (str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ } else if (strcmp(conversion, "ad_x500") == 0
+ || strcmp(conversion, "ad") == 0) {
+ for (c = 0; rdn_list[c] != NULL; c++) {
+ conv = check_ad_attr_name(str, rdn_list[c]);
+ str = talloc_asprintf_append(str, "%s%s",
+ (c == 0) ? "" : ",",
+ conv == NULL ? rdn_list[c] : conv);
+ talloc_free(conv);
+ conv = NULL;
+ if (str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ } else {
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *result = str;
+ } else {
+ talloc_free(str);
+ }
+
+ return ret;
+}
+
+static int expand_san_blob(struct sss_certmap_ctx *ctx, enum san_opt san_opt,
+ struct san_list *san_list, char **expanded)
+{
+ struct san_list *item;
+ char *exp;
+ int ret;
+
+ DLIST_FOR_EACH(item, san_list) {
+ if (item->san_opt == san_opt) {
+ ret = bin_to_ldap_filter_value(ctx, item->bin_val,
+ item->bin_val_len, &exp);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "bin conversion failed.");
+ return ret;
+ }
+
+ *expanded = exp;
+ return 0;
+ }
+ }
+
+ return ENOENT;
+}
+
+static int expand_san_string(struct sss_certmap_ctx *ctx, enum san_opt san_opt,
+ struct san_list *san_list, const char *attr_name,
+ char **expanded)
+{
+ struct san_list *item;
+ char *exp;
+
+ DLIST_FOR_EACH(item, san_list) {
+ if (item->san_opt == san_opt) {
+ if (attr_name == NULL) {
+ exp = talloc_strdup(ctx, item->val);
+ } else if (strcasecmp(attr_name, "short_name") == 0) {
+ exp = talloc_strdup(ctx, item->short_name);
+ } else {
+ CM_DEBUG(ctx, "Unsupported attribute name [%s].", attr_name);
+ return EINVAL;
+ }
+
+ if (exp == NULL) {
+ return ENOMEM;
+ }
+
+ *expanded = exp;
+ return 0;
+ }
+ }
+
+ return ENOENT;
+}
+
+static int expand_san_rdn_list(struct sss_certmap_ctx *ctx,
+ enum san_opt san_opt,
+ struct san_list *san_list,
+ const char *conversion,
+ char **expanded)
+{
+ struct san_list *item;
+ char *exp;
+ int ret;
+
+ DLIST_FOR_EACH(item, san_list) {
+ if (item->san_opt == san_opt) {
+ ret = get_dn_str(ctx, conversion, item->rdn_list, &exp);
+ if (ret != 0) {
+ return ret;
+ }
+
+ *expanded = exp;
+ return 0;
+ }
+ }
+
+ return ENOENT;
+}
+
+
+static int expand_san(struct sss_certmap_ctx *ctx,
+ struct parsed_template *parsed_template,
+ struct san_list *san_list,
+ char **expanded)
+{
+ int ret;
+
+ if (strcmp("subject_rfc822_name", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_RFC822_NAME, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_dns_name", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_DNS_NAME, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_x400_address", parsed_template->name) == 0) {
+ ret = expand_san_blob(ctx, SAN_X400_ADDRESS, san_list, expanded);
+ } else if (strcmp("subject_directory_name", parsed_template->name) == 0) {
+ ret = expand_san_rdn_list(ctx, SAN_DIRECTORY_NAME, san_list,
+ parsed_template->conversion, expanded);
+ } else if (strcmp("subject_ediparty_name", parsed_template->name) == 0) {
+ ret = expand_san_blob(ctx, SAN_EDIPART_NAME, san_list, expanded);
+ } else if (strcmp("subject_uri", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_URI, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_ip_address", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_IP_ADDRESS, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_registered_id", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_REGISTERED_ID, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_pkinit_principal", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_PKINIT, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_nt_principal", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_NT, san_list,
+ parsed_template->attr_name, expanded);
+ } else if (strcmp("subject_principal", parsed_template->name) == 0) {
+ ret = expand_san_string(ctx, SAN_PRINCIPAL, san_list,
+ parsed_template->attr_name, expanded);
+ } else {
+ CM_DEBUG(ctx, "Unsupported template name [%s].n",
+ parsed_template->name);
+ ret = EINVAL;
+ }
+
+ return ret;
+}
+
+static int expand_template(struct sss_certmap_ctx *ctx,
+ struct parsed_template *parsed_template,
+ struct sss_cert_content *cert_content,
+ char **expanded)
+{
+ int ret;
+ char *exp = NULL;
+
+ if (strcmp("issuer_dn", parsed_template->name) == 0) {
+ ret = get_dn_str(ctx, parsed_template->conversion,
+ cert_content->issuer_rdn_list, &exp);
+ } else if (strcmp("subject_dn", parsed_template->name) == 0) {
+ ret = get_dn_str(ctx, parsed_template->conversion,
+ cert_content->subject_rdn_list, &exp);
+ } else if (strncmp("subject_", parsed_template->name, 8) == 0) {
+ ret = expand_san(ctx, parsed_template, cert_content->san_list, &exp);
+ } else if (strcmp("cert", parsed_template->name) == 0) {
+ ret = expand_cert(ctx, parsed_template, cert_content, &exp);
+ } else {
+ CM_DEBUG(ctx, "Unsupported template name.");
+ ret = EINVAL;
+ goto done;
+ }
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to expand [%s] template.", parsed_template->name);
+ goto done;
+ }
+
+ if (exp == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *expanded = exp;
+ } else {
+ talloc_free(exp);
+ }
+
+ return ret;
+}
+
+static int get_filter(struct sss_certmap_ctx *ctx,
+ struct ldap_mapping_rule *parsed_mapping_rule,
+ struct sss_cert_content *cert_content,
+ char **filter)
+{
+ struct ldap_mapping_rule_comp *comp;
+ char *result = NULL;
+ char *expanded = NULL;
+ int ret;
+
+ result = talloc_strdup(ctx, "");
+ if (result == NULL) {
+ return ENOMEM;
+ }
+
+ for (comp = parsed_mapping_rule->list; comp != NULL; comp = comp->next) {
+ if (comp->type == comp_string) {
+ result = talloc_strdup_append(result, comp->val);
+ } else if (comp->type == comp_template) {
+ ret = expand_template(ctx, comp->parsed_template, cert_content,
+ &expanded);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to expanded template.");
+ goto done;
+ }
+
+ result = talloc_strdup_append(result, expanded);
+ talloc_free(expanded);
+ expanded = NULL;
+ if (result == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ ret = EINVAL;
+ CM_DEBUG(ctx, "Unsupported component type.");
+ goto done;
+ }
+ }
+
+ ret = 0;
+done:
+ talloc_free(expanded);
+ if (ret == 0) {
+ *filter = result;
+ } else {
+ talloc_free(result);
+ }
+
+ return ret;
+}
+
+static bool check_san_regexp(struct sss_certmap_ctx *ctx,
+ enum san_opt san_opt, regex_t regexp,
+ struct san_list *san_list)
+{
+ struct san_list *item;
+ bool match = false;
+ int ret;
+ char *tmp_str = NULL;
+
+ DLIST_FOR_EACH(item, san_list) {
+ if (item->san_opt == san_opt) {
+ if (item->san_opt == SAN_DIRECTORY_NAME) {
+ /* use LDAP order for matching */
+ ret = get_dn_str(ctx, NULL, item->rdn_list, &tmp_str);
+ if (ret != 0 || tmp_str == NULL) {
+ return false;
+ }
+ match = (regexec(&regexp, tmp_str, 0, NULL, 0) == 0);
+ talloc_free(tmp_str);
+ } else {
+ match = (item->val != NULL
+ && regexec(&regexp, item->val, 0, NULL, 0) == 0);
+ }
+ if (!match) {
+ return false;
+ }
+ }
+ }
+
+ return match;
+}
+
+static bool check_san_blob(enum san_opt san_opt,
+ uint8_t *bin_val, size_t bin_val_len,
+ struct san_list *san_list)
+{
+ struct san_list *item;
+ bool match = false;
+
+ if (bin_val == NULL || bin_val_len == 0) {
+ return false;
+ }
+
+ DLIST_FOR_EACH(item, san_list) {
+ if (item->san_opt == san_opt) {
+ match = (item->bin_val != NULL && item->bin_val_len != 0
+ && memmem(item->bin_val, item->bin_val_len,
+ bin_val, bin_val_len) != NULL);
+ if (!match) {
+ return false;
+ }
+ }
+ }
+
+ return match;
+}
+
+static bool check_san_str_other_name(enum san_opt san_opt,
+ const char *str_other_name_oid,
+ regex_t regexp,
+ struct san_list *san_list)
+{
+ struct san_list *item;
+ bool match = false;
+ char *tmp_str;
+
+ if (str_other_name_oid == NULL) {
+ return false;
+ }
+
+ DLIST_FOR_EACH(item, san_list) {
+ if (item->san_opt == san_opt
+ && strcmp(item->other_name_oid, str_other_name_oid) == 0) {
+ match = false;
+ if (item->bin_val != NULL && item->bin_val_len != 0) {
+ tmp_str = talloc_strndup(item, (char *) item->bin_val,
+ item->bin_val_len);
+ if (tmp_str != NULL) {
+ match = (regexec(&regexp, tmp_str, 0, NULL, 0) == 0);
+ }
+ talloc_free(tmp_str);
+ }
+ if (!match) {
+ return false;
+ }
+ }
+ }
+
+ return match;
+}
+
+static bool do_san_match(struct sss_certmap_ctx *ctx,
+ struct component_list *comp,
+ struct san_list *san_list)
+{
+ switch (comp->san_opt) {
+ case SAN_OTHER_NAME:
+ return check_san_blob(SAN_STRING_OTHER_NAME,
+ comp->bin_val, comp->bin_val_len,
+ san_list);
+ break;
+ case SAN_X400_ADDRESS:
+ case SAN_EDIPART_NAME:
+ return check_san_blob(comp->san_opt, comp->bin_val, comp->bin_val_len,
+ san_list);
+ break;
+ case SAN_RFC822_NAME:
+ case SAN_DNS_NAME:
+ case SAN_DIRECTORY_NAME:
+ case SAN_URI:
+ case SAN_IP_ADDRESS:
+ case SAN_REGISTERED_ID:
+ case SAN_PKINIT:
+ case SAN_NT:
+ case SAN_PRINCIPAL:
+ return check_san_regexp(ctx, comp->san_opt, comp->regexp, san_list);
+ break;
+ case SAN_STRING_OTHER_NAME:
+ return check_san_str_other_name(comp->san_opt, comp->str_other_name_oid,
+ comp->regexp, san_list);
+ break;
+ default:
+ CM_DEBUG(ctx, "Unsupported SAN option [%d].", comp->san_opt);
+ return false;
+ }
+}
+
+static int do_match(struct sss_certmap_ctx *ctx,
+ struct krb5_match_rule *parsed_match_rule,
+ struct sss_cert_content *cert_content)
+{
+ struct component_list *comp;
+ bool match = false;
+ size_t c;
+
+ if (parsed_match_rule == NULL || cert_content == NULL) {
+ return EINVAL;
+ }
+
+ /* Issuer */
+ for (comp = parsed_match_rule->issuer; comp != NULL; comp = comp->next) {
+ match = (cert_content->issuer_str != NULL
+ && regexec(&(comp->regexp), cert_content->issuer_str,
+ 0, NULL, 0) == 0);
+ if (match && parsed_match_rule->r == relation_or) {
+ /* match */
+ return 0;
+ } else if (!match && parsed_match_rule->r == relation_and) {
+ /* no match */
+ return ENOENT;
+ }
+
+ }
+
+ /* Subject */
+ for (comp = parsed_match_rule->subject; comp != NULL; comp = comp->next) {
+ match = (cert_content->subject_str != NULL
+ && regexec(&(comp->regexp), cert_content->subject_str,
+ 0, NULL, 0) == 0);
+ if (match && parsed_match_rule->r == relation_or) {
+ /* match */
+ return 0;
+ } else if (!match && parsed_match_rule->r == relation_and) {
+ /* no match */
+ return ENOENT;
+ }
+
+ }
+
+ /* Key Usage */
+ for (comp = parsed_match_rule->ku; comp != NULL; comp = comp->next) {
+ match = ((cert_content->key_usage & comp->ku) == comp->ku);
+ if (match && parsed_match_rule->r == relation_or) {
+ /* match */
+ return 0;
+ } else if (!match && parsed_match_rule->r == relation_and) {
+ /* no match */
+ return ENOENT;
+ }
+ }
+
+ /* Extended Key Usage */
+ for (comp = parsed_match_rule->eku; comp != NULL; comp = comp->next) {
+ for (c = 0; comp->eku_oid_list[c] != NULL; c++) {
+ match = string_in_list(comp->eku_oid_list[c],
+ discard_const(
+ cert_content->extended_key_usage_oids),
+ true);
+ if (match && parsed_match_rule->r == relation_or) {
+ /* match */
+ return 0;
+ } else if (!match && parsed_match_rule->r == relation_and) {
+ /* no match */
+ return ENOENT;
+ }
+ }
+ }
+
+ /* SAN */
+ for (comp = parsed_match_rule->san; comp != NULL; comp = comp->next) {
+ match = do_san_match(ctx, comp, cert_content->san_list);
+ if (match && parsed_match_rule->r == relation_or) {
+ /* match */
+ return 0;
+ } else if (!match && parsed_match_rule->r == relation_and) {
+ /* no match */
+ return ENOENT;
+ }
+ }
+
+ if (match) {
+ /* match */
+ return 0;
+ }
+
+ /* no match */
+ return ENOENT;
+}
+
+int sss_certmap_match_cert(struct sss_certmap_ctx *ctx,
+ const uint8_t *der_cert, size_t der_size)
+{
+ int ret;
+ struct match_map_rule *r;
+ struct priority_list *p;
+ struct sss_cert_content *cert_content = NULL;
+
+ ret = sss_cert_get_content(ctx, der_cert, der_size, &cert_content);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to get certificate content.");
+ return ret;
+ }
+
+ if (ctx->prio_list == NULL) {
+ /* Match all certificates if there are no rules applied */
+ ret = 0;
+ goto done;
+ }
+
+ for (p = ctx->prio_list; p != NULL; p = p->next) {
+ for (r = p->rule_list; r != NULL; r = r->next) {
+ ret = do_match(ctx, r->parsed_match_rule, cert_content);
+ if (ret == 0) {
+ /* match */
+ goto done;
+ }
+ }
+ }
+
+ ret = ENOENT;
+done:
+ talloc_free(cert_content);
+
+ return ret;
+}
+
+int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx,
+ const uint8_t *der_cert, size_t der_size,
+ char **_filter, char ***_domains)
+{
+ int ret;
+ struct match_map_rule *r;
+ struct priority_list *p;
+ struct sss_cert_content *cert_content = NULL;
+ char *filter = NULL;
+ char **domains = NULL;
+ size_t c;
+
+ if (_filter == NULL || _domains == NULL) {
+ return EINVAL;
+ }
+
+ ret = sss_cert_get_content(ctx, der_cert, der_size, &cert_content);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to get certificate content [%d].", ret);
+ return ret;
+ }
+
+ if (ctx->prio_list == NULL) {
+ if (ctx->default_mapping_rule == NULL) {
+ CM_DEBUG(ctx, "No matching or mapping rules available.");
+ return EINVAL;
+ }
+
+ ret = get_filter(ctx, ctx->default_mapping_rule, cert_content, &filter);
+ goto done;
+ }
+
+ for (p = ctx->prio_list; p != NULL; p = p->next) {
+ for (r = p->rule_list; r != NULL; r = r->next) {
+ ret = do_match(ctx, r->parsed_match_rule, cert_content);
+ if (ret == 0) {
+ /* match */
+ ret = get_filter(ctx, r->parsed_mapping_rule, cert_content,
+ &filter);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to get filter");
+ goto done;
+ }
+
+ if (r->domains != NULL) {
+ for (c = 0; r->domains[c] != NULL; c++);
+ domains = talloc_zero_array(ctx, char *, c + 1);
+ if (domains == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (c = 0; r->domains[c] != NULL; c++) {
+ domains[c] = talloc_strdup(domains, r->domains[c]);
+ if (domains[c] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ }
+
+ ret = 0;
+ goto done;
+ }
+ }
+ }
+
+ ret = ENOENT;
+
+done:
+ talloc_free(cert_content);
+ if (ret == 0) {
+ *_filter = filter;
+ *_domains = domains;
+ } else {
+ talloc_free(filter);
+ talloc_free(domains);
+ }
+
+ return ret;
+}
+
+int sss_certmap_init(TALLOC_CTX *mem_ctx,
+ sss_certmap_ext_debug *debug, void *debug_priv,
+ struct sss_certmap_ctx **ctx)
+{
+ int ret;
+
+ if (ctx == NULL) {
+ return EINVAL;
+ }
+
+ *ctx = talloc_zero(mem_ctx, struct sss_certmap_ctx);
+ if (*ctx == NULL) {
+ return ENOMEM;
+ }
+
+ (*ctx)->debug = debug;
+ (*ctx)->debug_priv = debug_priv;
+
+ ret = parse_mapping_rule(*ctx, DEFAULT_MAP_RULE,
+ &((*ctx)->default_mapping_rule));
+ if (ret != 0) {
+ CM_DEBUG((*ctx), "Failed to parse default mapping rule.");
+ talloc_free(*ctx);
+ *ctx = NULL;
+ return ret;
+ }
+
+ CM_DEBUG((*ctx), "sss_certmap initialized.");
+ return EOK;
+}
+
+void sss_certmap_free_ctx(struct sss_certmap_ctx *ctx)
+{
+ talloc_free(ctx);
+}
+
+void sss_certmap_free_filter_and_domains(char *filter, char **domains)
+{
+ talloc_free(filter);
+ talloc_free(domains);
+}
diff --git a/src/lib/certmap/sss_certmap.doxy.in b/src/lib/certmap/sss_certmap.doxy.in
new file mode 100644
index 000000000..e8959e209
--- /dev/null
+++ b/src/lib/certmap/sss_certmap.doxy.in
@@ -0,0 +1,3 @@
+PROJECT_NAME = sss_certmap
+OUTPUT_DIRECTORY = certmap_doc
+INPUT = @abs_top_srcdir@/src/lib/certmap/sss_certmap.h
diff --git a/src/lib/certmap/sss_certmap.exports b/src/lib/certmap/sss_certmap.exports
new file mode 100644
index 000000000..8b5d53666
--- /dev/null
+++ b/src/lib/certmap/sss_certmap.exports
@@ -0,0 +1,13 @@
+SSS_CERTMAP_0.0 {
+ global:
+ sss_certmap_init;
+ sss_certmap_free_ctx;
+ sss_certmap_err_msg;
+ sss_certmap_add_rule;
+ sss_certmap_match_cert;
+ sss_certmap_get_search_filter;
+ sss_cert_get_content;
+ sss_certmap_free_filter_and_domains;
+ local:
+ *;
+};
diff --git a/src/lib/certmap/sss_certmap.h b/src/lib/certmap/sss_certmap.h
new file mode 100644
index 000000000..55485cc35
--- /dev/null
+++ b/src/lib/certmap/sss_certmap.h
@@ -0,0 +1,155 @@
+/*
+ SSSD
+
+ Library for rule based certificate to user mapping
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SSS_CERTMAP_H_
+#define _SSS_CERTMAP_H_
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include <talloc.h>
+
+/**
+ * @defgroup sss_certmap Allow rule-based mapping of certificates to users
+ * Libsss_certmap provides a mechanism to map X509 certificate to users based
+ * on rules.
+ * @{
+ */
+
+/**
+ * Opaque type for the idmap context
+ */
+struct sss_certmap_ctx;
+
+/**
+ * Lowest priority of a rule
+ */
+#define SSS_CERTMAP_MIN_PRIO UINT32_MAX
+
+/**
+ * Typedef for external debug callback
+ */
+typedef void (sss_certmap_ext_debug)(void *pvt,
+ const char *file, long line,
+ const char *function,
+ const char *format, ...);
+/**
+ * @brief Initialize certmap context
+ *
+ * @param[in] mem_ctx Talloc memory context, may be NULL
+ * @param[in] debug Callback to handle debug output, may be NULL
+ * @param[in] debug_priv Private data for debugging callback, may be NULL
+ * @param[out] ctx New certmap context
+ *
+ * @return
+ * - 0: success
+ * - ENOMEM: failed to allocate internal Talloc context
+ * - EINVAL: ctx is NULL
+ */
+int sss_certmap_init(TALLOC_CTX *mem_ctx,
+ sss_certmap_ext_debug *debug, void *debug_priv,
+ struct sss_certmap_ctx **ctx);
+
+/**
+ * @brief Free certmap context
+ *
+ * @param[in] ctx certmap context previously initialized with
+ * @ref sss_certmap_init, may be NULL
+ */
+void sss_certmap_free_ctx(struct sss_certmap_ctx *ctx);
+
+/**
+ * @brief Add a rule to the certmap context
+ *
+ * @param[in] ctx certmap context previously initialized with
+ * @ref sss_certmap_init
+ * @param[in] priority priority of the rule, 0 is the hightest priority, the
+ * lowest is SSS_CERTMAP_MIN_PRIO
+ * @param[in] match_rule String with the matching rule
+ * @param[in] map_rule String with the mapping rule
+ * @param[in] domains NULL-terminated string array with a list of domains
+ * the rule should be valid for, i.e. only this domains
+ * should be searched for matching users
+ *
+ * @return
+ * - 0: success
+ */
+int sss_certmap_add_rule(struct sss_certmap_ctx *ctx,
+ uint32_t priority, const char *match_rule,
+ const char *map_rule, const char **domains);
+
+/**
+ * @brief Check if a certificate matches any of the applied rules
+ *
+ * @param[in] ctx certmap context previously initialized with
+ * @ref sss_certmap_init
+ * @param[in] der_cert binary blog with the DER encoded certificate
+ * @param[in] der_size size of the certificate blob
+ *
+ * @return
+ * - 0: certificate matches a rule
+ * - ENOENT: certificate does not match
+ * - EINVAL: internal error
+ */
+int sss_certmap_match_cert(struct sss_certmap_ctx *ctx,
+ const uint8_t *der_cert, size_t der_size);
+
+/**
+ * @brief Get the LDAP filter string for a certificate
+ *
+ * @param[in] ctx certmap context previously initialized with
+ * @ref sss_certmap_init
+ * @param[in] der_cert binary blog with the DER encoded certificate
+ * @param[in] der_size size of the certificate blob
+ * @param[out] filter LDAP filter string, caller should free the data by
+ * calling sss_certmap_free_filter_and_domains
+ * @param[out] domains NULL-terminated array of strings with the domains the
+ * rule applies, caller should free the data by calling
+ * sss_certmap_free_filter_and_domains
+ *
+ * @return
+ * - 0: certificate matches a rule
+ * - ENOENT: certificate does not match
+ * - EINVAL: internal error
+ */
+int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx,
+ const uint8_t *der_cert, size_t der_size,
+ char **filter, char ***domains);
+
+/**
+ * @brief Free data returned by @ref sss_certmap_get_search_filter
+ *
+ * @param[in] filter LDAP filter strings returned by
+ * sss_certmap_get_search_filter
+ * @param[in] domains string array of domains returned by
+ * sss_certmap_get_search_filter
+ */
+void sss_certmap_free_filter_and_domains(char *filter, char **domains);
+
+/**
+ * @}
+ */
+#endif /* _SSS_CERTMAP_H_ */
diff --git a/src/lib/certmap/sss_certmap.pc.in b/src/lib/certmap/sss_certmap.pc.in
new file mode 100644
index 000000000..f1a4432fc
--- /dev/null
+++ b/src/lib/certmap/sss_certmap.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: sss_certmap
+Description: SSS certificate mapping library
+Version: @VERSION@
+Libs: -L${libdir} -lsss_certmap
+Cflags:
+URL: https://pagure.io/SSSD/sssd/
diff --git a/src/lib/certmap/sss_certmap_attr_names.c b/src/lib/certmap/sss_certmap_attr_names.c
new file mode 100644
index 000000000..a28a46491
--- /dev/null
+++ b/src/lib/certmap/sss_certmap_attr_names.c
@@ -0,0 +1,107 @@
+/*
+ SSSD
+
+ Library for rule based certificate to user mapping - Attribute name
+ mapping for different implementations
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* NSS data taken from nss-utils:nss/lib/util/secoid.c and
+ * nss:nss/lib/certdb/alg1485.c */
+
+/* AD data taken from
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa376556%28v=vs.85%29.aspx
+ * and wine source code dlls/crypt32/oid.c  and include/wincrypt.h . */
+
+#include <stdbool.h>
+#include <string.h>
+#include <talloc.h>
+
+struct oid_attr_name_map {
+ bool nss_ad_differ;
+ const char *oid;
+ const char *nss;
+ const char *ad;
+} oid_attr_name_map[] = {
+ { false, "2.5.4.3", "CN", "CN"},
+ { true, "2.5.4.8", "ST", "S"},
+ { false, "2.5.4.10", "O", "O"},
+ { false, "2.5.4.11", "OU", "OU"},
+ { false, "2.5.4.46", "dnQualifier", "dnQualifier"},
+ { false, "2.5.4.6", "C", "C"},
+ { true, "2.5.4.5", "serialNumber", "SERIALNUMBER"},
+ { false, "2.5.4.7", "L", "L"},
+ { true, "2.5.4.12", "title", "T"},
+ { false, "2.5.4.4", "SN", "SN"},
+ { true, "2.5.4.42", "givenName", "G"},
+ { true, "2.5.4.43", "initials", "I"},
+ { true, "2.5.4.44", "generationQualifier", "OID.2.5.4.44"},
+ { false, "0.9.2342.19200300.100.1.25", "DC", "DC"},
+ { true, "0.9.2342.19200300.100.1.3", "MAIL", "OID,0.9.2342.19200300.100.1.3"},
+ { true, "0.9.2342.19200300.100.1.1", "UID", "OID.0.9.2342.19200300.100.1.1"},
+ { true, "2.5.4.13", "OID.2.5.4.13", "Description"},
+ { true, "2.5.4.16", "postalAddress", "OID.2.5.4.16"},
+ { true, "2.5.4.17", "postalCode", "PostalCode"},
+ { true, "2.5.4.18", "postOfficeBox", "POBox"},
+ { true, "2.5.4.51", "houseIdentifier", "OID.2.5.4.51"},
+ { false, "1.2.840.113549.1.9.1", "E", "E"},
+ { false, "2.5.4.9", "STREET", "STREET"},
+ { true, "2.5.4.65", "pseudonym", "OID.2.5.4.65"},
+ { true, "2.5.4.15", "businessCategory", "OID.2.5.4.15"},
+ { true, "2.5.4.41", "name", "OID.2.5.4.41"},
+
+ { false, NULL, NULL, NULL}
+};
+
+char *check_ad_attr_name(TALLOC_CTX *mem_ctx, const char *rdn)
+{
+ char *p;
+ size_t c;
+ size_t len;
+
+ if (rdn == NULL) {
+ return NULL;
+ }
+
+ p = strchr(rdn, '=');
+ if (p == NULL) {
+ return NULL;
+ }
+
+ len = p - rdn;
+ if (len == 0) {
+ return NULL;
+ }
+
+ for (c = 0; oid_attr_name_map[c].oid != NULL; c++) {
+ if (!oid_attr_name_map[c].nss_ad_differ) {
+ continue;
+ }
+
+ if (strlen(oid_attr_name_map[c].nss) != len
+ || strncmp(rdn, oid_attr_name_map[c].nss, len) != 0) {
+ continue;
+ }
+
+ return talloc_asprintf(mem_ctx, "%s%s", oid_attr_name_map[c].ad, p);
+ }
+
+ return NULL;
+}
diff --git a/src/lib/certmap/sss_certmap_int.h b/src/lib/certmap/sss_certmap_int.h
new file mode 100644
index 000000000..28f1c596c
--- /dev/null
+++ b/src/lib/certmap/sss_certmap_int.h
@@ -0,0 +1,187 @@
+/*
+ SSSD
+
+ Library for rule based certificate to user mapping
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/types.h>
+#include <regex.h>
+
+#ifndef __SSS_CERTMAP_INT_H__
+#define __SSS_CERTMAP_INT_H__
+
+#define CM_DEBUG(cm_ctx, format, ...) do { \
+ if (cm_ctx != NULL && cm_ctx->debug != NULL) { \
+ cm_ctx->debug(cm_ctx->debug_priv, __FILE__, __LINE__, __FUNCTION__, \
+ format, ##__VA_ARGS__); \
+ } \
+} while (0)
+
+#define DEFAULT_MATCH_RULE "<KU>digitalSignature<EKU>clientAuth"
+#define DEFAULT_MAP_RULE "LDAP:(userCertificate;binary={cert!bin})"
+
+enum san_opt {
+ SAN_OTHER_NAME = 0,
+ SAN_RFC822_NAME,
+ SAN_DNS_NAME,
+ SAN_X400_ADDRESS,
+ SAN_DIRECTORY_NAME,
+ SAN_EDIPART_NAME,
+ SAN_URI,
+ SAN_IP_ADDRESS,
+ SAN_REGISTERED_ID,
+ SAN_PKINIT,
+ SAN_NT,
+ SAN_PRINCIPAL,
+ SAN_STRING_OTHER_NAME,
+
+ SAN_END,
+ SAN_INVALID
+};
+
+/* KRB5 matching rule */
+enum relation_type {
+ relation_none = 0,
+ relation_and,
+ relation_or
+};
+
+struct component_list {
+ char *val;
+ regex_t regexp;
+ uint32_t ku;
+ const char **eku_oid_list;
+ enum san_opt san_opt;
+ char *str_other_name_oid;
+ uint8_t *bin_val;
+ size_t bin_val_len;
+ struct component_list *prev;
+ struct component_list *next;
+};
+
+struct krb5_match_rule {
+ enum relation_type r;
+ struct component_list *issuer;
+ struct component_list *subject;
+ struct component_list *ku;
+ struct component_list *eku;
+ struct component_list *san;
+};
+
+enum comp_type {
+ comp_none = 0,
+ comp_string,
+ comp_template
+};
+
+struct parsed_template {
+ char *name;
+ char *attr_name;
+ char *conversion;
+};
+
+struct ldap_mapping_rule_comp {
+ enum comp_type type;
+ char *val;
+ struct parsed_template *parsed_template;
+ struct ldap_mapping_rule_comp *prev;
+ struct ldap_mapping_rule_comp *next;
+};
+
+struct ldap_mapping_rule {
+ struct ldap_mapping_rule_comp *list;
+};
+
+struct match_map_rule {
+ uint32_t priority;
+ char *match_rule;
+ struct krb5_match_rule *parsed_match_rule;
+ char *map_rule;
+ struct ldap_mapping_rule *parsed_mapping_rule;
+ char **domains;
+ struct match_map_rule *prev;
+ struct match_map_rule *next;
+};
+
+struct priority_list {
+ uint32_t priority;
+ struct match_map_rule *rule_list;
+ struct priority_list *prev;
+ struct priority_list *next;
+};
+
+struct sss_certmap_ctx {
+ struct priority_list *prio_list;
+ sss_certmap_ext_debug *debug;
+ void *debug_priv;
+ struct ldap_mapping_rule *default_mapping_rule;
+};
+
+struct san_list {
+ enum san_opt san_opt;
+ char *val;
+ uint8_t *bin_val;
+ size_t bin_val_len;
+ char *other_name_oid;
+ char *short_name;
+ const char **rdn_list;
+ struct san_list *prev;
+ struct san_list *next;
+};
+
+/* key usage flags, see RFC 3280 section 4.2.1.3 */
+#define SSS_KU_DIGITAL_SIGNATURE 0x0080
+#define SSS_KU_NON_REPUDIATION 0x0040
+#define SSS_KU_KEY_ENCIPHERMENT 0x0020
+#define SSS_KU_DATA_ENCIPHERMENT 0x0010
+#define SSS_KU_KEY_AGREEMENT 0x0008
+#define SSS_KU_KEY_CERT_SIGN 0x0004
+#define SSS_KU_CRL_SIGN 0x0002
+#define SSS_KU_ENCIPHER_ONLY 0x0001
+#define SSS_KU_DECIPHER_ONLY 0x8000
+
+struct sss_cert_content {
+ const char *issuer_str;
+ const char **issuer_rdn_list;
+ const char *subject_str;
+ const char **subject_rdn_list;
+ uint32_t key_usage;
+ const char **extended_key_usage_oids;
+ struct san_list *san_list;
+
+ uint8_t *cert_der;
+ size_t cert_der_size;
+};
+
+int sss_cert_get_content(TALLOC_CTX *mem_ctx,
+ const uint8_t *der_blob, size_t der_size,
+ struct sss_cert_content **content);
+
+char *check_ad_attr_name(TALLOC_CTX *mem_ctx, const char *rdn);
+
+int parse_krb5_match_rule(struct sss_certmap_ctx *ctx,
+ const char *rule_start,
+ struct krb5_match_rule **match_rule);
+
+int parse_ldap_mapping_rule(struct sss_certmap_ctx *ctx,
+ const char *rule_start,
+ struct ldap_mapping_rule **mapping_rule);
+#endif /* __SSS_CERTMAP_INT_H__ */
diff --git a/src/lib/certmap/sss_certmap_krb5_match.c b/src/lib/certmap/sss_certmap_krb5_match.c
new file mode 100644
index 000000000..e40f17b8a
--- /dev/null
+++ b/src/lib/certmap/sss_certmap_krb5_match.c
@@ -0,0 +1,558 @@
+/*
+ SSSD
+
+ Library for rule based certificate to user mapping - KRB5 matching rules
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "util/cert.h"
+#include "util/crypto/sss_crypto.h"
+#include "lib/certmap/sss_certmap.h"
+#include "lib/certmap/sss_certmap_int.h"
+
+static bool is_dotted_decimal(const char *s, size_t len)
+{
+ size_t c = 0;
+ bool has_dot = false;
+
+ if (s == NULL || !isdigit(s[c++])) {
+ return false;
+ }
+
+ while ((len == 0 && s[c] != '\0') || (len != 0 && c < len)) {
+ if (s[c] != '.' && !isdigit(s[c])) {
+ return false;
+ }
+ if (!has_dot && s[c] == '.') {
+ has_dot = true;
+ }
+ c++;
+ }
+
+ return (has_dot && isdigit(s[c - 1]));
+}
+
+static int component_list_destructor(void *data)
+{
+ struct component_list *comp = talloc_get_type(data, struct component_list);
+
+ if (comp != NULL) {
+ regfree(&(comp->regexp));
+ }
+
+ return 0;
+}
+
+/*
+ * The syntax of the MIT Kerberos style matching rules is:
+ * [KRB5:][relation-operator]component-rule ...
+ *
+ * where:
+ *
+ * relation-operator
+ * can be either &&, meaning all component rules must match, or ||,
+ * meaning only one component rule must match. The default is &&.
+ *
+ * component-rule
+ * can be one of the following. Note that there is no punctuation or whitespace between component rules.
+ * <SUBJECT>regular-expression
+ * <ISSUER>regular-expression
+ * <SAN>regular-expression
+ * <EKU>extended-key-usage
+ * <KU>key-usage
+ *
+ * see man sss-certmap for more details
+ *
+ */
+
+static int get_comp_value(TALLOC_CTX *mem_ctx,
+ struct sss_certmap_ctx *ctx,
+ const char **cur,
+ struct component_list **_comp)
+
+{
+ struct component_list *comp = NULL;
+ const char *end;
+ int ret;
+
+ comp = talloc_zero(mem_ctx, struct component_list);
+ if (comp == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_set_destructor((TALLOC_CTX *) comp, component_list_destructor);
+
+ end = strchr(*cur, '<');
+
+ if (end == NULL) {
+ comp->val = talloc_strdup(comp, *cur);
+ } else {
+ comp->val = talloc_strndup(comp, *cur, end - *cur);
+ }
+ if (comp->val == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ if (*(comp->val) == '\0') {
+ CM_DEBUG(ctx, "Missing component value.");
+ ret = EINVAL;
+ goto done;
+ }
+
+ *cur += strlen(comp->val);
+ *_comp = comp;
+ ret = 0;
+
+done:
+ if (ret != 0) {
+ talloc_free(comp);
+ }
+
+ return ret;
+}
+
+static int parse_krb5_get_eku_value(TALLOC_CTX *mem_ctx,
+ struct sss_certmap_ctx *ctx,
+ const char **cur,
+ struct component_list **_comp)
+{
+ struct component_list *comp = NULL;
+ int ret;
+ char **eku_list;
+ size_t c;
+ size_t k;
+ const char *o;
+ size_t e = 0;
+ int eku_list_size;
+
+ struct ext_key_usage {
+ const char *name;
+ const char *oid;
+ } ext_key_usage[] = {
+ /* RFC 3280 section 4.2.1.13 */
+ {"serverAuth", "1.3.6.1.5.5.7.3.1"},
+ {"clientAuth", "1.3.6.1.5.5.7.3.2"},
+ {"codeSigning", "1.3.6.1.5.5.7.3.3"},
+ {"emailProtection", "1.3.6.1.5.5.7.3.4"},
+ {"timeStamping", "1.3.6.1.5.5.7.3.8"},
+ {"OCSPSigning", "1.3.6.1.5.5.7.3.9"},
+
+ /* RFC 4556 section 3.2.2 */
+ {"KPClientAuth", "1.3.6.1.5.2.3.4"},
+ {"pkinit", "1.3.6.1.5.2.3.4"},
+
+ /* https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography*/
+ {"msScLogin", "1.3.6.1.4.1.311.20.2.2"},
+
+ {NULL ,0}
+ };
+
+ ret = get_comp_value(mem_ctx, ctx, cur, &comp);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to parse regexp.");
+ goto done;
+ }
+
+ ret = split_on_separator(mem_ctx, comp->val, ',', true, true,
+ &eku_list, &eku_list_size);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to split list.");
+ goto done;
+ }
+
+ for (c = 0; eku_list[c] != NULL; c++) {
+ for (k = 0; ext_key_usage[k].name != NULL; k++) {
+CM_DEBUG(ctx, "[%s][%s].", eku_list[c], ext_key_usage[k].name);
+ if (strcasecmp(eku_list[c], ext_key_usage[k].name) == 0) {
+ if (comp->eku_oid_list == NULL) {
+ comp->eku_oid_list = talloc_zero_array(comp, const char *,
+ eku_list_size + 1);
+ if (comp->eku_oid_list == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list,
+ ext_key_usage[k].oid);
+ if (comp->eku_oid_list[e] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ e++;
+ break;
+ }
+ }
+
+ if (ext_key_usage[k].name == NULL) {
+ /* check for an dotted-decimal OID */
+ if (*(eku_list[c]) != '.') {
+ o = eku_list[c];
+ if (is_dotted_decimal(o, 0)) {
+ /* looks like a OID, only '.' and digits */
+ comp->eku_oid_list[e] = talloc_strdup(comp->eku_oid_list,
+ eku_list[c]);
+ if (comp->eku_oid_list[e] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ e++;
+ continue;
+ }
+ }
+ CM_DEBUG(ctx, "No matching extended key usage found.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *_comp = comp;
+ } else {
+ talloc_free(comp);
+ }
+
+ return ret;
+}
+
+static int parse_krb5_get_ku_value(TALLOC_CTX *mem_ctx,
+ struct sss_certmap_ctx *ctx,
+ const char **cur,
+ struct component_list **_comp)
+{
+ struct component_list *comp = NULL;
+ int ret;
+ char **ku_list;
+ size_t c;
+ size_t k;
+
+ struct key_usage {
+ const char *name;
+ uint32_t flag;
+ } key_usage[] = {
+ {"digitalSignature" , SSS_KU_DIGITAL_SIGNATURE},
+ {"nonRepudiation" , SSS_KU_NON_REPUDIATION},
+ {"keyEncipherment" , SSS_KU_KEY_ENCIPHERMENT},
+ {"dataEncipherment" , SSS_KU_DATA_ENCIPHERMENT},
+ {"keyAgreement" , SSS_KU_KEY_AGREEMENT},
+ {"keyCertSign" , SSS_KU_KEY_CERT_SIGN},
+ {"cRLSign" , SSS_KU_CRL_SIGN},
+ {"encipherOnly" , SSS_KU_ENCIPHER_ONLY},
+ {"decipherOnly" , SSS_KU_DECIPHER_ONLY},
+ {NULL ,0}
+ };
+
+
+ ret = get_comp_value(mem_ctx, ctx, cur, &comp);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to get value.");
+ goto done;
+ }
+
+ ret = split_on_separator(mem_ctx, comp->val, ',', true, true,
+ &ku_list, NULL);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to split list.");
+ goto done;
+ }
+
+ for (c = 0; ku_list[c] != NULL; c++) {
+ for (k = 0; key_usage[k].name != NULL; k++) {
+ if (strcasecmp(ku_list[c], key_usage[k].name) == 0) {
+ comp->ku |= key_usage[k].flag;
+ break;
+ }
+ }
+
+ if (key_usage[k].name == NULL) {
+ /* FIXME: add check for numerical ku */
+ CM_DEBUG(ctx, "No matching key usage found.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *_comp = comp;
+ } else {
+ talloc_free(comp);
+ }
+
+ return ret;
+}
+
+static int parse_krb5_get_component_value(TALLOC_CTX *mem_ctx,
+ struct sss_certmap_ctx *ctx,
+ const char **cur,
+ struct component_list **_comp)
+{
+ struct component_list *comp = NULL;
+ int ret;
+
+ ret = get_comp_value(mem_ctx, ctx, cur, &comp);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to parse regexp.");
+ goto done;
+ }
+
+ ret = regcomp(&(comp->regexp), comp->val, REG_EXTENDED);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to parse regexp.");
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *_comp = comp;
+ } else {
+ talloc_free(comp);
+ }
+
+ return ret;
+}
+
+struct san_name {
+ const char *name;
+ enum san_opt san_opt;
+ bool is_string;
+} san_names[] = {
+ /* https://www.ietf.org/rfc/rfc3280.txt section 4.2.1.7 */
+ {"otherName", SAN_OTHER_NAME, false},
+ {"rfc822Name", SAN_RFC822_NAME,true},
+ {"dNSName", SAN_DNS_NAME, true},
+ {"x400Address", SAN_X400_ADDRESS, false},
+ {"directoryName", SAN_DIRECTORY_NAME, true},
+ {"ediPartyName", SAN_EDIPART_NAME, false},
+ {"uniformResourceIdentifier", SAN_URI, true},
+ {"iPAddress", SAN_IP_ADDRESS, true},
+ {"registeredID", SAN_REGISTERED_ID, true},
+ /* https://www.ietf.org/rfc/rfc4556.txt section 3.2.2 */
+ {"pkinitSAN", SAN_PKINIT, true},
+ /* https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography */
+ {"ntPrincipalName", SAN_NT, true},
+ /* both previous principal types */
+ {"Principal", SAN_PRINCIPAL, true},
+ {"stringOtherName", SAN_STRING_OTHER_NAME, true},
+ {NULL, SAN_END, false}
+};
+
+static int parse_krb5_get_san_option(TALLOC_CTX *mem_ctx,
+ struct sss_certmap_ctx *ctx,
+ const char **cur,
+ enum san_opt *option,
+ char **str_other_name_oid)
+{
+ char *end;
+ size_t c;
+ size_t len;
+
+ end = strchr(*cur, '>');
+ if (end == NULL) {
+ CM_DEBUG(ctx, "Failed to parse SAN option.");
+ return EINVAL;
+ }
+
+ len = end - *cur;
+
+ if (len == 0) {
+ c= SAN_PRINCIPAL;
+ } else {
+ for (c = 0; san_names[c].name != NULL; c++) {
+ if (strncasecmp(*cur, san_names[c].name, len) == 0) {
+ break;
+ }
+ }
+ if (san_names[c].name == NULL) {
+ if (is_dotted_decimal(*cur, len)) {
+ c = SAN_STRING_OTHER_NAME;
+ *str_other_name_oid = talloc_strndup(mem_ctx, *cur, len);
+ if (*str_other_name_oid == NULL) {
+ CM_DEBUG(ctx, "talloc_strndup failed.");
+ return ENOMEM;
+ }
+ } else {
+ CM_DEBUG(ctx, "Unknown SAN option.");
+ return EINVAL;
+ }
+ }
+ }
+
+ *option = san_names[c].san_opt;
+ *cur = end + 1;
+
+ return 0;
+}
+
+static int parse_krb5_get_san_value(TALLOC_CTX *mem_ctx,
+ struct sss_certmap_ctx *ctx,
+ const char **cur,
+ struct component_list **_comp)
+{
+ struct component_list *comp = NULL;
+ enum san_opt san_opt = SAN_PRINCIPAL;
+ int ret;
+ char *str_other_name_oid = NULL;
+
+ if (*(*cur - 1) == ':') {
+ ret = parse_krb5_get_san_option(mem_ctx, ctx, cur, &san_opt,
+ &str_other_name_oid);
+ if (ret != 0) {
+ goto done;
+ }
+ }
+
+ if (san_names[san_opt].is_string) {
+ ret = parse_krb5_get_component_value(mem_ctx, ctx, cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+ } else {
+ ret = get_comp_value(mem_ctx, ctx, cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+
+ if (comp->val != NULL) {
+ comp->bin_val = sss_base64_decode(comp, comp->val,
+ &comp->bin_val_len);
+ /* for some reasons the NSS version of sss_base64_decode might
+ * return a non-NULL value on error but len is still 0, so better
+ * check both. */
+ if (comp->bin_val == NULL || comp->bin_val_len == 0) {
+ CM_DEBUG(ctx, "Base64 decode failed.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+ }
+ comp->san_opt = san_opt;
+
+done:
+ if (ret == 0) {
+ comp->str_other_name_oid = talloc_steal(comp, str_other_name_oid);
+ *_comp = comp;
+ } else {
+ talloc_free(comp);
+ talloc_free(str_other_name_oid);
+ }
+
+ return ret;
+}
+
+int parse_krb5_match_rule(struct sss_certmap_ctx *ctx,
+ const char *rule_start,
+ struct krb5_match_rule **match_rule)
+{
+ const char *cur;
+ struct krb5_match_rule *rule;
+ struct component_list *comp;
+ int ret;
+
+ rule = talloc_zero(ctx, struct krb5_match_rule);
+ if (rule == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cur = rule_start;
+ /* check relation */
+ if (strncmp(cur, "&&", 2) == 0) {
+ rule->r = relation_and;
+ cur += 2;
+ } else if (strncmp(cur, "||", 2) == 0) {
+ rule->r = relation_or;
+ cur += 2;
+ } else {
+ rule->r = relation_and;
+ }
+
+ while (*cur != '\0') {
+ /* new component must start with '<' */
+ if (*cur != '<') {
+ CM_DEBUG(ctx, "Invalid KRB5 matching rule.");
+ ret = EINVAL;
+ goto done;
+ }
+ cur++;
+
+ if (strncmp(cur, "ISSUER>", 7) == 0) {
+ cur += 7;
+ ret = parse_krb5_get_component_value(rule, ctx, &cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(rule->issuer, comp);
+ } else if (strncmp(cur, "SUBJECT>", 8) == 0) {
+ cur += 8;
+ ret = parse_krb5_get_component_value(rule, ctx, &cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(rule->subject, comp);
+ } else if (strncmp(cur, "KU>", 3) == 0) {
+ cur += 3;
+ ret = parse_krb5_get_ku_value(rule, ctx, &cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(rule->ku, comp);
+ } else if (strncmp(cur, "EKU>", 4) == 0) {
+ cur += 4;
+ ret = parse_krb5_get_eku_value(rule, ctx, &cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(rule->eku, comp);
+ } else if (strncmp(cur, "SAN>", 4) == 0
+ || strncmp(cur, "SAN:", 4) == 0) {
+ cur += 4;
+ ret = parse_krb5_get_san_value(rule, ctx, &cur, &comp);
+ if (ret != 0) {
+ goto done;
+ }
+ DLIST_ADD(rule->san, comp);
+ } else {
+ CM_DEBUG(ctx, "Invalid KRB5 matching rule.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *match_rule = rule;
+ } else {
+ talloc_free(rule);
+ }
+
+ return ret;
+}
diff --git a/src/lib/certmap/sss_certmap_ldap_mapping.c b/src/lib/certmap/sss_certmap_ldap_mapping.c
new file mode 100644
index 000000000..c64c05b31
--- /dev/null
+++ b/src/lib/certmap/sss_certmap_ldap_mapping.c
@@ -0,0 +1,367 @@
+/*
+ SSSD
+
+ Library for rule based certificate to user mapping - LDAP mapping rules
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "util/cert.h"
+#include "util/crypto/sss_crypto.h"
+#include "lib/certmap/sss_certmap.h"
+#include "lib/certmap/sss_certmap_int.h"
+struct template_table {
+ const char *name;
+ const char **attr_name;
+ const char **conversion;
+};
+
+const char *empty[] = {NULL};
+const char *name_attr[] = {"short_name", NULL};
+const char *x500_conv[] = {"ad_x500", "ad", "ad_ldap",
+ "nss_x500", "nss", "nss_ldap", NULL};
+const char *bin_conv[] = {"bin", "base64", NULL};
+
+struct template_table template_table[] = {
+ {"issuer_dn", empty, x500_conv},
+ {"subject_dn", empty, x500_conv},
+ {"cert", empty, bin_conv},
+ {"subject_rfc822_name", name_attr, empty},
+ {"subject_dns_name", name_attr, empty},
+ {"subject_x400_address", empty, empty},
+ {"subject_directory_name", empty, empty},
+ {"subject_ediparty_name", empty, empty},
+ {"subject_uri", empty, empty},
+ {"subject_ip_address", empty, empty},
+ {"subject_registered_id", empty, empty},
+ {"subject_pkinit_principal", name_attr, empty},
+ {"subject_nt_principal", name_attr, empty},
+ {"subject_principal", name_attr, empty},
+ {NULL, NULL, NULL}};
+
+static int check_parsed_template(struct sss_certmap_ctx *ctx,
+ struct parsed_template *parsed)
+{
+ size_t n;
+ size_t a;
+ size_t c;
+ bool attr_name_valid = false;
+ bool conversion_valid = false;
+
+ for (n = 0; template_table[n].name != NULL; n++) {
+ if (strcmp(template_table[n].name, parsed->name) != 0) {
+ continue;
+ }
+
+ if (parsed->attr_name != NULL) {
+ for (a = 0; template_table[n].attr_name[a] != NULL; a++) {
+ if (strcmp(template_table[n].attr_name[a],
+ parsed->attr_name) == 0) {
+ attr_name_valid = true;
+ break;
+ }
+ }
+ } else {
+ attr_name_valid = true;
+ }
+
+ if (parsed->conversion != NULL) {
+ for (c = 0; template_table[n].conversion[c] != NULL; c++) {
+ if (strcmp(template_table[n].conversion[c],
+ parsed->conversion) == 0) {
+ conversion_valid = true;
+ break;
+ }
+ }
+ } else {
+ conversion_valid = true;
+ }
+
+ if (attr_name_valid && conversion_valid) {
+ return 0;
+ }
+ }
+
+ return EINVAL;
+}
+
+static int parse_template(TALLOC_CTX *mem_ctx, struct sss_certmap_ctx *ctx,
+ const char *template,
+ struct parsed_template **parsed_template)
+{
+ int ret;
+ struct parsed_template *parsed = NULL;
+ const char *dot;
+ const char *excl;
+ const char *p;
+
+ parsed = talloc_zero(mem_ctx, struct parsed_template);
+ if (parsed == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ dot = strchr(template, '.');
+ if (dot != NULL) {
+ p = strchr(dot + 1, '.');
+ if (p != NULL) {
+ CM_DEBUG(ctx, "Only one '.' allowed in template.");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (dot == template) {
+ CM_DEBUG(ctx, "Missing name in template.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ excl = strchr(template, '!');
+ if (excl != NULL) {
+ p = strchr(excl + 1, '!');
+ if (p != NULL) {
+ CM_DEBUG(ctx, "Only one '!' allowed in template.");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (excl == template) {
+ CM_DEBUG(ctx, "Missing name in template.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ if (excl != NULL && excl[1] != '\0') {
+ parsed->conversion = talloc_strdup(parsed, excl + 1);
+ if (parsed->conversion == NULL) {
+ CM_DEBUG(ctx, "Memory allocation failed.");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (dot != NULL && dot[1] != '\0' && dot[1] != '!') {
+ if (excl == NULL) {
+ parsed->attr_name = talloc_strdup(parsed, dot + 1);
+ } else {
+ parsed->attr_name = talloc_strndup(parsed, dot + 1,
+ (excl - dot - 1));
+ }
+ if (parsed->attr_name == NULL) {
+ CM_DEBUG(ctx, "Memory allocation failed.");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (dot != NULL) {
+ parsed->name = talloc_strndup(parsed, template, (dot - template));
+ } else if (excl != NULL) {
+ parsed->name = talloc_strndup(parsed, template, (excl - template));
+ } else {
+ parsed->name = talloc_strdup(parsed, template);
+ }
+ if (parsed->name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = check_parsed_template(ctx, parsed);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Parse template invalid.");
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *parsed_template = parsed;
+ } else {
+ talloc_free(parsed);
+ }
+
+ return ret;
+}
+
+static int add_comp(struct sss_certmap_ctx *ctx, struct ldap_mapping_rule *rule,
+ const char *string, enum comp_type type)
+{
+ int ret;
+ struct ldap_mapping_rule_comp *comp;
+
+ comp = talloc_zero(rule, struct ldap_mapping_rule_comp);
+ if (comp == NULL) {
+ return ENOMEM;
+ }
+
+ comp->type = type;
+ comp->val = talloc_strdup(comp, string);
+ if (comp->val == NULL) {
+ talloc_free(comp);
+ return ENOMEM;
+ }
+
+ if (type == comp_template) {
+ ret = parse_template(comp, ctx, string, &comp->parsed_template);
+ if (ret != 0) {
+ talloc_free(comp);
+ return ret;
+ }
+ }
+
+ DLIST_ADD_END(rule->list, comp, struct ldap_mapping_rule_comp *);
+
+ return 0;
+}
+
+static int add_string(struct sss_certmap_ctx *ctx,
+ struct ldap_mapping_rule *rule, const char *string)
+{
+ return add_comp(ctx, rule, string, comp_string);
+}
+
+static int add_template(struct sss_certmap_ctx *ctx,
+ struct ldap_mapping_rule *rule, const char *string)
+{
+ return add_comp(ctx, rule, string, comp_template);
+}
+
+int parse_ldap_mapping_rule(struct sss_certmap_ctx *ctx,
+ const char *rule_start,
+ struct ldap_mapping_rule **mapping_rule)
+{
+ size_t c;
+ const char *cur;
+ char *tmp_string = NULL;
+ size_t tmp_string_size;
+ struct ldap_mapping_rule *rule = NULL;
+ int ret;
+ bool in_template = false;
+
+ rule = talloc_zero(ctx, struct ldap_mapping_rule);
+ if (rule == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tmp_string_size = strlen(rule_start) + 1;
+ tmp_string = talloc_zero_size(ctx, tmp_string_size);
+ if (tmp_string == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cur = rule_start;
+ c = 0;
+
+ while (*cur != '\0') {
+ if (c > tmp_string_size) {
+ CM_DEBUG(ctx, "Cannot parse mapping rule.");
+ ret = EIO;
+ goto done;
+ }
+ switch (*cur) {
+ case '{':
+ if (in_template) {
+ CM_DEBUG(ctx, "'{' not allowed in templates.");
+ ret = EINVAL;
+ goto done;
+ }
+ if (cur[1] == '{') {
+ /* Add only a single '{' to the output */
+ tmp_string[c] = '{';
+ c++;
+ cur += 2;
+ } else {
+ if (c != 0) {
+ ret = add_string(ctx, rule, tmp_string);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to add string.");
+ ret = EINVAL;
+ goto done;
+ }
+ memset(tmp_string, 0, tmp_string_size);
+ c = 0;
+ }
+ cur++;
+ in_template = true;
+ }
+ break;
+ case '}':
+ if (cur[1] == '}') {
+ if (in_template) {
+ CM_DEBUG(ctx, "'}}' not allowed in templates.");
+ ret = EINVAL;
+ goto done;
+ } else {
+ /* Add only a single '}' to the output */
+ tmp_string[c] = '}';
+ c++;
+ cur += 2;
+ }
+ } else {
+ ret = add_template(ctx, rule, tmp_string);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to add template.");
+ ret = EINVAL;
+ goto done;
+ }
+ memset(tmp_string, 0, tmp_string_size);
+ c = 0;
+ cur++;
+ in_template = false;
+ }
+ break;
+ default:
+ tmp_string[c] = *cur;
+ c++;
+ cur++;
+ }
+ }
+ if (in_template) {
+ CM_DEBUG(ctx, "Rule ended inside template.");
+ ret = EINVAL;
+ goto done;
+ }
+ if (c != 0) {
+ ret = add_string(ctx, rule, tmp_string);
+ if (ret != 0) {
+ CM_DEBUG(ctx, "Failed to add string.");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+done:
+ if (ret == 0) {
+ *mapping_rule = rule;
+ } else {
+ talloc_free(rule);
+ }
+
+ talloc_free(tmp_string);
+
+ return ret;
+}
diff --git a/src/man/Makefile.am b/src/man/Makefile.am
index 215ce693b..142d6e274 100644
--- a/src/man/Makefile.am
+++ b/src/man/Makefile.am
@@ -59,7 +59,7 @@ man_MANS = \
sss_useradd.8 sss_userdel.8 sss_usermod.8 \
sss_groupadd.8 sss_groupdel.8 sss_groupmod.8 \
sssd.8 sssd.conf.5 sssd-ldap.5 \
- sssd-krb5.5 sssd-simple.5 \
+ sssd-krb5.5 sssd-simple.5 sss-certmap.5 \
sssd_krb5_locator_plugin.8 sss_groupshow.8 \
pam_sss.8 sss_obfuscate.8 sss_cache.8 sss_debuglevel.8 sss_seed.8 \
sss_override.8 idmap_sss.8 sssctl.8 \
diff --git a/src/man/po/po4a.cfg b/src/man/po/po4a.cfg
index ffcf9a279..d1f6ac39f 100644
--- a/src/man/po/po4a.cfg
+++ b/src/man/po/po4a.cfg
@@ -6,6 +6,7 @@
[type:docbook] pam_sss.8.xml $lang:$(builddir)/$lang/pam_sss.8.xml
[type:docbook] sssd_krb5_locator_plugin.8.xml $lang:$(builddir)/$lang/sssd_krb5_locator_plugin.8.xml
[type:docbook] sssd-simple.5.xml $lang:$(builddir)/$lang/sssd-simple.5.xml
+[type:docbook] sss-certmap.5.xml $lang:$(builddir)/$lang/sss-certmap.5.xml
[type:docbook] sssd-ipa.5.xml $lang:$(builddir)/$lang/sssd-ipa.5.xml
[type:docbook] sssd-ad.5.xml $lang:$(builddir)/$lang/sssd-ad.5.xml
[type:docbook] sssd-sudo.5.xml $lang:$(builddir)/$lang/sssd-sudo.5.xml
diff --git a/src/man/sss-certmap.5.xml b/src/man/sss-certmap.5.xml
new file mode 100644
index 000000000..bbe68509f
--- /dev/null
+++ b/src/man/sss-certmap.5.xml
@@ -0,0 +1,600 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<reference>
+<title>SSSD Manual pages</title>
+<refentry>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/upstream.xml" />
+
+ <refmeta>
+ <refentrytitle>sss-certmap</refentrytitle>
+ <manvolnum>5</manvolnum>
+ <refmiscinfo class="manual">File Formats and Conventions</refmiscinfo>
+ </refmeta>
+
+ <refnamediv id='name'>
+ <refname>sss-certmap</refname>
+ <refpurpose>SSSD Certificate Matching and Mapping Rules</refpurpose>
+ </refnamediv>
+
+ <refsect1 id='description'>
+ <title>DESCRIPTION</title>
+ <para>
+ The manual page describes the rules which can be used by SSSD and
+ other components to match X.509 certificates and map them to
+ accounts.
+ </para>
+ <para>
+ Each rule has four components, a <quote>priority</quote>, a
+ <quote>matching rule</quote>, a <quote>mapping rule</quote> and a
+ <quote>domain list</quote>. All components are optional. A missing
+ <quote>priority</quote> will add the rule with the lowest priority.
+ The default <quote>matching rule</quote> will match certificates with
+ the digitalSignature key usage and clientAuth extended key usage. If
+ the <quote>mapping rule</quote> is empty the certificates will be
+ searched in the userCertificate attribute as DER encoded binary. If
+ no domains are given only the local domain will be searched.
+ </para>
+ </refsect1>
+
+ <refsect1 id='components'>
+ <title>RULE COMPONENTS</title>
+ <refsect2 id='priority'>
+ <title>PRIORITY</title>
+ <para>
+ The rules are process by priority while the number '0' (zero)
+ indicates the highest priority. The higher the number the lower is
+ the priority. A missing value indicates the lowest priority.
+ </para>
+ <para>
+ Internally the priority is treated as unsigned 32bit integer, using
+ a priority value larger than 4294967295 will cause an error.
+ </para>
+ </refsect2>
+ <refsect2 id='match'>
+ <title>MATCHING RULE</title>
+ <para>
+ The matching rule is used to select a certificate to which the
+ mapping rule should be applied. It uses a system similar to the one
+ used by <quote>pkinit_cert_match</quote> option of MIT Kerberos. It
+ consists of a keyword enclosed by '&lt;' and '&gt;' which identified
+ a certain part of the certificate and a pattern which should be
+ found for the rule to match. Multiple keyword pattern pairs can be
+ either joined with '&amp;&amp;' (and) or '&#124;&#124;' (or).
+ </para>
+ <para>
+ The available options are:
+ <variablelist>
+ <varlistentry>
+ <term>&lt;SUBJECT&gt;regular-expression</term>
+ <listitem>
+ <para>
+ With this a part or the whole subject name of the
+ certificate can be matched. For the matching POSIX
+ Extended Regular Expression syntax is used, see regex(7)
+ for details.
+ </para>
+ <para>
+ For the matching the subject name stored in the
+ certificate in DER encoded ASN.1 is converted into a
+ string according to RFC 4514. This means the most
+ specific name component comes first. Please note that
+ not all possible attribute names are covered by RFC
+ 4514. The names included are 'CN', 'L', 'ST', 'O',
+ 'OU', 'C', 'STREET', 'DC' and 'UID'. Other attribute
+ names might be shown differently on different platform
+ and by different tools. To avoid confusion those
+ attribute names are best not used or covered by a
+ suitable regular-expression.
+ </para>
+ <para>
+ Example: &lt;SUBJECT&gt;.*,DC=MY,DC=DOMAIN
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;ISSUER&gt;regular-expression</term>
+ <listitem>
+ <para>
+ With this a part or the whole issuer name of the
+ certificate can be matched. All comments for
+ &lt;SUBJECT&gt; apply her as well.
+ </para>
+ <para>
+ Example: &lt;ISSUER&gt;^CN=My-CA,DC=MY,DC=DOMAIN$
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;KU&gt;key-usage</term>
+ <listitem>
+ <para>
+ This option can be used to specify which key usage
+ values the certificate should have. The following value
+ can be used in a comma separate list:
+ <itemizedlist>
+ <listitem><para>digitalSignature</para></listitem>
+ <listitem><para>nonRepudiation</para></listitem>
+ <listitem><para>keyEncipherment</para></listitem>
+ <listitem><para>dataEncipherment</para></listitem>
+ <listitem><para>keyAgreement</para></listitem>
+ <listitem><para>keyCertSign</para></listitem>
+ <listitem><para>cRLSign</para></listitem>
+ <listitem><para>encipherOnly</para></listitem>
+ <listitem><para>decipherOnly</para></listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ A numerical value in the range of a 32bit unsigned
+ integer can be used as well to cover special use cases.
+ </para>
+ <para>
+ Example: &lt;KU&gt;digitalSignature,keyEncipherment
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;EKU&gt;extended-key-usage</term>
+ <listitem>
+ <para>
+ This option can be used to specify which extended key
+ usage the certificate should have. The following value
+ can be used in a comma separated list:
+ <itemizedlist>
+ <listitem><para>serverAuth</para></listitem>
+ <listitem><para>clientAuth</para></listitem>
+ <listitem><para>codeSigning</para></listitem>
+ <listitem><para>emailProtection</para></listitem>
+ <listitem><para>timeStamping</para></listitem>
+ <listitem><para>OCSPSigning</para></listitem>
+ <listitem><para>KPClientAuth</para></listitem>
+ <listitem><para>pkinit</para></listitem>
+ <listitem><para>msScLogin</para></listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ Extended key usages which are not listed above can be
+ specified with their OID in dotted-decimal notation.
+ </para>
+ <para>
+ Example: &lt;EKU&gt;clientAuth,1.3.6.1.5.2.3.4
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN&gt;regular-expression</term>
+ <listitem>
+ <para>
+ To be compatible with the usage of MIT Kerberos this
+ option will match the Kerberos principals in the PKINIT
+ or AD NT Principal SAN as &lt;SAN:Principal&gt; does.
+ </para>
+ <para>
+ Example: &lt;SAN&gt;.*@MY\.REALM
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN:Principal&gt;regular-expression</term>
+ <listitem>
+ <para>
+ Match the Kerberos principals in the PKINIT or AD NT
+ Principal SAN.
+ </para>
+ <para>
+ Example: &lt;SAN:Principal&gt;.*@MY\.REALM
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN:ntPrincipalName&gt;regular-expression</term>
+ <listitem>
+ <para>
+ Match the Kerberos principals from the AD NT Principal
+ SAN.
+ </para>
+ <para>
+ Example: &lt;SAN:ntPrincipalName&gt;.*@MY.AD.REALM
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN:pkinit&gt;regular-expression</term>
+ <listitem>
+ <para>
+ Match the Kerberos principals from the PKINIT SAN.
+ </para>
+ <para>
+ Example: &lt;SAN:ntPrincipalName&gt;.*@MY\.PKINIT\.REALM
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN:dotted-decimal-oid&gt;regular-expression</term>
+ <listitem>
+ <para>
+ Take the value of the otherName SAN component given by
+ the OID in dotted-decimal notation, interpret it as
+ string and try to match it against the regular
+ expression.
+ </para>
+ <para>
+ Example: &lt;SAN:1.2.3.4&gt;test
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN:otherName&gt;base64-string</term>
+ <listitem>
+ <para>
+ Do a binary match with the base64 encoded blob against
+ all otherName SAN components. With this option it is
+ possible to match against custom otherName components
+ with special encodings which could not be treated as
+ strings.
+ </para>
+ <para>
+ Example: &lt;SAN:otherName&gt;MTIz
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN:rfc822Name&gt;regular-expression</term>
+ <listitem>
+ <para>
+ Match the value of the rfc822Name SAN.
+ </para>
+ <para>
+ Example: &lt;SAN:rfc822Name&gt;.*@email\.domain
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN:dNSName&gt;regular-expression</term>
+ <listitem>
+ <para>
+ Match the value of the dNSName SAN.
+ </para>
+ <para>
+ Example: &lt;SAN:dNSName&gt;.*\.my\.dns\.domain
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN:x400Address&gt;base64-string</term>
+ <listitem>
+ <para>
+ Binary match the value of the x400Address SAN.
+ </para>
+ <para>
+ Example: &lt;SAN:x400Address&gt;MTIz
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN:directoryName&gt;regular-expression</term>
+ <listitem>
+ <para>
+ Match the value of the directoryName SAN. The same
+ comments as given for &lt;ISSUER&gt; and &lt;SUBJECT&gt;
+ apply here as well.
+ </para>
+ <para>
+ Example: &lt;SAN:directoryName&gt;.*,DC=com
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN:ediPartyName&gt;base64-string</term>
+ <listitem>
+ <para>
+ Binary match the value of the ediPartyName SAN.
+ </para>
+ <para>
+ Example: &lt;SAN:ediPartyName&gt;MTIz
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN:uniformResourceIdentifier&gt;regular-expression</term>
+ <listitem>
+ <para>
+ Match the value of the uniformResourceIdentifier SAN.
+ </para>
+ <para>
+ Example: &lt;SAN:uniformResourceIdentifier&gt;URN:.*
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN:iPAddress&gt;regular-expression</term>
+ <listitem>
+ <para>
+ Match the value of the iPAddress SAN.
+ </para>
+ <para>
+ Example: &lt;SAN:iPAddress&gt;192\.168\..*
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;SAN:registeredID&gt;regular-expression</term>
+ <listitem>
+ <para>
+ Match the value of the registeredID SAN as
+ dotted-decimal string.
+ </para>
+ <para>
+ Example: &lt;SAN:registeredID&gt;1\.2\.3\..*
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect2>
+ <refsect2 id='map'>
+ <title>MAPPING RULE</title>
+ <para>
+ The mapping rule is used to associate a certificate with one or more
+ accounts. A Smartcard with the certificate and the matching private
+ key can then be used to authenticate as one of those accounts.
+ </para>
+ <para>
+ Currently SSSD basically only supports LDAP to lookup user
+ information (the exception is the proxy provider which is not of
+ relevance here). Because of this the mapping rule is based on LDAP
+ search filter syntax with templates to add certificate content to
+ the filter. It is expected that the filter will only contain the
+ specific data needed for the mapping an that the caller will embed
+ it in another filter to do the actual search. Because of this the
+ filter string should start and stop with '(' and ')' respectively.
+ </para>
+ <para>
+ In general it is recommended to use attributes from the certificate
+ and add them to special attributes to the LDAP user object. E.g. the
+ 'altSecurityIdentities' attribute in AD or the 'ipaCertMapData'
+ attribute for IPA can be used.
+ </para>
+ <para>
+ This should be preferred to read user specific data from the
+ certificate like e.g. an email address and search for it in the LDAP
+ server. The reason is that the user specific data in LDAP might
+ change for various reasons would would break the mapping. On the
+ other hand it would be hard to break the mapping on purpose for a
+ specific user.
+ </para>
+ <para>
+ The templates to add certificate data to the search filter are based
+ on Python-style formatting strings. They consists of a keyword in
+ curly braces with an optional sub-component specifier separated by a
+ '.' or an optional conversion/formatting option separated by a '!'.
+ Allowed values are:
+ <variablelist>
+ <varlistentry>
+ <term>{issuer_dn[!((ad|ad_x500)|ad_ldap|nss_x500|(nss|nss_ldap))]}</term>
+ <listitem>
+ <para>
+ This template will add the full issuer DN converted to a
+ string according to RFC 4514. If X.500 ordering (most
+ specific RDN comes last) an option with the '_x500'
+ prefix should be used.
+ </para>
+ <para>
+ The conversion options starting with 'ad_' will use
+ attribute names as used by AD, e.g. 'S' instead of 'ST'.
+ </para>
+ <para>
+ The conversion options starting with 'nss_' will use
+ attribute names as used by NSS.
+ </para>
+ <para>
+ The default conversion option is 'nss', i.e. attribute
+ names according to NSS and LDAP/RFC 4514 ordering.
+ </para>
+ <para>
+ Example: (ipacertmapdata=X509:&lt;I&gt;{issuer_dn!ad}&lt;S&gt;{subject_dn!ad})
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{subject_dn[!((ad|ad_x500)|ad_ldap|nss_x500|(nss|nss_ldap))]}</term>
+ <listitem>
+ <para>
+ This template will add the full subject DN converted to
+ string according to RFC 4514. If X.500 ordering (most
+ specific RDN comes last) an option with the '_x500'
+ prefix should be used.
+ </para>
+ <para>
+ The conversion options starting with 'ad_' will use
+ attribute names as used by AD, e.g. 'S' instead of 'ST'.
+ </para>
+ <para>
+ The conversion options starting with 'nss_' will use
+ attribute names as used by NSS.
+ </para>
+ <para>
+ The default conversion option is 'nss', i.e. attribute
+ names according to NSS and LDAP/RFC 4514 ordering.
+ </para>
+ <para>
+ Example: (ipacertmapdata=X509:&lt;I&gt;{issuer_dn!nss_x500}&lt;S&gt;{subject_dn!nss_x500})
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{cert[!(bin|base64)]}</term>
+ <listitem>
+ <para>
+ This template will add the whole DER encoded certificate
+ as a string to the search filter. Depending on the
+ conversion option the binary certificate is either
+ converted to an escaped hex sequence '\xx' or base64.
+ The escaped hex sequence is the default and can e.g. be
+ used with the LDAP attribute 'userCertificate;binary'.
+ </para>
+ <para>
+ Example: (userCertificate;binary={cert!bin})
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{subject_principal[.short_name]}</term>
+ <listitem>
+ <para>
+ This template will add the Kerberos principal which is
+ taken either from the SAN used by pkinit or the one used
+ by AD. The 'short_name' component represent the first
+ part of the principal before the '@' sign.
+ </para>
+ <para>
+ Example: (|(userPrincipal={subject_principal})(samAccountName={subject_principal.short_name}))
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{subject_pkinit_principal[.short_name]}</term>
+ <listitem>
+ <para>
+ This template will add the Kerberos principal which is
+ given by then SAN used by pkinit. The 'short_name'
+ component represent the first part of the principal
+ before the '@' sign.
+ </para>
+ <para>
+ Example: (|(userPrincipal={subject_pkinit_principal})(uid={subject_pkinit_principal.short_name}))
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{subject_nt_principal[.short_name]}</term>
+ <listitem>
+ <para>
+ This template will add the Kerberos principal which is
+ given by then SAN used by AD. The 'short_name' component
+ represent the first part of the principal before the '@'
+ sign.
+ </para>
+ <para>
+ Example: (|(userPrincipal={subject_principal})(samAccountName={subject_principal.short_name}))
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{subject_rfc822_name[.short_name]}</term>
+ <listitem>
+ <para>
+ This template will add the string which is stored in the
+ rfc822Name component of the SAN, typically an email
+ address. The 'short_name' component represent the first
+ part of the address before the '@' sign.
+ </para>
+ <para>
+ Example: (|(mail={subject_rfc822_name})(uid={subject_rfc822_name.short_name}))
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{subject_dns_name[.short_name]}</term>
+ <listitem>
+ <para>
+ This template will add the string which is stored in the
+ dNSName component of the SAN, typically a fully-qualified host name.
+ The 'short_name' component represent the first
+ part of the name before the first '.' sign.
+ </para>
+ <para>
+ Example: (|(fqdn={subject_dns_name})(host={subject_dns_name.short_name}))
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{subject_uri}</term>
+ <listitem>
+ <para>
+ This template will add the string which is stored in the
+ uniformResourceIdentifier component of the SAN.
+ </para>
+ <para>
+ Example: (uri={subject_uri})
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{subject_ip_address}</term>
+ <listitem>
+ <para>
+ This template will add the string which is stored in the
+ iPAddress component of the SAN.
+ </para>
+ <para>
+ Example: (ip={subject_ip_address})
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{subject_x400_address}</term>
+ <listitem>
+ <para>
+ This template will add the value which is stored in the
+ x400Address component of the SAN as escaped hex
+ sequence.
+ </para>
+ <para>
+ Example: (attr:binary={subject_x400_address})
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{subject_directory_name[!((ad|ad_x500)|ad_ldap|nss_x500|(nss|nss_ldap))]}</term>
+ <listitem>
+ <para>
+ This template will add the DN string of the value which
+ is stored in the directoryName component of the SAN.
+ </para>
+ <para>
+ Example: (orig_dn={subject_directory_name})
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{subject_ediparty_name}</term>
+ <listitem>
+ <para>
+ This template will add the value which is stored in the
+ ediPartyName component of the SAN as escaped hex
+ sequence.
+ </para>
+ <para>
+ Example: (attr:binary={subject_ediparty_name})
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>{subject_registered_id}</term>
+ <listitem>
+ <para>
+ This template will add the OID which is stored in the
+ registeredID component of the SAN as as dotted-decimal
+ string.
+ </para>
+ <para>
+ Example: (oid={subject_registered_id})
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect2>
+ <refsect2 id='domains'>
+ <title>DOMAIN LIST</title>
+ <para>
+ If the domain list is not empty users mapped to a given certificate
+ are not only searched in the local domain but in the listed domains
+ as well as long as they are know by SSSD. Domains not know to SSSD
+ will be ignored.
+ </para>
+ </refsect2>
+ </refsect1>
+</refentry>
+</reference>
diff --git a/src/tests/cmocka/test_certmap.c b/src/tests/cmocka/test_certmap.c
new file mode 100644
index 000000000..c998443d0
--- /dev/null
+++ b/src/tests/cmocka/test_certmap.c
@@ -0,0 +1,1443 @@
+/*
+ SSSD
+
+ certmap - Tests for SSSD's certificate mapping library
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <popt.h>
+
+#include "lib/certmap/sss_certmap.h"
+#include "lib/certmap/sss_certmap_int.h"
+
+#include "util/crypto/sss_crypto.h"
+
+#include "tests/cmocka/common_mock.h"
+#include "tests/common.h"
+
+#ifdef HAVE_NSS
+#include "util/crypto/nss/nss_util.h"
+#endif
+
+struct priv_sss_debug {
+ int level;
+};
+
+void ext_debug(void *private, const char *file, long line, const char *function,
+ const char *format, ...)
+{
+ va_list ap;
+ struct priv_sss_debug *data = private;
+ int level = SSSDBG_OP_FAILURE;
+
+ if (data != NULL) {
+ level = data->level;
+ }
+
+ if (DEBUG_IS_SET(level)) {
+ va_start(ap, format);
+ sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED,
+ format, ap);
+ va_end(ap);
+ }
+}
+
+static void test_sss_certmap_init(void **state)
+{
+ int ret;
+ struct sss_certmap_ctx *ctx;
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+
+ sss_certmap_free_ctx(ctx);
+}
+
+static struct sss_certmap_ctx *setup_prio(const int *l)
+{
+ int ret;
+ size_t c;
+ struct sss_certmap_ctx *ctx;
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+
+ for (c = 0; c < 10; c++) {
+ ret = sss_certmap_add_rule(ctx, l[c], NULL, NULL, NULL);
+ assert_int_equal(ret, EOK);
+ }
+
+ return ctx;
+}
+
+static void test_sss_certmap_add_rule(void **state)
+{
+ struct sss_certmap_ctx *ctx;
+ int i;
+ struct priority_list *p;
+ struct priority_list *last;
+ size_t c;
+
+ const int tests_a[][10] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+ {9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
+ {1, 3, 5 ,7, 9, 0, 2, 4, 6, 8},
+ {0, 2, 4, 6, 8, 1, 3, 5, 7, 9},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+
+ const int tests_b[][10] = {{0, 0, 0, 0, 1, 1, 1, 2, 2, 2},
+ {2, 2, 2, 1, 1, 1, 0, 0, 0, 0},
+ {0, 1, 2, 0, 1, 2, 0, 1, 2, 0},
+ {0, 2, 1, 0, 2, 1, 0, 2, 1, 0},
+ {0, 1, 2, 0, 2, 1, 0, 0, 1, 2},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+
+ for (c = 0; tests_a[c][0] != 0 || tests_a[c][9] != 0; c++) {
+ ctx = setup_prio(tests_a[0]);
+ assert_non_null(ctx);
+ i = 0;
+ for (p = ctx->prio_list; p != NULL; p = p->next) {
+ assert_int_equal(i, p->priority);
+ assert_non_null(p->rule_list);
+ assert_int_equal(i, p->rule_list->priority);
+ assert_null(p->rule_list->prev);
+ assert_null(p->rule_list->next);
+ i++;
+ }
+
+ i = 9;
+ for (last = ctx->prio_list; last->next != NULL; last = last->next);
+ for (p = last; p != NULL; p = p->prev) {
+ assert_int_equal(i, p->priority);
+ assert_int_equal(i, p->rule_list->priority);
+ i--;
+ }
+
+ sss_certmap_free_ctx(ctx);
+ }
+ for (c = 0; tests_b[c][0] != 0 || tests_b[c][9] != 0; c++) {
+ ctx = setup_prio(tests_b[0]);
+ assert_non_null(ctx);
+ i = 0;
+ for (p = ctx->prio_list; p != NULL; p = p->next) {
+ assert_int_equal(i, p->priority);
+ assert_non_null(p->rule_list);
+ assert_int_equal(i, p->rule_list->priority);
+ assert_null(p->rule_list->prev);
+ assert_non_null(p->rule_list->next);
+ assert_ptr_equal(p->rule_list, p->rule_list->next->prev);
+ assert_non_null(p->rule_list->next->next);
+ assert_ptr_equal(p->rule_list->next,
+ p->rule_list->next->next->prev);
+ if (i == 0) {
+ assert_non_null(p->rule_list->next->next->next);
+ assert_ptr_equal(p->rule_list->next->next,
+ p->rule_list->next->next->next->prev);
+ assert_null(p->rule_list->next->next->next->next);
+ } else {
+ assert_null(p->rule_list->next->next->next);
+ }
+ i++;
+ }
+ sss_certmap_free_ctx(ctx);
+ }
+}
+
+static void test_sss_certmap_add_matching_rule(void **state)
+{
+ struct sss_certmap_ctx *ctx;
+ int ret;
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "fsdf", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "FDSF:fsdf", NULL, NULL);
+ assert_int_equal(ret, ESRCH);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<rgerge>", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "KRB5:<rgerge>", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<ISSUER>", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<SUBJECT>", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<KU>", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<KU>ddqwdq", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<KU>digitalSignature,dddq", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+
+ ret = sss_certmap_add_rule(ctx, 1, "<EKU>", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<EKU>dwqwqw", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<EKU>.", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<EKU>.1.2.3", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<EKU>1.2.3.", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<EKU>1.a.3", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<SAN:fwfwef>", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<SAN:rfc822Name", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ /* invalid base64 input */
+ ret = sss_certmap_add_rule(ctx, 1, "<SAN:ediPartyName>...", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ /* invalid OID input */
+ ret = sss_certmap_add_rule(ctx, 1, "<SAN:.>dqq", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<SAN:.1>dqq", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<SAN:1.>dqq", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<SAN:11>dqq", NULL, NULL);
+ assert_int_equal(ret, EINVAL);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "<ISSUER>a", NULL, NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
+ relation_and);
+ assert_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
+ assert_string_equal("a",
+ ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
+ talloc_free(ctx);
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+ ret = sss_certmap_add_rule(ctx, 1, "&&<ISSUER>a", NULL, NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
+ relation_and);
+ assert_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
+ assert_string_equal("a",
+ ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
+ talloc_free(ctx);
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+ ret = sss_certmap_add_rule(ctx, 1, "KRB5:||<ISSUER>a", NULL, NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
+ relation_or);
+ assert_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
+ assert_string_equal("a",
+ ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
+ talloc_free(ctx);
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+ ret = sss_certmap_add_rule(ctx, 1, "KRB5:<ISSUER>a<SUBJECT>b", NULL, NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
+ relation_and);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
+ assert_string_equal("b",
+ ctx->prio_list->rule_list->parsed_match_rule->subject->val);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
+ assert_string_equal("a",
+ ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
+ talloc_free(ctx);
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+ ret = sss_certmap_add_rule(ctx, 1000,
+ "KRB5:<ISSUER>a<SUBJECT>b<ISSUER>c<SUBJECT>d",
+ NULL, NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
+ relation_and);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
+ assert_string_equal("d",
+ ctx->prio_list->rule_list->parsed_match_rule->subject->val);
+ assert_string_equal("b",
+ ctx->prio_list->rule_list->parsed_match_rule->subject->next->val);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
+ assert_string_equal("c",
+ ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
+ assert_string_equal("a",
+ ctx->prio_list->rule_list->parsed_match_rule->issuer->next->val);
+
+ ret = sss_certmap_add_rule(ctx, 99,
+ "KRB5:<ISSUER>a<SUBJECT>b"
+ "<KU>dataEncipherment,cRLSign<ISSUER>c"
+ "<SUBJECT>d",
+ NULL, NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
+ relation_and);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
+ assert_string_equal("d",
+ ctx->prio_list->rule_list->parsed_match_rule->subject->val);
+ assert_string_equal("b",
+ ctx->prio_list->rule_list->parsed_match_rule->subject->next->val);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
+ assert_string_equal("c",
+ ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
+ assert_string_equal("a",
+ ctx->prio_list->rule_list->parsed_match_rule->issuer->next->val);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->ku);
+ assert_int_equal(SSS_KU_CRL_SIGN|SSS_KU_DATA_ENCIPHERMENT,
+ ctx->prio_list->rule_list->parsed_match_rule->ku->ku);
+
+ ret = sss_certmap_add_rule(ctx, 98,
+ "KRB5:<ISSUER>a<SUBJECT>b"
+ "<KU>dataEncipherment,cRLSign<ISSUER>c"
+ "<EKU>clientAuth,emailProtection"
+ "<SUBJECT>d",
+ NULL, NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
+ relation_and);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->subject);
+ assert_string_equal("d",
+ ctx->prio_list->rule_list->parsed_match_rule->subject->val);
+ assert_string_equal("b",
+ ctx->prio_list->rule_list->parsed_match_rule->subject->next->val);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->issuer);
+ assert_string_equal("c",
+ ctx->prio_list->rule_list->parsed_match_rule->issuer->val);
+ assert_string_equal("a",
+ ctx->prio_list->rule_list->parsed_match_rule->issuer->next->val);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->ku);
+ assert_int_equal(SSS_KU_CRL_SIGN|SSS_KU_DATA_ENCIPHERMENT,
+ ctx->prio_list->rule_list->parsed_match_rule->ku->ku);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->eku);
+ assert_true(string_in_list("1.3.6.1.5.5.7.3.2",
+ discard_const(
+ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list),
+ true));
+ assert_true(string_in_list("1.3.6.1.5.5.7.3.4",
+ discard_const(
+ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list),
+ true));
+ assert_null(
+ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[2]);
+
+ ret = sss_certmap_add_rule(ctx, 97,
+ "KRB5:<EKU>clientAuth,1.2.3,emailProtection",
+ NULL, NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
+ relation_and);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->eku);
+ assert_true(string_in_list("1.3.6.1.5.5.7.3.2",
+ discard_const(
+ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list),
+ true));
+ assert_true(string_in_list("1.3.6.1.5.5.7.3.4",
+ discard_const(
+ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list),
+ true));
+ assert_true(string_in_list("1.2.3",
+ discard_const(
+ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list),
+ true));
+ assert_null(
+ ctx->prio_list->rule_list->parsed_match_rule->eku->eku_oid_list[3]);
+
+ /* SAN tests */
+ ret = sss_certmap_add_rule(ctx, 89, "KRB5:<SAN>abc", NULL, NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
+ relation_and);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt,
+ SAN_PRINCIPAL);
+ assert_string_equal(ctx->prio_list->rule_list->parsed_match_rule->san->val,
+ "abc");
+
+ ret = sss_certmap_add_rule(ctx, 88, "KRB5:<SAN:dnsName>def", NULL, NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
+ relation_and);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt,
+ SAN_DNS_NAME);
+ assert_string_equal(ctx->prio_list->rule_list->parsed_match_rule->san->val,
+ "def");
+
+ ret = sss_certmap_add_rule(ctx, 87, "KRB5:<SAN:x400Address>aGlq",
+ NULL, NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
+ relation_and);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt,
+ SAN_X400_ADDRESS);
+ assert_int_equal(
+ ctx->prio_list->rule_list->parsed_match_rule->san->bin_val_len,
+ 3);
+ assert_memory_equal(
+ ctx->prio_list->rule_list->parsed_match_rule->san->bin_val,
+ "hij", 3);
+
+ ret = sss_certmap_add_rule(ctx, 86, "KRB5:<SAN:1.2.3.4>klm",
+ NULL, NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->r,
+ relation_and);
+ assert_non_null(ctx->prio_list->rule_list->parsed_match_rule->san);
+ assert_int_equal(ctx->prio_list->rule_list->parsed_match_rule->san->san_opt,
+ SAN_STRING_OTHER_NAME);
+ assert_string_equal(ctx->prio_list->rule_list->parsed_match_rule->san->val,
+ "klm");
+ assert_string_equal("1.2.3.4",
+ ctx->prio_list->rule_list->parsed_match_rule->san->str_other_name_oid);
+
+ talloc_free(ctx);
+}
+
+static void test_check_ad_attr_name(void **state)
+{
+ char *res;
+
+ res = check_ad_attr_name(NULL, NULL);
+ assert_null(res);
+
+ res = check_ad_attr_name(NULL, "");
+ assert_null(res);
+
+ res = check_ad_attr_name(NULL, "dsddqwdas");
+ assert_null(res);
+
+ res = check_ad_attr_name(NULL, "dsddq=wdas");
+ assert_null(res);
+
+ res = check_ad_attr_name(NULL, "CN=abc");
+ assert_null(res);
+
+ res = check_ad_attr_name(NULL, "O=xyz");
+ assert_null(res);
+
+ res = check_ad_attr_name(NULL, "ST=def");
+ assert_non_null(res);
+ assert_string_equal(res, "S=def");
+ talloc_free(res);
+}
+
+const uint8_t test_cert_der[] = {
+0x30, 0x82, 0x04, 0x09, 0x30, 0x82, 0x02, 0xf1, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x09,
+0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
+0x34, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x09, 0x49, 0x50, 0x41, 0x2e,
+0x44, 0x45, 0x56, 0x45, 0x4c, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x15,
+0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x34, 0x32, 0x38, 0x31,
+0x30, 0x32, 0x31, 0x31, 0x31, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x34, 0x32, 0x38, 0x31, 0x30,
+0x32, 0x31, 0x31, 0x31, 0x5a, 0x30, 0x32, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a,
+0x0c, 0x09, 0x49, 0x50, 0x41, 0x2e, 0x44, 0x45, 0x56, 0x45, 0x4c, 0x31, 0x1c, 0x30, 0x1a, 0x06,
+0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x69, 0x70, 0x61, 0x2d, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2e,
+0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f,
+0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb2, 0x32, 0x92, 0xab, 0x47, 0xb8,
+0x0c, 0x13, 0x54, 0x4a, 0x1f, 0x1e, 0x29, 0x06, 0xff, 0xd0, 0x50, 0xcb, 0xf7, 0x5f, 0x79, 0x91,
+0x65, 0xb1, 0x39, 0x01, 0x83, 0x6a, 0xad, 0x9e, 0x77, 0x3b, 0xf3, 0x0d, 0xd7, 0xb9, 0xf6, 0xdc,
+0x9e, 0x4a, 0x49, 0xa7, 0xd0, 0x66, 0x72, 0xcc, 0xbf, 0x77, 0xd6, 0xde, 0xa9, 0xfe, 0x67, 0x96,
+0xcc, 0x49, 0xf1, 0x37, 0x23, 0x2e, 0xc4, 0x50, 0xf4, 0xeb, 0xba, 0x62, 0xd4, 0x23, 0x4d, 0xf3,
+0x37, 0x38, 0x82, 0xee, 0x3b, 0x3f, 0x2c, 0xd0, 0x80, 0x9b, 0x17, 0xaa, 0x9b, 0xeb, 0xa6, 0xdd,
+0xf6, 0x15, 0xff, 0x06, 0xb2, 0xce, 0xff, 0xdf, 0x8a, 0x9e, 0x95, 0x85, 0x49, 0x1f, 0x84, 0xfd,
+0x81, 0x26, 0xce, 0x06, 0x32, 0x0d, 0x36, 0xca, 0x7c, 0x15, 0x81, 0x68, 0x6b, 0x8f, 0x3e, 0xb3,
+0xa2, 0xfc, 0xae, 0xaf, 0xc2, 0x44, 0x58, 0x15, 0x95, 0x40, 0xfc, 0x56, 0x19, 0x91, 0x80, 0xed,
+0x42, 0x11, 0x66, 0x04, 0xef, 0x3c, 0xe0, 0x76, 0x33, 0x4b, 0x83, 0xfa, 0x7e, 0xb4, 0x47, 0xdc,
+0xfb, 0xed, 0x46, 0xa5, 0x8d, 0x0a, 0x66, 0x87, 0xa5, 0xef, 0x7b, 0x74, 0x62, 0xac, 0xbe, 0x73,
+0x36, 0xc9, 0xb4, 0xfe, 0x20, 0xc4, 0x81, 0xf3, 0xfe, 0x78, 0x19, 0xa8, 0xd0, 0xaf, 0x7f, 0x81,
+0x72, 0x24, 0x61, 0xd9, 0x76, 0x93, 0xe3, 0x0b, 0xd2, 0x4f, 0x19, 0x17, 0x33, 0x57, 0xd4, 0x82,
+0xb0, 0xf1, 0xa8, 0x03, 0xf6, 0x01, 0x99, 0xa9, 0xb8, 0x8c, 0x83, 0xc9, 0xba, 0x19, 0x87, 0xea,
+0xd6, 0x3b, 0x06, 0xeb, 0x4c, 0xf7, 0xf1, 0xe5, 0x28, 0xa9, 0x10, 0xb6, 0x46, 0xde, 0xe1, 0xe1,
+0x3f, 0xc1, 0xcc, 0x72, 0xbe, 0x2a, 0x43, 0xc6, 0xf6, 0xd0, 0xb5, 0xa0, 0xc4, 0x24, 0x6e, 0x4f,
+0xbd, 0xec, 0x22, 0x8a, 0x07, 0x11, 0x3d, 0xf9, 0xd3, 0x15, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+0x82, 0x01, 0x26, 0x30, 0x82, 0x01, 0x22, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
+0x30, 0x16, 0x80, 0x14, 0xf2, 0x9d, 0x42, 0x4e, 0x0f, 0xc4, 0x48, 0x25, 0x58, 0x2f, 0x1c, 0xce,
+0x0f, 0xa1, 0x3f, 0x22, 0xc8, 0x55, 0xc8, 0x91, 0x30, 0x3b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+0x05, 0x07, 0x01, 0x01, 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+0x05, 0x07, 0x30, 0x01, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x69, 0x70, 0x61,
+0x2d, 0x63, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2f, 0x63, 0x61,
+0x2f, 0x6f, 0x63, 0x73, 0x70, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+0x04, 0x03, 0x02, 0x04, 0xf0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14,
+0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+0x05, 0x07, 0x03, 0x02, 0x30, 0x74, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x6d, 0x30, 0x6b, 0x30,
+0x69, 0xa0, 0x31, 0xa0, 0x2f, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x69, 0x70,
+0x61, 0x2d, 0x63, 0x61, 0x2e, 0x69, 0x70, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x2f, 0x69,
+0x70, 0x61, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x52, 0x4c,
+0x2e, 0x62, 0x69, 0x6e, 0xa2, 0x34, 0xa4, 0x32, 0x30, 0x30, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03,
+0x55, 0x04, 0x0a, 0x0c, 0x05, 0x69, 0x70, 0x61, 0x63, 0x61, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03,
+0x55, 0x04, 0x03, 0x0c, 0x15, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
+0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+0x0e, 0x04, 0x16, 0x04, 0x14, 0x2d, 0x2b, 0x3f, 0xcb, 0xf5, 0xb2, 0xff, 0x32, 0x2c, 0xa8, 0xc2,
+0x1c, 0xdd, 0xbd, 0x8c, 0x80, 0x1e, 0xdd, 0x31, 0x82, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9a, 0x47, 0x2e,
+0x50, 0xa7, 0x4d, 0x1d, 0x53, 0x0f, 0xc9, 0x71, 0x42, 0x0c, 0xe5, 0xda, 0x7d, 0x49, 0x64, 0xe7,
+0xab, 0xc8, 0xdf, 0xdf, 0x02, 0xc1, 0x87, 0xd1, 0x5b, 0xde, 0xda, 0x6f, 0x2b, 0xe4, 0xf0, 0xbe,
+0xba, 0x09, 0xdf, 0x02, 0x85, 0x0b, 0x8a, 0xe6, 0x9b, 0x06, 0x7d, 0x69, 0x38, 0x6c, 0x72, 0xff,
+0x4c, 0x7b, 0x2a, 0x0d, 0x3f, 0x23, 0x2f, 0x16, 0x46, 0xff, 0x05, 0x93, 0xb0, 0xea, 0x24, 0x28,
+0xd7, 0x12, 0xa1, 0x57, 0xb8, 0x59, 0x19, 0x25, 0xf3, 0x43, 0x0a, 0xd3, 0xfd, 0x0f, 0x37, 0x8d,
+0xb8, 0xca, 0x15, 0xe7, 0x48, 0x8a, 0xa0, 0xc7, 0xc7, 0x4b, 0x7f, 0x01, 0x3c, 0x58, 0xd7, 0x37,
+0xe5, 0xff, 0x7d, 0x2b, 0x01, 0xac, 0x0d, 0x9f, 0x51, 0x6a, 0xe5, 0x40, 0x24, 0xe6, 0x5e, 0x55,
+0x0d, 0xf7, 0xb8, 0x2f, 0x42, 0xac, 0x6d, 0xe5, 0x29, 0x6b, 0xc6, 0x0b, 0xa4, 0xbf, 0x19, 0xbd,
+0x39, 0x27, 0xee, 0xfe, 0xc5, 0xb3, 0xdb, 0x62, 0xd4, 0xbe, 0xd2, 0x47, 0xba, 0x96, 0x30, 0x5a,
+0xfd, 0x62, 0x00, 0xb8, 0x27, 0x5d, 0x2f, 0x3a, 0x94, 0x0b, 0x95, 0x35, 0x85, 0x40, 0x2c, 0xbc,
+0x67, 0xdf, 0x8a, 0xf9, 0xf1, 0x7b, 0x19, 0x96, 0x3e, 0x42, 0x48, 0x13, 0x23, 0x04, 0x95, 0xa9,
+0x6b, 0x11, 0x33, 0x81, 0x47, 0x5a, 0x83, 0x72, 0xf6, 0x20, 0xfa, 0x8e, 0x41, 0x7b, 0x8f, 0x77,
+0x47, 0x7c, 0xc7, 0x5d, 0x46, 0xf4, 0x4f, 0xfd, 0x81, 0x0a, 0xae, 0x39, 0x27, 0xb6, 0x6a, 0x26,
+0x63, 0xb1, 0xd3, 0xbf, 0x55, 0x83, 0x82, 0x9b, 0x36, 0x6c, 0x33, 0x64, 0x0f, 0x50, 0xc0, 0x55,
+0x94, 0x13, 0xc3, 0x85, 0xf4, 0xd5, 0x71, 0x65, 0xd0, 0xc0, 0xdd, 0xfc, 0xe6, 0xec, 0x9c, 0x5b,
+0xf0, 0x11, 0xb5, 0x2c, 0xf3, 0x48, 0xc1, 0x36, 0x8c, 0xa2, 0x96, 0x48, 0x84};
+
+const uint8_t test_cert2_der[] = {
+0x30, 0x82, 0x06, 0x98, 0x30, 0x82, 0x05, 0x80, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0a, 0x61,
+0x22, 0x88, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x15, 0x30, 0x13, 0x06, 0x0a,
+0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x05, 0x64, 0x65, 0x76, 0x65,
+0x6c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01,
+0x19, 0x16, 0x02, 0x61, 0x64, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0f,
+0x61, 0x64, 0x2d, 0x41, 0x44, 0x2d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x2d, 0x43, 0x41, 0x30,
+0x1e, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x31, 0x31, 0x31, 0x33, 0x35, 0x31, 0x31, 0x31, 0x5a,
+0x17, 0x0d, 0x31, 0x37, 0x31, 0x31, 0x31, 0x31, 0x31, 0x33, 0x35, 0x31, 0x31, 0x31, 0x5a, 0x30,
+0x70, 0x31, 0x15, 0x30, 0x13, 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01,
+0x19, 0x16, 0x05, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x0a, 0x09, 0x92,
+0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x02, 0x61, 0x64, 0x31, 0x0e, 0x30, 0x0c,
+0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x05, 0x55, 0x73, 0x65, 0x72, 0x73, 0x31, 0x0c, 0x30, 0x0a,
+0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x03, 0x74, 0x20, 0x75, 0x31, 0x25, 0x30, 0x23, 0x06, 0x09,
+0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x16, 0x74, 0x65, 0x73, 0x74, 0x2e,
+0x75, 0x73, 0x65, 0x72, 0x40, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69,
+0x6e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+0x01, 0x00, 0x9c, 0xcf, 0x36, 0x99, 0xde, 0x63, 0x74, 0x2b, 0x77, 0x25, 0x9e, 0x24, 0xd9, 0x77,
+0x4b, 0x5f, 0x98, 0xc0, 0x8c, 0xd7, 0x20, 0x91, 0xc0, 0x1c, 0xe8, 0x37, 0x45, 0xbf, 0x3c, 0xd9,
+0x33, 0xbd, 0xe9, 0xde, 0xc9, 0x5d, 0xd4, 0xcd, 0x06, 0x0a, 0x0d, 0xd4, 0xf1, 0x7c, 0x74, 0x5b,
+0x29, 0xd5, 0x66, 0x9c, 0x2c, 0x9f, 0x6b, 0x1a, 0x0f, 0x0d, 0xe6, 0x6c, 0x62, 0xa5, 0x41, 0x4f,
+0xc3, 0xa4, 0x88, 0x27, 0x11, 0x5d, 0xb7, 0xb1, 0xfb, 0xf8, 0x8d, 0xee, 0x43, 0x8d, 0x93, 0xb5,
+0x8c, 0xb4, 0x34, 0x06, 0xf5, 0xe9, 0x2f, 0x5a, 0x26, 0x68, 0xd7, 0x43, 0x60, 0x82, 0x5e, 0x22,
+0xa7, 0xc6, 0x34, 0x40, 0x19, 0xa5, 0x8e, 0xf0, 0x58, 0x9f, 0x16, 0x2d, 0x43, 0x3f, 0x0c, 0xda,
+0xe2, 0x23, 0xf6, 0x09, 0x2a, 0x5e, 0xbd, 0x84, 0x27, 0xc8, 0xab, 0xd5, 0x70, 0xf8, 0x3d, 0x9c,
+0x14, 0xc2, 0xc2, 0xa2, 0x77, 0xe8, 0x44, 0x73, 0x10, 0x01, 0x34, 0x40, 0x1f, 0xc6, 0x2f, 0xa0,
+0x70, 0xee, 0x2f, 0xd5, 0x4b, 0xbe, 0x4c, 0xc7, 0x45, 0xf7, 0xac, 0x9c, 0xc3, 0x68, 0x5b, 0x1d,
+0x5a, 0x4b, 0x77, 0x65, 0x76, 0xe4, 0xb3, 0x92, 0xf4, 0x84, 0x0a, 0x9e, 0x6a, 0x9c, 0xc9, 0x53,
+0x42, 0x9f, 0x6d, 0xfe, 0xf9, 0xf5, 0xf2, 0x9a, 0x15, 0x50, 0x47, 0xef, 0xf4, 0x06, 0x59, 0xc8,
+0x50, 0x48, 0x4b, 0x46, 0x95, 0x68, 0x25, 0xc5, 0xbd, 0x4f, 0x65, 0x34, 0x00, 0xfc, 0x31, 0x69,
+0xf8, 0x3e, 0xe0, 0x20, 0x83, 0x41, 0x27, 0x0b, 0x5c, 0x46, 0x98, 0x14, 0xf0, 0x07, 0xde, 0x02,
+0x17, 0xb1, 0xd2, 0x9c, 0xbe, 0x1c, 0x0d, 0x56, 0x22, 0x1b, 0x02, 0xfe, 0xda, 0x69, 0xb9, 0xef,
+0x91, 0x37, 0x39, 0x7f, 0x24, 0xda, 0xc4, 0x81, 0x5e, 0x82, 0x31, 0x2f, 0x98, 0x1d, 0xf7, 0x73,
+0x5b, 0x23, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x03, 0x5d, 0x30, 0x82, 0x03, 0x59, 0x30,
+0x3d, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x07, 0x04, 0x30, 0x30, 0x2e,
+0x06, 0x26, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x08, 0x87, 0x85, 0xa1, 0x23, 0x84,
+0xc8, 0xb2, 0x26, 0x83, 0x9d, 0x9d, 0x21, 0x82, 0xd4, 0xa6, 0x1b, 0x86, 0xa3, 0xba, 0x37, 0x81,
+0x10, 0x85, 0x89, 0xd5, 0x02, 0xd6, 0x8f, 0x24, 0x02, 0x01, 0x64, 0x02, 0x01, 0x02, 0x30, 0x29,
+0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x22, 0x30, 0x20, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x04, 0x06, 0x0a, 0x2b,
+0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x04, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x35, 0x06, 0x09, 0x2b, 0x06, 0x01,
+0x04, 0x01, 0x82, 0x37, 0x15, 0x0a, 0x04, 0x28, 0x30, 0x26, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06,
+0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+0x03, 0x04, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x04,
+0x30, 0x81, 0x94, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x0f, 0x04, 0x81,
+0x86, 0x30, 0x81, 0x83, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01,
+0x2a, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2d, 0x30, 0x0b,
+0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x16, 0x30, 0x0b, 0x06, 0x09, 0x60,
+0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x19, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+0x65, 0x03, 0x04, 0x01, 0x02, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
+0x01, 0x05, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x07, 0x30, 0x07,
+0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x07, 0x30, 0x0e, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+0x0d, 0x03, 0x02, 0x02, 0x02, 0x00, 0x80, 0x30, 0x0e, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+0x0d, 0x03, 0x04, 0x02, 0x02, 0x02, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+0x04, 0x14, 0x49, 0xac, 0xad, 0xe0, 0x65, 0x30, 0xc4, 0xce, 0xa0, 0x09, 0x03, 0x5b, 0xad, 0x4a,
+0x7b, 0x49, 0x5e, 0xc9, 0x6c, 0xb4, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+0x16, 0x80, 0x14, 0x62, 0x50, 0xb6, 0x8d, 0xa1, 0xe6, 0x2d, 0x91, 0xbf, 0xb0, 0x54, 0x4d, 0x8f,
+0xa8, 0xca, 0x10, 0xae, 0xb8, 0xdd, 0x54, 0x30, 0x81, 0xcc, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
+0x81, 0xc4, 0x30, 0x81, 0xc1, 0x30, 0x81, 0xbe, 0xa0, 0x81, 0xbb, 0xa0, 0x81, 0xb8, 0x86, 0x81,
+0xb5, 0x6c, 0x64, 0x61, 0x70, 0x3a, 0x2f, 0x2f, 0x2f, 0x43, 0x4e, 0x3d, 0x61, 0x64, 0x2d, 0x41,
+0x44, 0x2d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x2d, 0x43, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x61,
+0x64, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x44, 0x50, 0x2c,
+0x43, 0x4e, 0x3d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x25, 0x32, 0x30, 0x4b, 0x65, 0x79, 0x25,
+0x32, 0x30, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x53, 0x65,
+0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x44, 0x43, 0x3d, 0x61, 0x64, 0x2c, 0x44, 0x43,
+0x3d, 0x64, 0x65, 0x76, 0x65, 0x6c, 0x3f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+0x74, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74,
+0x3f, 0x62, 0x61, 0x73, 0x65, 0x3f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73,
+0x73, 0x3d, 0x63, 0x52, 0x4c, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f,
+0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x30, 0x81, 0xbe, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+0x07, 0x01, 0x01, 0x04, 0x81, 0xb1, 0x30, 0x81, 0xae, 0x30, 0x81, 0xab, 0x06, 0x08, 0x2b, 0x06,
+0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x81, 0x9e, 0x6c, 0x64, 0x61, 0x70, 0x3a, 0x2f, 0x2f,
+0x2f, 0x43, 0x4e, 0x3d, 0x61, 0x64, 0x2d, 0x41, 0x44, 0x2d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52,
+0x2d, 0x43, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x41, 0x49, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x50, 0x75,
+0x62, 0x6c, 0x69, 0x63, 0x25, 0x32, 0x30, 0x4b, 0x65, 0x79, 0x25, 0x32, 0x30, 0x53, 0x65, 0x72,
+0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
+0x6f, 0x6e, 0x2c, 0x44, 0x43, 0x3d, 0x61, 0x64, 0x2c, 0x44, 0x43, 0x3d, 0x64, 0x65, 0x76, 0x65,
+0x6c, 0x3f, 0x63, 0x41, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x3f,
+0x62, 0x61, 0x73, 0x65, 0x3f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73,
+0x3d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x75,
+0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x3f, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x38,
+0x30, 0x36, 0xa0, 0x1c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x03,
+0xa0, 0x0e, 0x0c, 0x0c, 0x74, 0x75, 0x31, 0x40, 0x61, 0x64, 0x2e, 0x64, 0x65, 0x76, 0x65, 0x6c,
+0x81, 0x16, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x40, 0x65, 0x6d, 0x61, 0x69,
+0x6c, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x41, 0x45, 0x0a, 0x6d,
+0xbb, 0x7f, 0x5c, 0x07, 0x0c, 0xc9, 0xb0, 0x39, 0x55, 0x6d, 0x7c, 0xb5, 0x02, 0xcd, 0xe8, 0xb2,
+0xe5, 0x02, 0x94, 0x77, 0x60, 0xdb, 0xd1, 0xaf, 0x1d, 0xdb, 0x44, 0x5f, 0xce, 0x83, 0xdb, 0x80,
+0x2e, 0xe2, 0xb2, 0x08, 0x25, 0x82, 0x14, 0xcb, 0x48, 0x95, 0x20, 0x13, 0x6c, 0xa9, 0xaa, 0xf8,
+0x31, 0x56, 0xed, 0xc0, 0x3b, 0xd4, 0xae, 0x2e, 0xe3, 0x8f, 0x05, 0xfc, 0xab, 0x5f, 0x2a, 0x69,
+0x23, 0xbc, 0xb8, 0x8c, 0xec, 0x2d, 0xa9, 0x0b, 0x86, 0x95, 0x73, 0x73, 0xdb, 0x17, 0xce, 0xc6,
+0xae, 0xc5, 0xb4, 0xc1, 0x25, 0x87, 0x3b, 0x67, 0x43, 0x9e, 0x87, 0x5a, 0xe6, 0xb9, 0xa0, 0x28,
+0x12, 0x3d, 0xa8, 0x2e, 0xd7, 0x5e, 0xef, 0x65, 0x2d, 0xe6, 0xa5, 0x67, 0x84, 0xac, 0xfd, 0x31,
+0xc1, 0x78, 0xd8, 0x72, 0x51, 0xa2, 0x88, 0x55, 0x0f, 0x97, 0x47, 0x93, 0x07, 0xea, 0x8a, 0x53,
+0x27, 0x4e, 0x34, 0x54, 0x34, 0x1f, 0xa0, 0x6a, 0x03, 0x44, 0xfb, 0x23, 0x61, 0x8e, 0x87, 0x8e,
+0x3c, 0xd0, 0x8f, 0xae, 0xe4, 0xcf, 0xee, 0x65, 0xa8, 0xba, 0x96, 0x68, 0x08, 0x1c, 0x60, 0xe2,
+0x4e, 0x11, 0xa3, 0x74, 0xb8, 0xa5, 0x4e, 0xea, 0x6a, 0x82, 0x4c, 0xc2, 0x4d, 0x63, 0x8e, 0x9f,
+0x7c, 0x2f, 0xa8, 0xc0, 0x62, 0xf8, 0xf7, 0xd9, 0x25, 0xc4, 0x91, 0xab, 0x4d, 0x6a, 0x44, 0xaf,
+0x75, 0x93, 0x53, 0x03, 0xa4, 0x99, 0xc8, 0xcd, 0x91, 0x89, 0x60, 0x75, 0x30, 0x99, 0x76, 0x05,
+0x5a, 0xa0, 0x03, 0xa7, 0xa1, 0x2c, 0x03, 0x04, 0x8f, 0xd4, 0x5a, 0x31, 0x52, 0x28, 0x5a, 0xe6,
+0xa2, 0xd3, 0x43, 0x21, 0x5b, 0xdc, 0xa2, 0x1d, 0x55, 0xa9, 0x48, 0xc5, 0xc4, 0xaa, 0xf3, 0x8b,
+0xe6, 0x3e, 0x75, 0x96, 0xe4, 0x3e, 0x64, 0xaf, 0xe8, 0xa7, 0x6a, 0xb6};
+
+void test_sss_cert_get_content(void **state)
+{
+ int ret;
+ struct sss_cert_content *content;
+
+ ret = sss_cert_get_content(NULL, test_cert_der, sizeof(test_cert_der),
+ &content);
+ assert_int_equal(ret , 0);
+ assert_non_null(content);
+ assert_non_null(content->issuer_str);
+ assert_string_equal(content->issuer_str, "CN=Certificate Authority,O=IPA.DEVEL");
+ assert_non_null(content->subject_str);
+ assert_string_equal(content->subject_str, "CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
+ assert_int_equal(content->key_usage, SSS_KU_DIGITAL_SIGNATURE
+ |SSS_KU_NON_REPUDIATION
+ |SSS_KU_KEY_ENCIPHERMENT
+ |SSS_KU_DATA_ENCIPHERMENT);
+ assert_non_null(content->extended_key_usage_oids);
+ assert_non_null(content->extended_key_usage_oids[0]);
+ assert_true(string_in_list("1.3.6.1.5.5.7.3.1",
+ discard_const(content->extended_key_usage_oids), true));
+ assert_true(string_in_list("1.3.6.1.5.5.7.3.2",
+ discard_const(content->extended_key_usage_oids), true));
+ assert_null(content->extended_key_usage_oids[2]);
+ assert_int_equal(content->cert_der_size, sizeof(test_cert_der));
+ assert_memory_equal(content->cert_der, test_cert_der, sizeof(test_cert_der));
+
+ assert_non_null(content->issuer_rdn_list);
+ assert_string_equal(content->issuer_rdn_list[0], "O=IPA.DEVEL");
+ assert_string_equal(content->issuer_rdn_list[1], "CN=Certificate Authority");
+ assert_null(content->issuer_rdn_list[2]);
+
+ assert_non_null(content->subject_rdn_list);
+ assert_string_equal(content->subject_rdn_list[0], "O=IPA.DEVEL");
+ assert_string_equal(content->subject_rdn_list[1], "CN=ipa-devel.ipa.devel");
+ assert_null(content->subject_rdn_list[2]);
+
+
+ talloc_free(content);
+}
+
+void test_sss_cert_get_content_2(void **state)
+{
+ int ret;
+ struct sss_cert_content *content;
+ struct san_list *i;
+
+ ret = sss_cert_get_content(NULL, test_cert2_der, sizeof(test_cert2_der),
+ &content);
+ assert_int_equal(ret, 0);
+ assert_non_null(content);
+ assert_non_null(content->issuer_str);
+ assert_string_equal(content->issuer_str,
+ "CN=ad-AD-SERVER-CA,DC=ad,DC=devel");
+ assert_non_null(content->subject_str);
+#if 0
+FIXME:
+ assert_string_equal(content->subject_str,
+ "E=test.user@email.domain,CN=t u,CN=Users,DC=ad,DC=devel,DC=ad,DC=devel");
+ //"CN=t u/emailAddress=test.user@email.domain,DC=ad,DC=devel");
+#endif
+ assert_int_equal(content->key_usage, SSS_KU_DIGITAL_SIGNATURE
+ |SSS_KU_KEY_ENCIPHERMENT);
+ assert_non_null(content->extended_key_usage_oids);
+ assert_non_null(content->extended_key_usage_oids[0]);
+ assert_true(string_in_list("1.3.6.1.5.5.7.3.2",
+ discard_const(content->extended_key_usage_oids), true));
+ assert_true(string_in_list("1.3.6.1.5.5.7.3.4",
+ discard_const(content->extended_key_usage_oids), true));
+ /* Can use Microsoft Encrypted File System OID */
+ assert_true(string_in_list("1.3.6.1.4.1.311.10.3.4",
+ discard_const(content->extended_key_usage_oids), true));
+ assert_null(content->extended_key_usage_oids[3]);
+ assert_int_equal(content->cert_der_size, sizeof(test_cert2_der));
+ assert_memory_equal(content->cert_der, test_cert2_der,
+ sizeof(test_cert2_der));
+
+ assert_non_null(content->issuer_rdn_list);
+ assert_string_equal(content->issuer_rdn_list[0], "DC=devel");
+ assert_string_equal(content->issuer_rdn_list[1], "DC=ad");
+ assert_string_equal(content->issuer_rdn_list[2], "CN=ad-AD-SERVER-CA");
+ assert_null(content->issuer_rdn_list[3]);
+
+ assert_non_null(content->subject_rdn_list);
+ assert_string_equal(content->subject_rdn_list[0], "DC=devel");
+ assert_string_equal(content->subject_rdn_list[1], "DC=ad");
+ assert_string_equal(content->subject_rdn_list[2], "CN=Users");
+ assert_string_equal(content->subject_rdn_list[3], "CN=t u");
+ assert_string_equal(content->subject_rdn_list[4],
+ "E=test.user@email.domain");
+ //"CN=t u/emailAddress=test.user@email.domain");
+ assert_null(content->subject_rdn_list[5]);
+
+ assert_non_null(content->san_list);
+
+ DLIST_FOR_EACH(i, content->san_list) {
+ switch (i->san_opt) {
+ case SAN_RFC822_NAME:
+ assert_string_equal(i->val, "test.user@email.domain");
+ assert_string_equal(i->short_name, "test.user");
+ break;
+ case SAN_STRING_OTHER_NAME:
+ assert_string_equal(i->other_name_oid, "1.3.6.1.4.1.311.20.2.3");
+ assert_int_equal(i->bin_val_len, 14);
+ assert_memory_equal(i->bin_val, "\f\ftu1@ad.devel", 14);
+ break;
+ case SAN_NT:
+ case SAN_PRINCIPAL:
+ assert_string_equal(i->val, "tu1@ad.devel");
+ assert_string_equal(i->short_name, "tu1");
+ break;
+ default:
+ assert_true(false);
+ }
+ }
+
+ talloc_free(content);
+}
+
+static void test_sss_certmap_match_cert(void **state)
+{
+ struct sss_certmap_ctx *ctx;
+ int ret;
+ size_t c;
+
+ struct match_tests {
+ const char *rule;
+ int result;
+ } match_tests[] = {
+ {"KRB5:<KU>digitalSignature", 0},
+ {"KRB5:<KU>digitalSignature,nonRepudiation", 0},
+ {"KRB5:<KU>digitalSignature,cRLSign", ENOENT},
+ {"KRB5:<EKU>clientAuth", 0},
+ {"KRB5:<EKU>clientAuth,OCSPSigning", ENOENT},
+ {"KRB5:<EKU>clientAuth,serverAuth", 0},
+ {NULL, 0}
+ };
+
+ struct match_tests match_tests_2[] = {
+ {"KRB5:<KU>digitalSignature", 0},
+ {"KRB5:<KU>keyEncipherment", 0},
+ {"KRB5:<KU>digitalSignature,keyEncipherment", 0},
+ {"KRB5:<KU>digitalSignature,keyEncipherment,cRLSign", ENOENT},
+ {"KRB5:<EKU>clientAuth", 0},
+ {"KRB5:<EKU>clientAuth,1.3.6.1.4.1.311.10.3.4", 0},
+ {"KRB5:<EKU>clientAuth,1.3.6.1.4.1.311.10.3.41", ENOENT},
+ {"KRB5:<SAN>tu1", 0},
+ {"KRB5:<SAN:Principal>tu1", 0},
+ {"KRB5:<SAN:ntPrincipalName>tu1", 0},
+ {"KRB5:<SAN:pkinitSAN>tu1", ENOENT},
+ {"KRB5:<SAN:Principal>^tu1@ad.devel$", 0},
+ {"KRB5:<SAN:rfc822Name>tu", ENOENT},
+ {"KRB5:<SAN:rfc822Name>test.user", 0},
+ {"KRB5:<SAN:rfc822Name>test.user<SAN>tu1", 0},
+ {"KRB5:||<SAN:rfc822Name>test.user<SAN>tu1", 0},
+ {"KRB5:&&<SAN:rfc822Name>tu1<SAN>tu1", ENOENT},
+ {"KRB5:||<SAN:rfc822Name>tu1<SAN>tu1", 0},
+ {"KRB5:<SAN:otherName>MTIz", ENOENT}, /* 123 */
+ {"KRB5:<SAN:otherName>DAx0dTFAYWQuZGV2ZWw=", 0}, /* "\f\ftu1@ad.devel" */
+ {"KRB5:<SAN:otherName>DAx0dTFAYWQuZGV2ZWx4", ENOENT}, /* "\f\ftu1@ad.develx" */
+ {"KRB5:<SAN:otherName>dHUxQGFkLmRldmVs", 0}, /* "tu1@ad.devel" */
+ {"KRB5:<SAN:1.3.6.1.4.1.311.20.2.3>test", ENOENT},
+ {"KRB5:<SAN:1.3.6.1.4.1.311.20.2.3>tu1@ad", 0},
+ /* Fails becasue the NT principal SAN starts with binary values */
+ {"KRB5:<SAN:1.3.6.1.4.1.311.20.2.3>^tu1@ad.devel$", ENOENT},
+ {NULL, 0}
+ };
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, "KRB5:<ISSUER>xyz<SUBJECT>xyz",
+ NULL, NULL);
+ assert_int_equal(ret, EOK);
+
+ ret = sss_certmap_match_cert(ctx, discard_const(test_cert_der),
+ sizeof(test_cert_der));
+ assert_int_equal(ret, ENOENT);
+
+ ret = sss_certmap_add_rule(ctx, 1,
+ "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
+ NULL, NULL);
+ assert_int_equal(ret, EOK);
+
+ ret = sss_certmap_match_cert(ctx, discard_const(test_cert_der),
+ sizeof(test_cert_der));
+ assert_int_equal(ret, 0);
+
+ sss_certmap_free_ctx(ctx);
+
+ for (c = 0; match_tests[c].rule != NULL; c++) {
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, match_tests[c].rule, NULL, NULL);
+ assert_int_equal(ret, EOK);
+
+ ret = sss_certmap_match_cert(ctx, discard_const(test_cert_der),
+ sizeof(test_cert_der));
+ assert_int_equal(ret, match_tests[c].result);
+
+ sss_certmap_free_ctx(ctx);
+ }
+
+ for (c = 0; match_tests_2[c].rule != NULL; c++) {
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+
+ print_error("Checking matching rule [%s]\n", match_tests_2[c].rule);
+
+ ret = sss_certmap_add_rule(ctx, 1, match_tests_2[c].rule, NULL, NULL);
+ assert_int_equal(ret, EOK);
+
+ ret = sss_certmap_match_cert(ctx, discard_const(test_cert2_der),
+ sizeof(test_cert2_der));
+ assert_int_equal(ret, match_tests_2[c].result);
+
+ sss_certmap_free_ctx(ctx);
+ }
+}
+
+static void test_sss_certmap_add_mapping_rule(void **state)
+{
+ struct sss_certmap_ctx *ctx;
+ int ret;
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 1, NULL, "FWEAWEF:fwefwe", NULL);
+ assert_int_equal(ret, ESRCH);
+
+ ret = sss_certmap_add_rule(ctx, 1, NULL, "LDAP:abc", NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule);
+ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list);
+ assert_int_equal(comp_string,
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->type);
+ assert_string_equal("abc",
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->val);
+ talloc_free(ctx);
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+ ret = sss_certmap_add_rule(ctx, 1, NULL, "LDAP:abc{issuer_dn}", NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule);
+ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list);
+ assert_int_equal(comp_string,
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->type);
+ assert_string_equal("abc",
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->val);
+ assert_int_equal(comp_template,
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->next->type);
+ assert_string_equal("issuer_dn",
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->next->val);
+ talloc_free(ctx);
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+ ret = sss_certmap_add_rule(ctx, 1, NULL, "{issuer_dn}a:b{{c}}", NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule);
+ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list);
+ assert_int_equal(comp_template,
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->type);
+ assert_string_equal("issuer_dn",
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->val);
+ assert_int_equal(comp_string,
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->next->type);
+ assert_string_equal("a:b{c}",
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->next->val);
+ talloc_free(ctx);
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+ ret = sss_certmap_add_rule(ctx, 1, NULL, "LDAP:{issuer_dn}{subject_dn}",
+ NULL);
+ assert_int_equal(ret, 0);
+ assert_non_null(ctx->prio_list);
+ assert_non_null(ctx->prio_list->rule_list);
+ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule);
+ assert_non_null(ctx->prio_list->rule_list->parsed_mapping_rule->list);
+ assert_int_equal(comp_template,
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->type);
+ assert_string_equal("issuer_dn",
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->val);
+ assert_int_equal(comp_template,
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->next->type);
+ assert_string_equal("subject_dn",
+ ctx->prio_list->rule_list->parsed_mapping_rule->list->next->val);
+ talloc_free(ctx);
+}
+
+#define TEST_CERT_BIN \
+ "\\30\\82\\04\\09\\30\\82\\02\\f1\\a0\\03\\02\\01\\02\\02\\01\\09" \
+ "\\30\\0d\\06\\09\\2a\\86\\48\\86\\f7\\0d\\01\\01\\0b\\05\\00\\30" \
+ "\\34\\31\\12\\30\\10\\06\\03\\55\\04\\0a\\0c\\09\\49\\50\\41\\2e" \
+ "\\44\\45\\56\\45\\4c\\31\\1e\\30\\1c\\06\\03\\55\\04\\03\\0c\\15" \
+ "\\43\\65\\72\\74\\69\\66\\69\\63\\61\\74\\65\\20\\41\\75\\74\\68" \
+ "\\6f\\72\\69\\74\\79\\30\\1e\\17\\0d\\31\\35\\30\\34\\32\\38\\31" \
+ "\\30\\32\\31\\31\\31\\5a\\17\\0d\\31\\37\\30\\34\\32\\38\\31\\30" \
+ "\\32\\31\\31\\31\\5a\\30\\32\\31\\12\\30\\10\\06\\03\\55\\04\\0a" \
+ "\\0c\\09\\49\\50\\41\\2e\\44\\45\\56\\45\\4c\\31\\1c\\30\\1a\\06" \
+ "\\03\\55\\04\\03\\0c\\13\\69\\70\\61\\2d\\64\\65\\76\\65\\6c\\2e" \
+ "\\69\\70\\61\\2e\\64\\65\\76\\65\\6c\\30\\82\\01\\22\\30\\0d\\06" \
+ "\\09\\2a\\86\\48\\86\\f7\\0d\\01\\01\\01\\05\\00\\03\\82\\01\\0f" \
+ "\\00\\30\\82\\01\\0a\\02\\82\\01\\01\\00\\b2\\32\\92\\ab\\47\\b8" \
+ "\\0c\\13\\54\\4a\\1f\\1e\\29\\06\\ff\\d0\\50\\cb\\f7\\5f\\79\\91" \
+ "\\65\\b1\\39\\01\\83\\6a\\ad\\9e\\77\\3b\\f3\\0d\\d7\\b9\\f6\\dc" \
+ "\\9e\\4a\\49\\a7\\d0\\66\\72\\cc\\bf\\77\\d6\\de\\a9\\fe\\67\\96" \
+ "\\cc\\49\\f1\\37\\23\\2e\\c4\\50\\f4\\eb\\ba\\62\\d4\\23\\4d\\f3" \
+ "\\37\\38\\82\\ee\\3b\\3f\\2c\\d0\\80\\9b\\17\\aa\\9b\\eb\\a6\\dd" \
+ "\\f6\\15\\ff\\06\\b2\\ce\\ff\\df\\8a\\9e\\95\\85\\49\\1f\\84\\fd" \
+ "\\81\\26\\ce\\06\\32\\0d\\36\\ca\\7c\\15\\81\\68\\6b\\8f\\3e\\b3" \
+ "\\a2\\fc\\ae\\af\\c2\\44\\58\\15\\95\\40\\fc\\56\\19\\91\\80\\ed" \
+ "\\42\\11\\66\\04\\ef\\3c\\e0\\76\\33\\4b\\83\\fa\\7e\\b4\\47\\dc" \
+ "\\fb\\ed\\46\\a5\\8d\\0a\\66\\87\\a5\\ef\\7b\\74\\62\\ac\\be\\73" \
+ "\\36\\c9\\b4\\fe\\20\\c4\\81\\f3\\fe\\78\\19\\a8\\d0\\af\\7f\\81" \
+ "\\72\\24\\61\\d9\\76\\93\\e3\\0b\\d2\\4f\\19\\17\\33\\57\\d4\\82" \
+ "\\b0\\f1\\a8\\03\\f6\\01\\99\\a9\\b8\\8c\\83\\c9\\ba\\19\\87\\ea" \
+ "\\d6\\3b\\06\\eb\\4c\\f7\\f1\\e5\\28\\a9\\10\\b6\\46\\de\\e1\\e1" \
+ "\\3f\\c1\\cc\\72\\be\\2a\\43\\c6\\f6\\d0\\b5\\a0\\c4\\24\\6e\\4f" \
+ "\\bd\\ec\\22\\8a\\07\\11\\3d\\f9\\d3\\15\\02\\03\\01\\00\\01\\a3" \
+ "\\82\\01\\26\\30\\82\\01\\22\\30\\1f\\06\\03\\55\\1d\\23\\04\\18" \
+ "\\30\\16\\80\\14\\f2\\9d\\42\\4e\\0f\\c4\\48\\25\\58\\2f\\1c\\ce" \
+ "\\0f\\a1\\3f\\22\\c8\\55\\c8\\91\\30\\3b\\06\\08\\2b\\06\\01\\05" \
+ "\\05\\07\\01\\01\\04\\2f\\30\\2d\\30\\2b\\06\\08\\2b\\06\\01\\05" \
+ "\\05\\07\\30\\01\\86\\1f\\68\\74\\74\\70\\3a\\2f\\2f\\69\\70\\61" \
+ "\\2d\\63\\61\\2e\\69\\70\\61\\2e\\64\\65\\76\\65\\6c\\2f\\63\\61" \
+ "\\2f\\6f\\63\\73\\70\\30\\0e\\06\\03\\55\\1d\\0f\\01\\01\\ff\\04" \
+ "\\04\\03\\02\\04\\f0\\30\\1d\\06\\03\\55\\1d\\25\\04\\16\\30\\14" \
+ "\\06\\08\\2b\\06\\01\\05\\05\\07\\03\\01\\06\\08\\2b\\06\\01\\05" \
+ "\\05\\07\\03\\02\\30\\74\\06\\03\\55\\1d\\1f\\04\\6d\\30\\6b\\30" \
+ "\\69\\a0\\31\\a0\\2f\\86\\2d\\68\\74\\74\\70\\3a\\2f\\2f\\69\\70" \
+ "\\61\\2d\\63\\61\\2e\\69\\70\\61\\2e\\64\\65\\76\\65\\6c\\2f\\69" \
+ "\\70\\61\\2f\\63\\72\\6c\\2f\\4d\\61\\73\\74\\65\\72\\43\\52\\4c" \
+ "\\2e\\62\\69\\6e\\a2\\34\\a4\\32\\30\\30\\31\\0e\\30\\0c\\06\\03" \
+ "\\55\\04\\0a\\0c\\05\\69\\70\\61\\63\\61\\31\\1e\\30\\1c\\06\\03" \
+ "\\55\\04\\03\\0c\\15\\43\\65\\72\\74\\69\\66\\69\\63\\61\\74\\65" \
+ "\\20\\41\\75\\74\\68\\6f\\72\\69\\74\\79\\30\\1d\\06\\03\\55\\1d" \
+ "\\0e\\04\\16\\04\\14\\2d\\2b\\3f\\cb\\f5\\b2\\ff\\32\\2c\\a8\\c2" \
+ "\\1c\\dd\\bd\\8c\\80\\1e\\dd\\31\\82\\30\\0d\\06\\09\\2a\\86\\48" \
+ "\\86\\f7\\0d\\01\\01\\0b\\05\\00\\03\\82\\01\\01\\00\\9a\\47\\2e" \
+ "\\50\\a7\\4d\\1d\\53\\0f\\c9\\71\\42\\0c\\e5\\da\\7d\\49\\64\\e7" \
+ "\\ab\\c8\\df\\df\\02\\c1\\87\\d1\\5b\\de\\da\\6f\\2b\\e4\\f0\\be" \
+ "\\ba\\09\\df\\02\\85\\0b\\8a\\e6\\9b\\06\\7d\\69\\38\\6c\\72\\ff" \
+ "\\4c\\7b\\2a\\0d\\3f\\23\\2f\\16\\46\\ff\\05\\93\\b0\\ea\\24\\28" \
+ "\\d7\\12\\a1\\57\\b8\\59\\19\\25\\f3\\43\\0a\\d3\\fd\\0f\\37\\8d" \
+ "\\b8\\ca\\15\\e7\\48\\8a\\a0\\c7\\c7\\4b\\7f\\01\\3c\\58\\d7\\37" \
+ "\\e5\\ff\\7d\\2b\\01\\ac\\0d\\9f\\51\\6a\\e5\\40\\24\\e6\\5e\\55" \
+ "\\0d\\f7\\b8\\2f\\42\\ac\\6d\\e5\\29\\6b\\c6\\0b\\a4\\bf\\19\\bd" \
+ "\\39\\27\\ee\\fe\\c5\\b3\\db\\62\\d4\\be\\d2\\47\\ba\\96\\30\\5a" \
+ "\\fd\\62\\00\\b8\\27\\5d\\2f\\3a\\94\\0b\\95\\35\\85\\40\\2c\\bc" \
+ "\\67\\df\\8a\\f9\\f1\\7b\\19\\96\\3e\\42\\48\\13\\23\\04\\95\\a9" \
+ "\\6b\\11\\33\\81\\47\\5a\\83\\72\\f6\\20\\fa\\8e\\41\\7b\\8f\\77" \
+ "\\47\\7c\\c7\\5d\\46\\f4\\4f\\fd\\81\\0a\\ae\\39\\27\\b6\\6a\\26" \
+ "\\63\\b1\\d3\\bf\\55\\83\\82\\9b\\36\\6c\\33\\64\\0f\\50\\c0\\55" \
+ "\\94\\13\\c3\\85\\f4\\d5\\71\\65\\d0\\c0\\dd\\fc\\e6\\ec\\9c\\5b" \
+ "\\f0\\11\\b5\\2c\\f3\\48\\c1\\36\\8c\\a2\\96\\48\\84"
+
+#define TEST_CERT2_BIN \
+ "\\30\\82\\06\\98\\30\\82\\05\\80\\a0\\03\\02\\01\\02\\02\\0a\\61" \
+ "\\22\\88\\c2\\00\\00\\00\\00\\02\\a6\\30\\0d\\06\\09\\2a\\86\\48" \
+ "\\86\\f7\\0d\\01\\01\\05\\05\\00\\30\\45\\31\\15\\30\\13\\06\\0a" \
+ "\\09\\92\\26\\89\\93\\f2\\2c\\64\\01\\19\\16\\05\\64\\65\\76\\65" \
+ "\\6c\\31\\12\\30\\10\\06\\0a\\09\\92\\26\\89\\93\\f2\\2c\\64\\01" \
+ "\\19\\16\\02\\61\\64\\31\\18\\30\\16\\06\\03\\55\\04\\03\\13\\0f" \
+ "\\61\\64\\2d\\41\\44\\2d\\53\\45\\52\\56\\45\\52\\2d\\43\\41\\30" \
+ "\\1e\\17\\0d\\31\\36\\31\\31\\31\\31\\31\\33\\35\\31\\31\\31\\5a" \
+ "\\17\\0d\\31\\37\\31\\31\\31\\31\\31\\33\\35\\31\\31\\31\\5a\\30" \
+ "\\70\\31\\15\\30\\13\\06\\0a\\09\\92\\26\\89\\93\\f2\\2c\\64\\01" \
+ "\\19\\16\\05\\64\\65\\76\\65\\6c\\31\\12\\30\\10\\06\\0a\\09\\92" \
+ "\\26\\89\\93\\f2\\2c\\64\\01\\19\\16\\02\\61\\64\\31\\0e\\30\\0c" \
+ "\\06\\03\\55\\04\\03\\13\\05\\55\\73\\65\\72\\73\\31\\0c\\30\\0a" \
+ "\\06\\03\\55\\04\\03\\13\\03\\74\\20\\75\\31\\25\\30\\23\\06\\09" \
+ "\\2a\\86\\48\\86\\f7\\0d\\01\\09\\01\\16\\16\\74\\65\\73\\74\\2e" \
+ "\\75\\73\\65\\72\\40\\65\\6d\\61\\69\\6c\\2e\\64\\6f\\6d\\61\\69" \
+ "\\6e\\30\\82\\01\\22\\30\\0d\\06\\09\\2a\\86\\48\\86\\f7\\0d\\01" \
+ "\\01\\01\\05\\00\\03\\82\\01\\0f\\00\\30\\82\\01\\0a\\02\\82\\01" \
+ "\\01\\00\\9c\\cf\\36\\99\\de\\63\\74\\2b\\77\\25\\9e\\24\\d9\\77" \
+ "\\4b\\5f\\98\\c0\\8c\\d7\\20\\91\\c0\\1c\\e8\\37\\45\\bf\\3c\\d9" \
+ "\\33\\bd\\e9\\de\\c9\\5d\\d4\\cd\\06\\0a\\0d\\d4\\f1\\7c\\74\\5b" \
+ "\\29\\d5\\66\\9c\\2c\\9f\\6b\\1a\\0f\\0d\\e6\\6c\\62\\a5\\41\\4f" \
+ "\\c3\\a4\\88\\27\\11\\5d\\b7\\b1\\fb\\f8\\8d\\ee\\43\\8d\\93\\b5" \
+ "\\8c\\b4\\34\\06\\f5\\e9\\2f\\5a\\26\\68\\d7\\43\\60\\82\\5e\\22" \
+ "\\a7\\c6\\34\\40\\19\\a5\\8e\\f0\\58\\9f\\16\\2d\\43\\3f\\0c\\da" \
+ "\\e2\\23\\f6\\09\\2a\\5e\\bd\\84\\27\\c8\\ab\\d5\\70\\f8\\3d\\9c" \
+ "\\14\\c2\\c2\\a2\\77\\e8\\44\\73\\10\\01\\34\\40\\1f\\c6\\2f\\a0" \
+ "\\70\\ee\\2f\\d5\\4b\\be\\4c\\c7\\45\\f7\\ac\\9c\\c3\\68\\5b\\1d" \
+ "\\5a\\4b\\77\\65\\76\\e4\\b3\\92\\f4\\84\\0a\\9e\\6a\\9c\\c9\\53" \
+ "\\42\\9f\\6d\\fe\\f9\\f5\\f2\\9a\\15\\50\\47\\ef\\f4\\06\\59\\c8" \
+ "\\50\\48\\4b\\46\\95\\68\\25\\c5\\bd\\4f\\65\\34\\00\\fc\\31\\69" \
+ "\\f8\\3e\\e0\\20\\83\\41\\27\\0b\\5c\\46\\98\\14\\f0\\07\\de\\02" \
+ "\\17\\b1\\d2\\9c\\be\\1c\\0d\\56\\22\\1b\\02\\fe\\da\\69\\b9\\ef" \
+ "\\91\\37\\39\\7f\\24\\da\\c4\\81\\5e\\82\\31\\2f\\98\\1d\\f7\\73" \
+ "\\5b\\23\\02\\03\\01\\00\\01\\a3\\82\\03\\5d\\30\\82\\03\\59\\30" \
+ "\\3d\\06\\09\\2b\\06\\01\\04\\01\\82\\37\\15\\07\\04\\30\\30\\2e" \
+ "\\06\\26\\2b\\06\\01\\04\\01\\82\\37\\15\\08\\87\\85\\a1\\23\\84" \
+ "\\c8\\b2\\26\\83\\9d\\9d\\21\\82\\d4\\a6\\1b\\86\\a3\\ba\\37\\81" \
+ "\\10\\85\\89\\d5\\02\\d6\\8f\\24\\02\\01\\64\\02\\01\\02\\30\\29" \
+ "\\06\\03\\55\\1d\\25\\04\\22\\30\\20\\06\\08\\2b\\06\\01\\05\\05" \
+ "\\07\\03\\02\\06\\08\\2b\\06\\01\\05\\05\\07\\03\\04\\06\\0a\\2b" \
+ "\\06\\01\\04\\01\\82\\37\\0a\\03\\04\\30\\0e\\06\\03\\55\\1d\\0f" \
+ "\\01\\01\\ff\\04\\04\\03\\02\\05\\a0\\30\\35\\06\\09\\2b\\06\\01" \
+ "\\04\\01\\82\\37\\15\\0a\\04\\28\\30\\26\\30\\0a\\06\\08\\2b\\06" \
+ "\\01\\05\\05\\07\\03\\02\\30\\0a\\06\\08\\2b\\06\\01\\05\\05\\07" \
+ "\\03\\04\\30\\0c\\06\\0a\\2b\\06\\01\\04\\01\\82\\37\\0a\\03\\04" \
+ "\\30\\81\\94\\06\\09\\2a\\86\\48\\86\\f7\\0d\\01\\09\\0f\\04\\81" \
+ "\\86\\30\\81\\83\\30\\0b\\06\\09\\60\\86\\48\\01\\65\\03\\04\\01" \
+ "\\2a\\30\\0b\\06\\09\\60\\86\\48\\01\\65\\03\\04\\01\\2d\\30\\0b" \
+ "\\06\\09\\60\\86\\48\\01\\65\\03\\04\\01\\16\\30\\0b\\06\\09\\60" \
+ "\\86\\48\\01\\65\\03\\04\\01\\19\\30\\0b\\06\\09\\60\\86\\48\\01" \
+ "\\65\\03\\04\\01\\02\\30\\0b\\06\\09\\60\\86\\48\\01\\65\\03\\04" \
+ "\\01\\05\\30\\0a\\06\\08\\2a\\86\\48\\86\\f7\\0d\\03\\07\\30\\07" \
+ "\\06\\05\\2b\\0e\\03\\02\\07\\30\\0e\\06\\08\\2a\\86\\48\\86\\f7" \
+ "\\0d\\03\\02\\02\\02\\00\\80\\30\\0e\\06\\08\\2a\\86\\48\\86\\f7" \
+ "\\0d\\03\\04\\02\\02\\02\\00\\30\\1d\\06\\03\\55\\1d\\0e\\04\\16" \
+ "\\04\\14\\49\\ac\\ad\\e0\\65\\30\\c4\\ce\\a0\\09\\03\\5b\\ad\\4a" \
+ "\\7b\\49\\5e\\c9\\6c\\b4\\30\\1f\\06\\03\\55\\1d\\23\\04\\18\\30" \
+ "\\16\\80\\14\\62\\50\\b6\\8d\\a1\\e6\\2d\\91\\bf\\b0\\54\\4d\\8f" \
+ "\\a8\\ca\\10\\ae\\b8\\dd\\54\\30\\81\\cc\\06\\03\\55\\1d\\1f\\04" \
+ "\\81\\c4\\30\\81\\c1\\30\\81\\be\\a0\\81\\bb\\a0\\81\\b8\\86\\81" \
+ "\\b5\\6c\\64\\61\\70\\3a\\2f\\2f\\2f\\43\\4e\\3d\\61\\64\\2d\\41" \
+ "\\44\\2d\\53\\45\\52\\56\\45\\52\\2d\\43\\41\\2c\\43\\4e\\3d\\61" \
+ "\\64\\2d\\73\\65\\72\\76\\65\\72\\2c\\43\\4e\\3d\\43\\44\\50\\2c" \
+ "\\43\\4e\\3d\\50\\75\\62\\6c\\69\\63\\25\\32\\30\\4b\\65\\79\\25" \
+ "\\32\\30\\53\\65\\72\\76\\69\\63\\65\\73\\2c\\43\\4e\\3d\\53\\65" \
+ "\\72\\76\\69\\63\\65\\73\\2c\\43\\4e\\3d\\43\\6f\\6e\\66\\69\\67" \
+ "\\75\\72\\61\\74\\69\\6f\\6e\\2c\\44\\43\\3d\\61\\64\\2c\\44\\43" \
+ "\\3d\\64\\65\\76\\65\\6c\\3f\\63\\65\\72\\74\\69\\66\\69\\63\\61" \
+ "\\74\\65\\52\\65\\76\\6f\\63\\61\\74\\69\\6f\\6e\\4c\\69\\73\\74" \
+ "\\3f\\62\\61\\73\\65\\3f\\6f\\62\\6a\\65\\63\\74\\43\\6c\\61\\73" \
+ "\\73\\3d\\63\\52\\4c\\44\\69\\73\\74\\72\\69\\62\\75\\74\\69\\6f" \
+ "\\6e\\50\\6f\\69\\6e\\74\\30\\81\\be\\06\\08\\2b\\06\\01\\05\\05" \
+ "\\07\\01\\01\\04\\81\\b1\\30\\81\\ae\\30\\81\\ab\\06\\08\\2b\\06" \
+ "\\01\\05\\05\\07\\30\\02\\86\\81\\9e\\6c\\64\\61\\70\\3a\\2f\\2f" \
+ "\\2f\\43\\4e\\3d\\61\\64\\2d\\41\\44\\2d\\53\\45\\52\\56\\45\\52" \
+ "\\2d\\43\\41\\2c\\43\\4e\\3d\\41\\49\\41\\2c\\43\\4e\\3d\\50\\75" \
+ "\\62\\6c\\69\\63\\25\\32\\30\\4b\\65\\79\\25\\32\\30\\53\\65\\72" \
+ "\\76\\69\\63\\65\\73\\2c\\43\\4e\\3d\\53\\65\\72\\76\\69\\63\\65" \
+ "\\73\\2c\\43\\4e\\3d\\43\\6f\\6e\\66\\69\\67\\75\\72\\61\\74\\69" \
+ "\\6f\\6e\\2c\\44\\43\\3d\\61\\64\\2c\\44\\43\\3d\\64\\65\\76\\65" \
+ "\\6c\\3f\\63\\41\\43\\65\\72\\74\\69\\66\\69\\63\\61\\74\\65\\3f" \
+ "\\62\\61\\73\\65\\3f\\6f\\62\\6a\\65\\63\\74\\43\\6c\\61\\73\\73" \
+ "\\3d\\63\\65\\72\\74\\69\\66\\69\\63\\61\\74\\69\\6f\\6e\\41\\75" \
+ "\\74\\68\\6f\\72\\69\\74\\79\\30\\3f\\06\\03\\55\\1d\\11\\04\\38" \
+ "\\30\\36\\a0\\1c\\06\\0a\\2b\\06\\01\\04\\01\\82\\37\\14\\02\\03" \
+ "\\a0\\0e\\0c\\0c\\74\\75\\31\\40\\61\\64\\2e\\64\\65\\76\\65\\6c" \
+ "\\81\\16\\74\\65\\73\\74\\2e\\75\\73\\65\\72\\40\\65\\6d\\61\\69" \
+ "\\6c\\2e\\64\\6f\\6d\\61\\69\\6e\\30\\0d\\06\\09\\2a\\86\\48\\86" \
+ "\\f7\\0d\\01\\01\\05\\05\\00\\03\\82\\01\\01\\00\\41\\45\\0a\\6d" \
+ "\\bb\\7f\\5c\\07\\0c\\c9\\b0\\39\\55\\6d\\7c\\b5\\02\\cd\\e8\\b2" \
+ "\\e5\\02\\94\\77\\60\\db\\d1\\af\\1d\\db\\44\\5f\\ce\\83\\db\\80" \
+ "\\2e\\e2\\b2\\08\\25\\82\\14\\cb\\48\\95\\20\\13\\6c\\a9\\aa\\f8" \
+ "\\31\\56\\ed\\c0\\3b\\d4\\ae\\2e\\e3\\8f\\05\\fc\\ab\\5f\\2a\\69" \
+ "\\23\\bc\\b8\\8c\\ec\\2d\\a9\\0b\\86\\95\\73\\73\\db\\17\\ce\\c6" \
+ "\\ae\\c5\\b4\\c1\\25\\87\\3b\\67\\43\\9e\\87\\5a\\e6\\b9\\a0\\28" \
+ "\\12\\3d\\a8\\2e\\d7\\5e\\ef\\65\\2d\\e6\\a5\\67\\84\\ac\\fd\\31" \
+ "\\c1\\78\\d8\\72\\51\\a2\\88\\55\\0f\\97\\47\\93\\07\\ea\\8a\\53" \
+ "\\27\\4e\\34\\54\\34\\1f\\a0\\6a\\03\\44\\fb\\23\\61\\8e\\87\\8e" \
+ "\\3c\\d0\\8f\\ae\\e4\\cf\\ee\\65\\a8\\ba\\96\\68\\08\\1c\\60\\e2" \
+ "\\4e\\11\\a3\\74\\b8\\a5\\4e\\ea\\6a\\82\\4c\\c2\\4d\\63\\8e\\9f" \
+ "\\7c\\2f\\a8\\c0\\62\\f8\\f7\\d9\\25\\c4\\91\\ab\\4d\\6a\\44\\af" \
+ "\\75\\93\\53\\03\\a4\\99\\c8\\cd\\91\\89\\60\\75\\30\\99\\76\\05" \
+ "\\5a\\a0\\03\\a7\\a1\\2c\\03\\04\\8f\\d4\\5a\\31\\52\\28\\5a\\e6" \
+ "\\a2\\d3\\43\\21\\5b\\dc\\a2\\1d\\55\\a9\\48\\c5\\c4\\aa\\f3\\8b" \
+ "\\e6\\3e\\75\\96\\e4\\3e\\64\\af\\e8\\a7\\6a\\b6"
+
+static void test_sss_certmap_get_search_filter(void **state)
+{
+ int ret;
+ struct sss_certmap_ctx *ctx;
+ char *filter;
+ char **domains;
+ const char *dom_list[] = {"test.dom", NULL};
+
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+
+ ret = sss_certmap_add_rule(ctx, 100,
+ "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
+ "LDAP:rule100=<I>{issuer_dn}<S>{subject_dn}", NULL);
+ assert_int_equal(ret, 0);
+
+ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
+ sizeof(test_cert_der),
+ &filter, &domains);
+ assert_int_equal(ret, 0);
+ assert_non_null(filter);
+ assert_string_equal(filter, "rule100=<I>CN=Certificate Authority,O=IPA.DEVEL"
+ "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
+ assert_null(domains);
+
+ ret = sss_certmap_add_rule(ctx, 99,
+ "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
+ "LDAP:rule99=<I>{issuer_dn}<S>{subject_dn}",
+ dom_list);
+ assert_int_equal(ret, 0);
+ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
+ sizeof(test_cert_der),
+ &filter, &domains);
+ assert_int_equal(ret, 0);
+ assert_non_null(filter);
+ assert_string_equal(filter, "rule99=<I>CN=Certificate Authority,O=IPA.DEVEL"
+ "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
+ assert_non_null(domains);
+ assert_string_equal(domains[0], "test.dom");
+ assert_null(domains[1]);
+
+ ret = sss_certmap_add_rule(ctx, 98,
+ "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
+ "LDAP:rule98=userCertificate;binary={cert!bin}",
+ dom_list);
+ assert_int_equal(ret, 0);
+ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
+ sizeof(test_cert_der),
+ &filter, &domains);
+ assert_int_equal(ret, 0);
+ assert_non_null(filter);
+ assert_string_equal(filter, "rule98=userCertificate;binary=" TEST_CERT_BIN);
+ assert_non_null(domains);
+ assert_string_equal(domains[0], "test.dom");
+ assert_null(domains[1]);
+
+ ret = sss_certmap_add_rule(ctx, 97,
+ "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
+ "LDAP:rule97=<I>{issuer_dn!nss_x500}<S>{subject_dn}",
+ dom_list);
+ assert_int_equal(ret, 0);
+ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
+ sizeof(test_cert_der),
+ &filter, &domains);
+ assert_int_equal(ret, 0);
+ assert_non_null(filter);
+ assert_string_equal(filter, "rule97=<I>O=IPA.DEVEL,CN=Certificate Authority"
+ "<S>CN=ipa-devel.ipa.devel,O=IPA.DEVEL");
+ assert_non_null(domains);
+ assert_string_equal(domains[0], "test.dom");
+ assert_null(domains[1]);
+
+ ret = sss_certmap_add_rule(ctx, 96,
+ "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
+ "LDAP:rule96=<I>{issuer_dn!nss_x500}<S>{subject_dn!nss_x500}",
+ dom_list);
+ assert_int_equal(ret, 0);
+ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
+ sizeof(test_cert_der),
+ &filter, &domains);
+ assert_int_equal(ret, 0);
+ assert_non_null(filter);
+ assert_string_equal(filter, "rule96=<I>O=IPA.DEVEL,CN=Certificate Authority"
+ "<S>O=IPA.DEVEL,CN=ipa-devel.ipa.devel");
+ assert_non_null(domains);
+ assert_string_equal(domains[0], "test.dom");
+ assert_null(domains[1]);
+
+ ret = sss_certmap_add_rule(ctx, 95,
+ "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
+ NULL, NULL);
+ assert_int_equal(ret, 0);
+ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
+ sizeof(test_cert_der),
+ &filter, &domains);
+ assert_int_equal(ret, 0);
+ assert_non_null(filter);
+ assert_string_equal(filter, "(userCertificate;binary=" TEST_CERT_BIN ")");
+ assert_null(domains);
+
+ ret = sss_certmap_add_rule(ctx, 94,
+ "KRB5:<ISSUER>CN=Certificate Authority,O=IPA.DEVEL",
+ "LDAP:rule94=<I>{issuer_dn!ad_x500}<S>{subject_dn!ad_x500}",
+ dom_list);
+ assert_int_equal(ret, 0);
+ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert_der),
+ sizeof(test_cert_der),
+ &filter, &domains);
+ assert_int_equal(ret, 0);
+ assert_non_null(filter);
+ assert_string_equal(filter, "rule94=<I>O=IPA.DEVEL,CN=Certificate Authority"
+ "<S>O=IPA.DEVEL,CN=ipa-devel.ipa.devel");
+ assert_non_null(domains);
+ assert_string_equal(domains[0], "test.dom");
+ assert_null(domains[1]);
+
+
+ ret = sss_certmap_add_rule(ctx, 89, NULL,
+ "(rule89={subject_nt_principal})",
+ NULL);
+ assert_int_equal(ret, 0);
+ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der),
+ sizeof(test_cert2_der),
+ &filter, &domains);
+ assert_int_equal(ret, 0);
+ assert_non_null(filter);
+ assert_string_equal(filter, "(rule89=tu1@ad.devel)");
+ assert_null(domains);
+
+ ret = sss_certmap_add_rule(ctx, 88, NULL,
+ "(rule88={subject_nt_principal.short_name})",
+ NULL);
+ assert_int_equal(ret, 0);
+ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der),
+ sizeof(test_cert2_der),
+ &filter, &domains);
+ assert_int_equal(ret, 0);
+ assert_non_null(filter);
+ assert_string_equal(filter, "(rule88=tu1)");
+ assert_null(domains);
+
+ ret = sss_certmap_add_rule(ctx, 87, NULL,
+ "LDAP:rule87=<I>{issuer_dn!nss_x500}<S>{subject_dn!nss_x500}",
+ NULL);
+ assert_int_equal(ret, 0);
+ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der),
+ sizeof(test_cert2_der),
+ &filter, &domains);
+ assert_int_equal(ret, 0);
+ assert_non_null(filter);
+ assert_string_equal(filter, "rule87=<I>DC=devel,DC=ad,CN=ad-AD-SERVER-CA"
+ "<S>DC=devel,DC=ad,CN=Users,CN=t u,E=test.user@email.domain");
+ assert_null(domains);
+
+ ret = sss_certmap_add_rule(ctx, 86, NULL,
+ "LDAP:rule86=<I>{issuer_dn!ad_x500}<S>{subject_dn!ad_x500}",
+ NULL);
+ assert_int_equal(ret, 0);
+ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der),
+ sizeof(test_cert2_der),
+ &filter, &domains);
+ assert_int_equal(ret, 0);
+ assert_non_null(filter);
+ assert_string_equal(filter, "rule86=<I>DC=devel,DC=ad,CN=ad-AD-SERVER-CA"
+ "<S>DC=devel,DC=ad,CN=Users,CN=t u,E=test.user@email.domain");
+ assert_null(domains);
+
+
+ sss_certmap_free_ctx(ctx);
+
+ /* check defaults when no rules are added yet */
+ ret = sss_certmap_init(NULL, ext_debug, NULL, &ctx);
+ assert_int_equal(ret, EOK);
+ assert_non_null(ctx);
+ assert_null(ctx->prio_list);
+ ret = sss_certmap_get_search_filter(ctx, discard_const(test_cert2_der),
+ sizeof(test_cert2_der),
+ &filter, &domains);
+ assert_int_equal(ret, 0);
+ assert_non_null(filter);
+ assert_string_equal(filter, "(userCertificate;binary=" TEST_CERT2_BIN")");
+ assert_null(domains);
+}
+
+int main(int argc, const char *argv[])
+{
+ int rv;
+ poptContext pc;
+ int opt;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_DEBUG_OPTS
+ POPT_TABLEEND
+ };
+
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_sss_certmap_init),
+ cmocka_unit_test(test_sss_certmap_add_rule),
+ cmocka_unit_test(test_sss_certmap_add_matching_rule),
+ cmocka_unit_test(test_check_ad_attr_name),
+ cmocka_unit_test(test_sss_cert_get_content),
+ cmocka_unit_test(test_sss_cert_get_content_2),
+ cmocka_unit_test(test_sss_certmap_match_cert),
+ cmocka_unit_test(test_sss_certmap_add_mapping_rule),
+ cmocka_unit_test(test_sss_certmap_get_search_filter),
+ };
+
+ /* Set debug level to invalid value so we can deside if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+ poptFreeContext(pc);
+
+ DEBUG_CLI_INIT(debug_level);
+
+#ifdef HAVE_NSS
+ nspr_nss_init();
+#endif
+
+ tests_set_cwd();
+ rv = cmocka_run_group_tests(tests, NULL, NULL);
+
+#ifdef HAVE_NSS
+ /* Cleanup NSS and NSPR to make valgrind happy. */
+ nspr_nss_cleanup();
+#endif
+
+ return rv;
+}
diff --git a/src/tests/dlopen-tests.c b/src/tests/dlopen-tests.c
index 419857cc7..3914317de 100644
--- a/src/tests/dlopen-tests.c
+++ b/src/tests/dlopen-tests.c
@@ -45,6 +45,7 @@ struct so {
{ "libsss_idmap.so", { LIBPFX"libsss_idmap.so", NULL } },
{ "libsss_nss_idmap.so", { LIBPFX"libsss_nss_idmap.so", NULL } },
{ "libnss_sss.so", { LIBPFX"libnss_sss.so", NULL } },
+ { "libsss_certmap.so", { LIBPFX"libsss_certmap.so", NULL } },
{ "pam_sss.so", { LIBPFX"pam_sss.so", NULL } },
#ifdef BUILD_LIBWBCLIENT
{ "libwbclient.so", { LIBPFX"libwbclient.so", NULL } },