diff options
author | Sumit Bose <sbose@redhat.com> | 2017-02-02 11:24:02 +0100 |
---|---|---|
committer | Sumit Bose <sbose@redhat.com> | 2017-02-13 17:31:47 +0100 |
commit | 0fe77bbfa9d4e6099c29c7fdcb10971c3ee768b9 (patch) | |
tree | 5005340c8bad32cf4926adc1cff40de5b74ac7f4 | |
parent | a900fb16256367af2daceec381e1908ecf17c15e (diff) | |
download | sssd-0fe77bbfa9d4e6099c29c7fdcb10971c3ee768b9.tar.gz sssd-0fe77bbfa9d4e6099c29c7fdcb10971c3ee768b9.tar.xz sssd-0fe77bbfa9d4e6099c29c7fdcb10971c3ee768b9.zip |
certmap: add new library libsss_certmap
-rw-r--r-- | Makefile.am | 54 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | contrib/sssd.spec.in | 31 | ||||
-rw-r--r-- | src/lib/certmap/sss_cert_content_nss.c | 346 | ||||
-rw-r--r-- | src/lib/certmap/sss_certmap.c | 1398 | ||||
-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 | 69 | ||||
-rw-r--r-- | src/lib/certmap/sss_certmap.pc.in | 11 | ||||
-rw-r--r-- | src/lib/certmap/sss_certmap_int.h | 140 | ||||
-rw-r--r-- | src/tests/cmocka/test_certmap.c | 853 | ||||
-rw-r--r-- | src/tests/dlopen-tests.c | 1 |
12 files changed, 2919 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index a3965ec00..95cf55e18 100644 --- a/Makefile.am +++ b/Makefile.am @@ -274,6 +274,7 @@ if HAVE_CMOCKA test_ipa_dn \ simple-access-tests \ krb5_common_test \ + sss_certmap_test \ $(NULL) if HAVE_LIBRESOLV @@ -335,7 +336,8 @@ sssdlib_LTLIBRARIES = \ libsss_ldap.la \ libsss_krb5.la \ libsss_proxy.la \ - libsss_simple.la + libsss_simple.la \ + $(NULL) if BUILD_SAMBA sssdlib_LTLIBRARIES += \ @@ -1044,6 +1046,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 @@ -1098,6 +1101,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 @@ -1662,6 +1666,35 @@ sss_ssh_knownhostsproxy_LDADD = \ $(CLIENT_LIBS) $(TALLOC_LIBS) $(POPT_LIBS) 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_cert_content_nss.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 # ################# @@ -3157,6 +3190,24 @@ krb5_common_test_LDADD = \ libdlopen_test_providers.la \ $(NULL) +if HAVE_NSS +sss_certmap_test_SOURCES = \ + src/tests/cmocka/test_certmap.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 @@ -4275,6 +4326,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 d264abf3e..e82c010b2 100644 --- a/configure.ac +++ b/configure.ac @@ -482,6 +482,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 c0c9e364b..bbb5eb5d6 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} @@ -1139,6 +1158,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..c166877e8 --- /dev/null +++ b/src/lib/certmap/sss_cert_content_nss.c @@ -0,0 +1,346 @@ +/* + 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 <nspr4/prprf.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) { + 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; +} + +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; + NSSInitParameters parameters = { 0 }; + parameters.length = sizeof (parameters); + + 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; + } + + 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..0028a120c --- /dev/null +++ b/src/lib/certmap/sss_certmap.c @@ -0,0 +1,1398 @@ +/* + 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" + +#include "util/crypto/nss/nss_util.h" + +int debug_level; +void sss_debug_fn(const char *file, + long line, + const char *function, + int level, + const char *format, ...) +{ + return; +} + +//#define ERRMSG(text) text "("__FILE__":"__LINE__" "__FUNCTION__")" +#define ERRMSG(text) text + +int sss_certmap_init(TALLOC_CTX *mem_ctx, + sss_certmap_ext_debug *debug, void *debug_priv, + struct sss_certmap_ctx **ctx) +{ + 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; + + CM_DEBUG((*ctx), "sss_certmap initialized.\n"); + 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); +} + +const char *sss_certmap_err_msg(struct sss_certmap_ctx *ctx) +{ + if (ctx->err_msg != NULL) { + return ctx->err_msg; + } + + return "(no error messages available)"; +} + +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 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 + * + * TODO: + * generic extension handling for e.g. MSFT 1.3.6.1.4.1.311.20.2 + */ + +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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + 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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + ret = ENOMEM; + goto done; + } + if (*(comp->val) == '\0') { + ctx->err_msg = ERRMSG("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) { + ctx->err_msg = ERRMSG("Failed to parse regexp."); + goto done; + } + + ret = split_on_separator(mem_ctx, comp->val, ',', true, true, + &eku_list, &eku_list_size); + if (ret != 0) { + ctx->err_msg = ERRMSG("Failed to split list."); + 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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + 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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + 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]; + while (*o != '\0') { + if (*o != '.' && !isdigit(*o)) { + break; + } + o++; + } + if (*o == '\0' && *(o - 1) != '.') { + /* 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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + 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) { + ctx->err_msg = ERRMSG("Failed to get value."); + 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) { + ctx->err_msg = ERRMSG("Failed to split list."); + 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) { + ctx->err_msg = ERRMSG("Failed to parse regexp."); + goto done; + } + + ret = regcomp(&(comp->regexp), comp->val, REG_EXTENDED); + if (ret != 0) { + ctx->err_msg = ERRMSG("Failed to parse regexp."); + goto done; + } + + ret = 0; + +done: + if (ret == 0) { + *_comp = comp; + } else { + talloc_free(comp); + } + + return ret; +} + +static 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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + 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 != '<') { + ctx->err_msg = ERRMSG("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 { + ctx->err_msg = ERRMSG("Invalid KRB5 matching rule."); + ret = EINVAL; + goto done; + } + } + + ret = 0; + +done: + if (ret == 0) { + *match_rule = rule; + } else { + talloc_free(rule); + } + + return ret; +} + +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) { + ctx->err_msg = ERRMSG("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) { + ctx->err_msg = ERRMSG("Failed to parse KRB5 matching rule.\n"); + goto done; + } + } else { + ctx->err_msg = ERRMSG("Unsupported matching rule type.\n"); + ret = ESRCH; + goto done; + } + + ret = EOK; + +done: + talloc_free(type); + + return ret; +} + +struct template_table { + const char *name; + const char **attr_name; + const char **conversion; +}; + +const char *empty[] = {NULL}; +const char *name_attr[] = {"name", NULL}; +const char *x500_conv[] = {"x500", "x500ad", NULL}; +const char *bin_conv[] = {"bin", "base64", NULL}; + +struct template_table template_table[] = { + {"issuer_dn", empty, x500_conv}, + {"subject_dn", empty, x500_conv}, + {"subject_nt_principal", name_attr, empty}, + {"cert", empty, bin_conv}, + {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) { + //ctx->err_msg = ERRMSG("Memory allocation failed."); + ret = ENOMEM; + goto done; + } + + dot = strchr(template, '.'); + if (dot != NULL) { + p = strchr(dot + 1, '.'); + if (p != NULL) { + //ctx->err_msg = ERRMSG("Only one '.' allowed in template."); + CM_DEBUG(ctx, "Only one '.' allowed in template."); + ret = EINVAL; + goto done; + } + + if (dot == template) { + //ctx->err_msg = ERRMSG("Missing name in 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) { + //ctx->err_msg = ERRMSG("Only one '!' allowed in template."); + CM_DEBUG(ctx, "Only one '!' allowed in template."); + ret = EINVAL; + goto done; + } + + if (excl == template) { + //ctx->err_msg = ERRMSG("Missing name in 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) { + //ctx->err_msg = ERRMSG("Memory allocation failed."); + 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) { + //ctx->err_msg = ERRMSG("Memory allocation failed."); + CM_DEBUG(ctx, "Memory allocation failed."); + 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); +} + +static 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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + ret = ENOMEM; + goto done; + } + + tmp_string_size = strlen(rule_start) + 1; + tmp_string = talloc_zero_size(ctx, tmp_string_size); + if (tmp_string == NULL) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + ret = ENOMEM; + goto done; + } + + cur = rule_start; + c = 0; + + while (*cur != '\0') { + if (c > tmp_string_size) { + ctx->err_msg = ERRMSG("Cannot parse mapping rule."); + ret = EIO; + goto done; + } + switch (*cur) { + case '{': + if (in_template) { + ctx->err_msg = ERRMSG("'{' 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) { + ctx->err_msg = ERRMSG("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) { + ctx->err_msg = ERRMSG("'}}' 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) { + ctx->err_msg = ERRMSG("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) { + ctx->err_msg = ERRMSG("Rule ended inside template."); + ret = EINVAL; + goto done; + } + if (c != 0) { + ret = add_string(ctx, rule, tmp_string); + if (ret != 0) { + ctx->err_msg = ERRMSG("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; +} + +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) { + ctx->err_msg = ERRMSG("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) { + ctx->err_msg = ERRMSG("Failed to parse LDAP mapping rule.\n"); + goto done; + } + } else { + ctx->err_msg = ERRMSG("Unsupported mapping rule type.\n"); + ret = ESRCH; + goto done; + } + + ret = EOK; + +done: + talloc_free(type); + + return ret; +} + +int sss_certmap_add_rule(struct sss_certmap_ctx *ctx, + unsigned int 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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + return ENOMEM; + } + + rule = talloc_zero(tmp_ctx, struct match_map_rule); + if (rule == NULL) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + 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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + 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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + 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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + 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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + ret = ENOMEM; + goto done; + } + } + } + + if (ctx->prio_list == NULL) { + ctx->prio_list = talloc_zero(ctx, struct priority_list); + if (ctx->prio_list == NULL) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + 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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + 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) { + ctx->err_msg = ERRMSG("bin conversion failed."); + 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) { + ctx->err_msg = ERRMSG("base64 conversion failed."); + CM_DEBUG(ctx, "base64 conversion failed."); + ret = ENOMEM; + goto done; + } + } else { + ctx->err_msg = ERRMSG("Unsupported conversion."); + 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; + + str = talloc_strdup(ctx, ""); + if (str == NULL) { + ret = ENOMEM; + goto done; + } + if (conversion == NULL || strcmp(conversion, "ldap") == 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, "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 { + ret = EINVAL; + goto done; + } + + ret = 0; + +done: + if (ret == 0) { + *result = str; + } else { + talloc_free(str); + } + + 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; + + if (strcmp("issuer_dn", parsed_template->name) == 0) { + //exp = talloc_strdup(ctx, cert_content->issuer_str); + ret = get_dn_str(ctx, parsed_template->conversion, + cert_content->issuer_rdn_list, &exp); + } else if (strcmp("subject_dn", parsed_template->name) == 0) { + //exp = talloc_strdup(ctx, cert_content->subject_str); + ret = get_dn_str(ctx, parsed_template->conversion, + cert_content->subject_rdn_list, &exp); + } else if (strcmp("cert", parsed_template->name) == 0) { + ret = expand_cert(ctx, parsed_template, cert_content, &exp); + } else { + ctx->err_msg = ERRMSG("Unsupported template name."); + CM_DEBUG(ctx, "Unsupported template name."); + ret = EINVAL; + goto done; + } + if (ret != 0) { + ctx->err_msg = ERRMSG("Failed to expand template."); + CM_DEBUG(ctx, "Failed to expand [%s] template.", parsed_template->name); + goto done; + } + + if (exp == NULL) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + 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) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + 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."); + ctx->err_msg = ERRMSG("Failed to expand template."); + goto done; + } + + result = talloc_strdup_append(result, expanded); + talloc_free(expanded); + expanded = NULL; + if (result == NULL) { + ctx->err_msg = ERRMSG("Memory allocation failed."); + ret = ENOMEM; + goto done; + } + } else { + ret = EINVAL; + CM_DEBUG(ctx, "Unsupported component type."); + ctx->err_msg = ERRMSG("Unsupported component type."); + goto done; + } + } + + ret = 0; +done: + talloc_free(expanded); + if (ret == 0) { + *filter = result; + } else { + talloc_free(result); + } + + return ret; +} + +static int do_match(struct krb5_match_rule *parsed_match_rule, + struct sss_cert_content *cert_content) +{ + struct component_list *comp; + bool match = false; + + if (parsed_match_rule == NULL || cert_content == NULL) { + return EINVAL; + } + + /* Issuer */ + if (cert_content->issuer_str != NULL) { + for (comp = parsed_match_rule->issuer; comp != NULL; comp = comp->next) { + match = (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; + } + + } + } + + if (cert_content->subject_str != NULL) { + for (comp = parsed_match_rule->subject; comp != NULL; comp = comp->next) { + if (match && parsed_match_rule->r == relation_or) { + match = (regexec(&(comp->regexp), cert_content->issuer_str, + 0, NULL, 0) == 0); + /* 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, + 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) { + ctx->err_msg = ERRMSG("Failed to get certificate content."); + return ret; + } + + for (p = ctx->prio_list; p != NULL; p = p->next) { + for (r = p->rule_list; r != NULL; r = r->next) { + ret = do_match(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, + 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) { + ctx->err_msg = ERRMSG("Failed to get certificate content."); + return ret; + } + + for (p = ctx->prio_list; p != NULL; p = p->next) { + for (r = p->rule_list; r != NULL; r = r->next) { + ret = do_match(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"); + ctx->err_msg = ERRMSG("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) { + ctx->err_msg = ERRMSG("Memory allocation false."); + ret = ENOMEM; + goto done; + } + + for (c = 0; r->domains[c] != NULL; c++) { + domains[c] = talloc_strdup(domains, r->domains[c]); + if (domains[c] == NULL) { + ctx->err_msg = ERRMSG("Memory allocation false."); + 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; +} 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..2e3f7f76e --- /dev/null +++ b/src/lib/certmap/sss_certmap.h @@ -0,0 +1,69 @@ +/* + 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> + +/** + * Opaque type for the idmap context + */ +struct sss_certmap_ctx; + +#define SSS_CERTMAP_MIN_PRIO UINT32_MAX + +typedef void (sss_certmap_ext_debug)(void *private, + const char *file, long line, + const char *function, + const char *format, ...); +/** + * @brief + */ +int sss_certmap_init(TALLOC_CTX *mem_ctx, + sss_certmap_ext_debug *debug, void *debug_priv, + struct sss_certmap_ctx **ctx); + +void sss_certmap_free_ctx(struct sss_certmap_ctx *ctx); + +const char *sss_certmap_err_msg(struct sss_certmap_ctx *ctx); + +int sss_certmap_add_rule(struct sss_certmap_ctx *ctx, + unsigned int priority, const char *match_rule, + const char *map_rule, const char **domains); + +int sss_certmap_match_cert(struct sss_certmap_ctx *ctx, + uint8_t *der_cert, size_t der_size); + +int sss_certmap_get_search_filter(struct sss_certmap_ctx *ctx, + uint8_t *der_cert, size_t der_size, + char **filter, char ***domains); + +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..e60b476ef --- /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: http://fedorahosted.org/sssd/ diff --git a/src/lib/certmap/sss_certmap_int.h b/src/lib/certmap/sss_certmap_int.h new file mode 100644 index 000000000..12954a326 --- /dev/null +++ b/src/lib/certmap/sss_certmap_int.h @@ -0,0 +1,140 @@ +/* + 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}" + +/* 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; + 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; +}; + +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 { + unsigned int 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 { + unsigned int priority; + struct match_map_rule *rule_list; + struct priority_list *prev; + struct priority_list *next; +}; + +struct sss_certmap_ctx { + struct priority_list *prio_list; + const char *err_msg; + sss_certmap_ext_debug *debug; + void *debug_priv; +}; + +/* 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; + + 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); +#endif /* __SSS_CERTMAP_INT_H__ */ diff --git a/src/tests/cmocka/test_certmap.c b/src/tests/cmocka/test_certmap.c new file mode 100644 index 000000000..ab89db6ff --- /dev/null +++ b/src/tests/cmocka/test_certmap.c @@ -0,0 +1,853 @@ +/* + 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" + +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, "<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]); + + talloc_free(ctx); +} + +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}; + +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); +} + +static void test_sss_certmap_match_cert(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, "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); +} + +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" +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:<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, "<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:<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, "<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: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, "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:<I>{issuer_dn!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, "<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:<I>{issuer_dn!x500}<S>{subject_dn!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, "<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); + + sss_certmap_free_ctx(ctx); +} + +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_sss_cert_get_content), + 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); + + tests_set_cwd(); + rv = cmocka_run_group_tests(tests, NULL, NULL); + + return rv; +} diff --git a/src/tests/dlopen-tests.c b/src/tests/dlopen-tests.c index d11ae9651..89b4dae22 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 } }, |