dpward / rpms / sssd

Forked from rpms/sssd 3 years ago
Clone
Blob Blame History Raw
From 64d74f65ca66fafd67c99ac17c5e45e584f59d83 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Thu, 2 Feb 2017 11:24:02 +0100
Subject: [PATCH 04/15] certmap: add new library libsss_certmap
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

With this library it would be possible to map certificates and users not
only by adding the full certificate to the user's LDAP object but by
adding e.g. only parts like the issuer and subject name. Additionally
the library is also able to flexible select/match certificates based on
values in the certificate.

Details about mapping and matching rules can be found in the included
man page.

Related to https://pagure.io/SSSD/sssd/issue/3050

Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
---
 Makefile.am                                |   55 ++
 configure.ac                               |    1 +
 contrib/sssd.spec.in                       |   32 +
 src/lib/certmap/sss_cert_content_nss.c     | 1014 +++++++++++++++++++
 src/lib/certmap/sss_certmap.c              |  993 +++++++++++++++++++
 src/lib/certmap/sss_certmap.doxy.in        |    3 +
 src/lib/certmap/sss_certmap.exports        |   13 +
 src/lib/certmap/sss_certmap.h              |  155 +++
 src/lib/certmap/sss_certmap.pc.in          |   11 +
 src/lib/certmap/sss_certmap_attr_names.c   |  107 +++
 src/lib/certmap/sss_certmap_int.h          |  187 ++++
 src/lib/certmap/sss_certmap_krb5_match.c   |  558 +++++++++++
 src/lib/certmap/sss_certmap_ldap_mapping.c |  367 +++++++
 src/man/Makefile.am                        |    2 +-
 src/man/po/po4a.cfg                        |    1 +
 src/man/sss-certmap.5.xml                  |  600 ++++++++++++
 src/tests/cmocka/test_certmap.c            | 1443 ++++++++++++++++++++++++++++
 src/tests/dlopen-tests.c                   |    1 +
 18 files changed, 5542 insertions(+), 1 deletion(-)
 create mode 100644 src/lib/certmap/sss_cert_content_nss.c
 create mode 100644 src/lib/certmap/sss_certmap.c
 create mode 100644 src/lib/certmap/sss_certmap.doxy.in
 create mode 100644 src/lib/certmap/sss_certmap.exports
 create mode 100644 src/lib/certmap/sss_certmap.h
 create mode 100644 src/lib/certmap/sss_certmap.pc.in
 create mode 100644 src/lib/certmap/sss_certmap_attr_names.c
 create mode 100644 src/lib/certmap/sss_certmap_int.h
 create mode 100644 src/lib/certmap/sss_certmap_krb5_match.c
 create mode 100644 src/lib/certmap/sss_certmap_ldap_mapping.c
 create mode 100644 src/man/sss-certmap.5.xml
 create mode 100644 src/tests/cmocka/test_certmap.c

diff --git a/Makefile.am b/Makefile.am
index 6dae4f2dd7f2dee501add82c7ab4f15fcbcc59ac..8ca12c10d2713b6a72361d84b25486500c79f407 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 e6a3b4e857bcbec16873f54008e6b42aeb9b7cd7..dd1012015a5fea9f25e5b5199b4868fbc0bc14c4 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 5bd2beb89014ba7c86b4231f0357a7a6e10a18a8..28ebe07a26a3112210b092b7831e7f6aae061c8d 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 0000000000000000000000000000000000000000..d3182895465706c87503b8abb0cea49b7677625d
--- /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 0000000000000000000000000000000000000000..080cab8ab585ce64a88c352a23a8062887fa720a
--- /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 0000000000000000000000000000000000000000..e8959e2099a0f517c314833e47d410306fb02702
--- /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 0000000000000000000000000000000000000000..8b5d5366697401649547c09c6b6f3db571c2b518
--- /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 0000000000000000000000000000000000000000..55485cc35e8bd7cf2cb2b0c5a06a7521025e3c43
--- /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 0000000000000000000000000000000000000000..f1a4432fce8ccd5642a622ca6a8d3a7954fc7ba3
--- /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 0000000000000000000000000000000000000000..a28a464910728cdd1f316b2b979da84f440685ea
--- /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 0000000000000000000000000000000000000000..28f1c596cfb5e78077b6a8e9baefa88b4900a022
--- /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 0000000000000000000000000000000000000000..e40f17b8ace46e61087e0a2fa570a362a84cead2
--- /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 0000000000000000000000000000000000000000..c64c05b311f043b4d70f98f718780601c3e6a002
--- /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 215ce693b56e74db394dbc238c03c87f5f6efe99..142d6e2743f814294e3d92c8342070b8230bb3e5 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 ffcf9a2793da3c0115bc846744ccb9592a9a68ef..d1f6ac39f841c61ae3d2393fb3402dc21b9cbd69 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 0000000000000000000000000000000000000000..bbe68509f2222613a7ed69599519d7fca0506df0
--- /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 0000000000000000000000000000000000000000..c998443d086eaa72cc2a05c38ddfc5ba590a1ce7
--- /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 419857cc739d197493e46629d00aa5fb6cfde824..3914317de90f870fab42d2d72d8e2eb5ea8d9e14 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 } },
-- 
2.9.3