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