summaryrefslogtreecommitdiffstats
path: root/lib/ldaputil/cert.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ldaputil/cert.c')
-rw-r--r--lib/ldaputil/cert.c452
1 files changed, 452 insertions, 0 deletions
diff --git a/lib/ldaputil/cert.c b/lib/ldaputil/cert.c
new file mode 100644
index 00000000..34c41845
--- /dev/null
+++ b/lib/ldaputil/cert.c
@@ -0,0 +1,452 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <string.h>
+#include <malloc.h>
+
+/* removed for ns security integration
+#include <sec.h>
+*/
+#include "prmem.h"
+#include "key.h"
+#include "cert.h"
+#include <ldaputil/certmap.h>
+#include <ldaputil/errors.h>
+#include <ldaputil/cert.h>
+#include "ldaputili.h"
+
+NSAPI_PUBLIC int ldapu_get_cert (void *SSLendpoint, void **cert)
+{
+ /* TEMPORARY -- not implemented yet*/
+ return LDAPU_FAILED;
+}
+
+
+NSAPI_PUBLIC int ldapu_get_cert_subject_dn (void *cert_in, char **subjectDN)
+{
+ CERTCertificate *cert = (CERTCertificate *)cert_in;
+ char *cert_subject = CERT_NameToAscii(&cert->subject);
+
+ if (cert_subject != NULL)
+ *subjectDN = strdup(cert_subject);
+ else
+ *subjectDN=NULL;
+
+ PR_Free(cert_subject);
+ return *subjectDN ? LDAPU_SUCCESS : LDAPU_ERR_EXTRACT_SUBJECTDN_FAILED;
+}
+
+NSAPI_PUBLIC int ldapu_get_cert_issuer_dn (void *cert_in, char **issuerDN)
+{
+ CERTCertificate *cert = (CERTCertificate *)cert_in;
+ char *cert_issuer = CERT_NameToAscii(&cert->issuer);
+
+ *issuerDN = strdup(cert_issuer);
+ PR_Free(cert_issuer);
+
+ return *issuerDN ? LDAPU_SUCCESS : LDAPU_ERR_EXTRACT_ISSUERDN_FAILED;
+}
+
+NSAPI_PUBLIC int ldapu_get_cert_der (void *cert_in, unsigned char **der,
+ unsigned int *len)
+{
+ CERTCertificate *cert = (CERTCertificate *)cert_in;
+ SECItem derCert = ((CERTCertificate*)cert)->derCert;
+ unsigned char *data = derCert.data;
+
+ *len = derCert.len;
+ *der = (unsigned char *)malloc(*len);
+
+ if (!*der) return LDAPU_ERR_OUT_OF_MEMORY;
+
+ memcpy(*der, data, *len);
+
+ return *len ? LDAPU_SUCCESS : LDAPU_ERR_EXTRACT_DERCERT_FAILED;
+}
+
+static int certmap_name_to_secoid (const char *str)
+{
+ if (!ldapu_strcasecmp(str, "c")) return SEC_OID_AVA_COUNTRY_NAME;
+ if (!ldapu_strcasecmp(str, "o")) return SEC_OID_AVA_ORGANIZATION_NAME;
+ if (!ldapu_strcasecmp(str, "cn")) return SEC_OID_AVA_COMMON_NAME;
+ if (!ldapu_strcasecmp(str, "l")) return SEC_OID_AVA_LOCALITY;
+ if (!ldapu_strcasecmp(str, "st")) return SEC_OID_AVA_STATE_OR_PROVINCE;
+ if (!ldapu_strcasecmp(str, "ou")) return SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME;
+ if (!ldapu_strcasecmp(str, "uid")) return SEC_OID_RFC1274_UID;
+ if (!ldapu_strcasecmp(str, "e")) return SEC_OID_PKCS9_EMAIL_ADDRESS;
+ if (!ldapu_strcasecmp(str, "mail")) return SEC_OID_RFC1274_MAIL;
+ if (!ldapu_strcasecmp(str, "dc")) return SEC_OID_AVA_DC;
+
+ return SEC_OID_AVA_UNKNOWN; /* return invalid OID */
+}
+
+NSAPI_PUBLIC int ldapu_get_cert_ava_val (void *cert_in, int which_dn,
+ const char *attr, char ***val_out)
+{
+ CERTCertificate *cert = (CERTCertificate *)cert_in;
+ CERTName *cert_dn;
+ CERTRDN **rdns;
+ CERTRDN **rdn;
+ CERTAVA **avas;
+ CERTAVA *ava;
+ int attr_tag = certmap_name_to_secoid(attr);
+ char **val;
+ char **ptr;
+ int rv;
+
+ *val_out = 0;
+
+ if (attr_tag == SEC_OID_AVA_UNKNOWN) {
+ return LDAPU_ERR_INVALID_ARGUMENT;
+ }
+
+ if (which_dn == LDAPU_SUBJECT_DN)
+ cert_dn = &cert->subject;
+ else if (which_dn == LDAPU_ISSUER_DN)
+ cert_dn = &cert->issuer;
+ else
+ return LDAPU_ERR_INVALID_ARGUMENT;
+
+ val = (char **)malloc(32*sizeof(char *));
+
+ if (!val) return LDAPU_ERR_OUT_OF_MEMORY;
+
+ ptr = val;
+
+ rdns = cert_dn->rdns;
+
+ if (rdns) {
+ for (rdn = rdns; *rdn; rdn++) {
+ avas = (*rdn)->avas;
+ while ((ava = *avas++) != NULL) {
+ int tag = CERT_GetAVATag(ava);
+
+ if (tag == attr_tag) {
+ char buf[BIG_LINE];
+ int lenLen;
+ int vallen;
+ /* Found it */
+
+ /* Copied from ns/lib/libsec ...
+ * XXX this code is incorrect in general
+ * -- should use a DER template.
+ */
+ lenLen = 2;
+ if (ava->value.len >= 128) lenLen = 3;
+ vallen = ava->value.len - lenLen;
+
+ rv = CERT_RFC1485_EscapeAndQuote(buf,
+ BIG_LINE,
+ (char*) ava->value.data + lenLen,
+ vallen);
+
+ if (rv == SECSuccess) {
+ *ptr++ = strdup(buf);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ *ptr = 0;
+
+ if (*val) {
+ /* At least one value found */
+ *val_out = val;
+ rv = LDAPU_SUCCESS;
+ }
+ else {
+ free(val);
+ rv = LDAPU_FAILED;
+ }
+
+ return rv;
+}
+
+static void
+_rdns_free (char*** rdns)
+{
+ auto char*** rdn;
+ for (rdn = rdns; *rdn; ++rdn) {
+ ldap_value_free (*rdn);
+ }
+ free (rdns);
+}
+
+static char***
+_explode_dn (const char* dn)
+{
+ auto char*** exp = NULL;
+ if (dn && *dn) {
+ auto char** rdns = ldap_explode_dn (dn, 0);
+ if (rdns) {
+ auto size_t expLen = 0;
+ auto char** rdn;
+ for (rdn = rdns; *rdn; ++rdn) {
+ auto char** avas = ldap_explode_rdn (*rdn, 0);
+ if (avas && *avas) {
+ exp = (char***) ldapu_realloc (exp, sizeof(char**) * (expLen + 2));
+ if (exp) {
+ exp[expLen++] = avas;
+ } else {
+ ldap_value_free (avas);
+ break;
+ }
+ } else { /* parse error */
+ if (avas) {
+ ldap_value_free (avas);
+ }
+ if (exp) {
+ exp[expLen] = NULL;
+ _rdns_free (exp);
+ exp = NULL;
+ }
+ break;
+ }
+ }
+ if (exp) {
+ exp[expLen] = NULL;
+ }
+ ldap_value_free (rdns);
+ }
+ }
+ return exp;
+}
+
+static size_t
+_rdns_count (char*** rdns)
+{
+ auto size_t count = 0;
+ auto char*** rdn;
+ for (rdn = rdns; *rdns; ++rdns) {
+ auto char** ava;
+ for (ava = *rdns; *ava; ++ava) {
+ ++count;
+ }
+ }
+ return count;
+}
+
+static int
+_replaceAVA (char* attr, char** avas)
+{
+ if (attr && avas) {
+ for (; *avas; ++avas) {
+ if (!ldapu_strcasecmp (*avas, attr)) {
+ *avas = attr;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+struct _attr_getter_pair {
+ char* (*getter) (CERTName* dn);
+ const char* name1;
+ const char* name2;
+} _attr_getter_table[] =
+{
+ {NULL, "OU", "organizationalUnitName"},
+ {CERT_GetOrgName, "O", "organizationName"},
+ {CERT_GetCommonName, "CN", "commonName"},
+ {CERT_GetCertEmailAddress, "E", NULL},
+ {CERT_GetCertEmailAddress, "MAIL", "rfc822mailbox"},
+ {CERT_GetCertUid, "uid", NULL},
+ {CERT_GetCountryName, "C", "country"},
+ {CERT_GetStateName, "ST", "state"},
+ {CERT_GetLocalityName, "L", "localityName"},
+ {CERT_GetDomainComponentName, "DC", "dc"},
+ {NULL, NULL, NULL}
+};
+
+static int
+_is_OU (const char* attr)
+{
+ auto struct _attr_getter_pair* descAttr;
+ for (descAttr = _attr_getter_table; descAttr->name1; ++descAttr) {
+ if (descAttr->getter == NULL) { /* OU attribute */
+ if (!ldapu_strcasecmp (attr, descAttr->name1) || (descAttr->name2 &&
+ !ldapu_strcasecmp (attr, descAttr->name2))) {
+ return 1;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static char**
+_previous_OU (char** ava, char** avas)
+{
+ while (ava != avas) {
+ --ava;
+ if (_is_OU (*ava)) {
+ return ava;
+ }
+ }
+ return NULL;
+}
+
+static char*
+_value_normalize (char* value)
+ /* Remove leading and trailing spaces, and
+ change consecutive spaces to a single space.
+ */
+{
+ auto char* t;
+ auto char* f;
+ t = f = value;
+ while (*f == ' ') ++f; /* ignore leading spaces */
+ for (; *f; ++f) {
+ if (*f != ' ' || t[-1] != ' ') {
+ *t++ = *f; /* no consecutive spaces */
+ }
+ }
+ if (t > value && t[-1] == ' ') {
+ --t; /* ignore trailing space */
+ }
+ *t = '\0';
+ return value;
+}
+
+static int
+_explode_AVA (char* AVA)
+ /* Change an attributeTypeAndValue a la <draft-ietf-asid-ldapv3-dn>,
+ to the type name, followed immediately by the attribute value,
+ both normalized.
+ */
+{
+ auto char* value = strchr (AVA, '=');
+ if (!value) return LDAPU_FAILED;
+ *value++ = '\0';
+ _value_normalize (AVA);
+ _value_normalize (value);
+ {
+ auto char* typeEnd = AVA + strlen (AVA);
+ if ((typeEnd + 1) != value) {
+ memmove (typeEnd+1, value, strlen(value)+1);
+ }
+ }
+ return LDAPU_SUCCESS;
+}
+
+static char*
+_AVA_value (char* AVA)
+{
+ return (AVA + strlen (AVA) + 1);
+}
+
+static int
+_value_match (char* value, char* desc)
+{
+ auto const int result =
+ !ldapu_strcasecmp (_value_normalize(value), desc);
+ return result;
+}
+
+int
+ldapu_member_certificate_match (void* cert, const char* desc)
+/*
+ * Return Values: (same as ldapu_find)
+ * LDAPU_SUCCESS cert matches desc
+ * LDAPU_FAILED cert doesn't match desc
+ * <rv> Something went wrong.
+ */
+{
+ auto int err = LDAPU_FAILED;
+ auto char*** descRDNs;
+ if (!cert || !desc || desc[0] != '{') return LDAPU_FAILED;
+ if (desc[1] == '\0') return LDAPU_SUCCESS; /* no AVAs */
+ descRDNs = _explode_dn (desc+1);
+ if (descRDNs) {
+ auto char** descAVAs = (char**)ldapu_malloc(sizeof(char*) * (_rdns_count(descRDNs)+1));
+ if (!descAVAs) {
+ err = LDAPU_ERR_OUT_OF_MEMORY;
+ } else {
+ auto CERTName* subject = &(((CERTCertificate*)cert)->subject);
+ auto char** descAVA;
+
+ err = LDAPU_SUCCESS;
+ { /* extract all the AVAs, but not duplicate types, except OU */
+ auto size_t descAVAsLen = 0;
+ auto char*** descRDN;
+ descAVAs[0] = NULL;
+ for (descRDN = descRDNs; err == LDAPU_SUCCESS && *descRDN; ++descRDN) {
+ for (descAVA = *descRDN; err == LDAPU_SUCCESS && *descAVA; ++descAVA) {
+ err = _explode_AVA (*descAVA);
+ if (err == LDAPU_SUCCESS) {
+ if (_is_OU (*descAVA) ||
+ !_replaceAVA (*descAVA, descAVAs)) {
+ descAVAs[descAVAsLen++] = *descAVA;
+ descAVAs[descAVAsLen] = NULL;
+ }
+ }
+ }
+ }
+ }
+
+ /* match all the attributes except OU */
+ for (descAVA = descAVAs; err == LDAPU_SUCCESS && *descAVA; ++descAVA) {
+ auto struct _attr_getter_pair* descAttr;
+ err = LDAPU_FAILED; /* if no match */
+ for (descAttr = _attr_getter_table; descAttr->name1; ++descAttr) {
+ if (!ldapu_strcasecmp (*descAVA, descAttr->name1) || (descAttr->name2 &&
+ !ldapu_strcasecmp (*descAVA, descAttr->name2))) {
+ if (descAttr->getter == NULL) { /* OU attribute */
+ err = LDAPU_SUCCESS; /* for now */
+ } else {
+ auto char* certVal = (*(descAttr->getter))(subject);
+ if (certVal && _value_match (certVal, _AVA_value (*descAVA))) {
+ err = LDAPU_SUCCESS;
+ }
+ PR_Free (certVal);
+ }
+ break;
+ }
+ }
+ }
+
+ /* match the OU attributes */
+ if (err == LDAPU_SUCCESS && descAVA != descAVAs) {
+ /* Iterate over the OUs in the certificate subject */
+ auto CERTRDN** certRDN = subject->rdns;
+ descAVA = _previous_OU (descAVA, descAVAs);
+ for (; descAVA && *certRDN; ++certRDN) {
+ auto CERTAVA** certAVA = (*certRDN)->avas;
+ for (; descAVA && *certAVA; ++certAVA) {
+ auto const int tag = CERT_GetAVATag (*certAVA);
+ if (tag == SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME) {
+ auto const size_t certValLen =(*certAVA)->value.len;
+ auto const size_t lenLen = (certValLen < 128) ? 2 : 3;
+ auto const size_t buflen = certValLen - lenLen;
+ auto char* buf = (char*)ldapu_malloc(buflen+1);
+ if (!buf) {
+ err = LDAPU_ERR_OUT_OF_MEMORY;
+ descAVA = NULL;
+ } else {
+ memcpy (buf, (*certAVA)->value.data+lenLen, buflen);
+ buf[buflen] = 0;
+ if (_value_match (buf, _AVA_value (*descAVA))) {
+ descAVA = _previous_OU (descAVA, descAVAs);
+ }
+ free (buf);
+ }
+ }
+ }
+ }
+ if (descAVA) {
+ err = LDAPU_FAILED; /* no match for descAVA in subject */
+ }
+ }
+ free (descAVAs);
+ }
+ _rdns_free (descRDNs);
+ }
+ return err;
+}
+