Blame SOURCES/pam_pkcs11-0.6.2-uid-attribute.patch

ac2449
diff -up ./doc/pam_pkcs11.xml.uid-attribute ./doc/pam_pkcs11.xml
ac2449
--- ./doc/pam_pkcs11.xml.uid-attribute	2008-08-28 12:12:46.000000000 -0700
ac2449
+++ ./doc/pam_pkcs11.xml	2015-07-06 19:24:37.485287137 -0700
ac2449
@@ -1679,6 +1679,8 @@ ldap_mapper configuration file shows lik
ac2449
         base = "ou=People,o=example,c=com";
ac2449
         attribute = "userCertificate";
ac2449
         filter = ""
ac2449
+        uid_attribute = "uid";
ac2449
+        attribute_map = "", "";
ac2449
         # SSL/TLS-Settings
ac2449
         ssl = tls
ac2449
         # tls_randfile = ...
ac2449
@@ -1757,6 +1759,43 @@ attribute. All certificates are utilized
ac2449
 </listitem>
ac2449
 </varlistentry>
ac2449
 
ac2449
+<varlistentry>
ac2449
+<term><token>uid_attribute</token></term>
ac2449
+<listitem>The attribute in an LDAP entry which contains the
ac2449
+user's login name.  When the module needs to determine the user's
ac2449
+login name from the information in the certificate, if this
ac2449
+option is set, the module can search for an LDAP entry which
ac2449
+contains the certificate that was read from the card in the
ac2449
+attribute named by the  setting and read the
ac2449
+user's login name from this attribute.  If not, the module must
ac2449
+iterate through all known users, checking for each user if the
ac2449
+certificate on the card matches that user.
ac2449
+</listitem>
ac2449
+</varlistentry>
ac2449
+
ac2449
+<varlistentry>
ac2449
+<term><token>attribute_map</token></term>
ac2449
+<listitem>A list of sets of conditions which can specify how the
ac2449
+module should locate an entry which corresponds to the user.
ac2449
+Each list item consists of a set of one or more conditions
ac2449
+consisting of an LDAP attribute and a certificate attribute, with
ac2449
+conditions in the same set being separated by a 
ac2449
+character.  An LDAP entry is considered to match a user if all of
ac2449
+the conditions are met.  The recognized certificate attributes include
ac2449
+<itemizedlist>
ac2449
+<listitem>"<symbol>cn</symbol>" a commonName attribute value from the certificate's subject</listitem>
ac2449
+<listitem>"<symbol>subject</symbol>" the certificate's entire subject name</listitem>
ac2449
+<listitem>"<symbol>kpn</symbol>" a Kerberos principal name subjectAltName value</listitem>
ac2449
+<listitem>"<symbol>email</symbol>" an rfc822Name subjectAltName value</listitem>
ac2449
+<listitem>"<symbol>upn</symbol>" a Microsoft Universal Principal Name subjectAltName value</listitem>
ac2449
+<listitem>"<symbol>uid</symbol>" a UID attribute value from the certificate's subject</listitem>
ac2449
+<listitem>"<symbol>cert</symbol>" the entire certificate</listitem>
ac2449
+</itemizedlist>
ac2449
+If no  is specified, the default method
ac2449
+is to search for entries which contain the card certificate in
ac2449
+the attribute named by the  setting.
ac2449
+</listitem>
ac2449
+</varlistentry>
ac2449
 
ac2449
 <varlistentry>
ac2449
 <term><token>filter</token></term>
ac2449
diff -up ./doc/README.ldap_mapper.uid-attribute ./doc/README.ldap_mapper
ac2449
--- ./doc/README.ldap_mapper.uid-attribute	2008-08-28 12:12:46.000000000 -0700
ac2449
+++ ./doc/README.ldap_mapper	2015-07-06 19:24:37.485287137 -0700
ac2449
@@ -27,6 +27,10 @@ pam_pkcs11.conf:
ac2449
         attribute = "userCertificate";
ac2449
         # Searchfilter for user entry. Must only let pass user entry for the login user.
ac2449
         filter = "(&(objectClass=posixAccount)(uid=%s))"
ac2449
+        # Attribute of user entry which contains the user's login name (optional)
ac2449
+        uid_attribute = "uid";
ac2449
+        # List of sets of ldap attribute / cert attribute pairs (optional)
ac2449
+        attribute_map = "uid=uid&mail=email", "krbprincipalname=upn", "userCertificate;binary=cert";
ac2449
   }
ac2449
 (...)
ac2449
 
ac2449
diff -up ./src/mappers/ldap_mapper.c.uid-attribute ./src/mappers/ldap_mapper.c
ac2449
--- ./src/mappers/ldap_mapper.c.uid-attribute	2015-07-06 19:24:37.479287249 -0700
ac2449
+++ ./src/mappers/ldap_mapper.c	2015-07-06 20:13:01.578027801 -0700
ac2449
@@ -101,14 +101,12 @@ static const char *binddn="";
ac2449
 static const char *passwd="";
ac2449
 static const char *base="ou=People,o=example,c=com";
ac2449
 static const char *attribute="userCertificate";
ac2449
+static const char *uid_attribute;
ac2449
+static const scconf_list *attribute_map;
ac2449
 static const char *filter="(&(objectClass=posixAccount)(uid=%s)";
ac2449
 static int searchtimeout=20;
ac2449
 static int ignorecase=0;
ac2449
-#ifdef HAVE_NSS
ac2449
-static X509 **ldap_x509;
ac2449
-#else
ac2449
-static const X509 **ldap_x509;
ac2449
-#endif
ac2449
+static char *uid_attribute_value;
ac2449
 static int certcnt=0;
ac2449
 
ac2449
 static ldap_ssl_options_t ssl_on = SSL_OFF;
ac2449
@@ -586,31 +584,335 @@ static int ldap_add_uri (char **uris, co
ac2449
   	return 0;
ac2449
 }
ac2449
 
ac2449
+/* Build a filter suitable for locating the entry for the named user. */
ac2449
+static void
ac2449
+ldap_x509_as_binary(X509 *x509, unsigned char **der, size_t *der_len)
ac2449
+{
ac2449
+#ifdef HAVE_NSS
ac2449
+	*der_len = 0;
ac2449
+	*der = malloc(x509->derCert.len);
ac2449
+	if (*der != NULL) {
ac2449
+		*der_len = x509->derCert.len;
ac2449
+		memcpy(*der, x509->derCert.data, *der_len);
ac2449
+	}
ac2449
+#else
ac2449
+	unsigned char *p = NULL, *q;
ac2449
+	int len;
ac2449
+
ac2449
+	*der_len = 0;
ac2449
+	*der = NULL;
ac2449
+	len = i2d_X509(x509, NULL);
ac2449
+	if (len > 0) {
ac2449
+		p = malloc(len);
ac2449
+		if (p != NULL) {
ac2449
+			q = p;
ac2449
+			if (i2d_X509(x509, &p) == len) {
ac2449
+				*der = q;
ac2449
+				*der_len = p - q;
ac2449
+			}
ac2449
+		}
ac2449
+	}
ac2449
+#endif
ac2449
+}
ac2449
+
ac2449
+/* Encode anything that isn't printable in the binary string as a hex escape,
ac2449
+ * and return the result as a NUL-terminated string. */
ac2449
+static char *
ac2449
+ldap_encode_escapes(const unsigned char *binary, size_t length)
ac2449
+{
ac2449
+	char *ret;
ac2449
+	unsigned int i, j;
ac2449
+
ac2449
+	ret = malloc(length * 3 + 1);
ac2449
+	if (ret == NULL) {
ac2449
+		DBG("ldap_encode_escapes(): out of memory");
ac2449
+		return NULL;
ac2449
+	}
ac2449
+	for (i = 0, j = 0; i < length; i++) {
ac2449
+		if (((binary[i] >= '0') && (binary[i] <= '9')) ||
ac2449
+		    ((binary[i] >= 'a') && (binary[i] <= 'z')) ||
ac2449
+		    ((binary[i] >= 'A') && (binary[i] <= 'Z'))) {
ac2449
+			ret[j++] = binary[i];
ac2449
+		} else {
ac2449
+			ret[j++] = '\\';
ac2449
+			ret[j++] = "0123456789abcdef"[(binary[i] >> 4) & 0x0f];
ac2449
+			ret[j++] = "0123456789abcdef"[binary[i] & 0x0f];
ac2449
+		}
ac2449
+	}
ac2449
+	ret[j] = '\0';
ac2449
+	return ret;
ac2449
+}
ac2449
+
ac2449
+/* Build a subfilter for matching the passed-in certificate against the
ac2449
+ * configured attribute. */
ac2449
+static char *
ac2449
+ldap_build_default_cert_filter(X509 *x509)
ac2449
+{
ac2449
+	char *buf, *cert;
ac2449
+	unsigned char *der;
ac2449
+	size_t buf_len, der_len;
ac2449
+
ac2449
+	ldap_x509_as_binary(x509, &der, &der_len);
ac2449
+	if (der == NULL) {
ac2449
+		DBG("ldap_build_cert_filter(): failed to encode certificate");
ac2449
+		return NULL;
ac2449
+	}
ac2449
+	cert = ldap_encode_escapes(der, der_len);
ac2449
+	free(der);
ac2449
+	if (cert == NULL) {
ac2449
+		DBG("ldap_build_cert_filter(): failed to escape certificate");
ac2449
+		return NULL;
ac2449
+	}
ac2449
+	buf_len = 1 + strlen(attribute) + 1 + strlen(cert) + 2;
ac2449
+	buf = malloc(buf_len);
ac2449
+	if (buf == NULL) {
ac2449
+		DBG("ldap_build_cert_filter(): out of memory");
ac2449
+		free(cert);
ac2449
+		return NULL;
ac2449
+	}
ac2449
+	snprintf(buf, buf_len, "(%s=%s)", attribute, cert);
ac2449
+	free(cert);
ac2449
+	return buf;
ac2449
+}
ac2449
+
ac2449
+/* Build a subfilter for matching the passed-in certificate using the mapping
ac2449
+ * information, or against the configured attribute. */
ac2449
+static char *
ac2449
+ldap_build_partial_cert_filter(const char *map, X509 *x509)
ac2449
+{
ac2449
+	char *buf, *certs[2] = {NULL, NULL}, **values = NULL;
ac2449
+	unsigned char *der;
ac2449
+	const char *p, *q;
ac2449
+	size_t buf_len, der_len, len, n;
ac2449
+	int i;
ac2449
+
ac2449
+	p = strchr(map, '=');
ac2449
+	if (p == NULL) {
ac2449
+		DBG1("ldap_build_cert_filter(): error parsing filter '%s'",
ac2449
+		     map);
ac2449
+		return NULL;
ac2449
+	}
ac2449
+	q = p + strcspn(p, "&";;
ac2449
+	if (strncmp(p + 1, "cn", q - p - 1) == 0) {
ac2449
+		values = cert_info(x509, CERT_CN, ALGORITHM_NULL);
ac2449
+	} else
ac2449
+	if (strncmp(p + 1, "subject", q - p - 1) == 0) {
ac2449
+		values = cert_info(x509, CERT_SUBJECT, ALGORITHM_NULL);
ac2449
+	} else
ac2449
+	if (strncmp(p + 1, "kpn", q - p - 1) == 0) {
ac2449
+		values = cert_info(x509, CERT_KPN, ALGORITHM_NULL);
ac2449
+	} else
ac2449
+	if (strncmp(p + 1, "email", q - p - 1) == 0) {
ac2449
+		values = cert_info(x509, CERT_EMAIL, ALGORITHM_NULL);
ac2449
+	} else
ac2449
+	if (strncmp(p + 1, "upn", q - p - 1) == 0) {
ac2449
+		values = cert_info(x509, CERT_UPN, ALGORITHM_NULL);
ac2449
+	} else
ac2449
+	if (strncmp(p + 1, "uid", q - p - 1) == 0) {
ac2449
+		values = cert_info(x509, CERT_UID, ALGORITHM_NULL);
ac2449
+	} else
ac2449
+	if (strncmp(p + 1, "cert", q - p - 1) == 0) {
ac2449
+		ldap_x509_as_binary(x509, &der, &der_len);
ac2449
+		if (der == NULL) {
ac2449
+			DBG("ldap_build_cert_filter(): error encoding "
ac2449
+			    "certificate");
ac2449
+			return NULL;
ac2449
+		}
ac2449
+		certs[0] = ldap_encode_escapes(der, der_len);
ac2449
+		free(der);
ac2449
+		if (certs[0] == NULL) {
ac2449
+			DBG("ldap_build_cert_filter(): error escaping "
ac2449
+			    "certificate");
ac2449
+			return NULL;
ac2449
+		}
ac2449
+		values = certs;
ac2449
+	} else {
ac2449
+		DBG2("ldap_build_cert_filter(): unrecognized certificate "
ac2449
+		     "attribute '%.*s'", q - p - 1, p + 1);
ac2449
+		return NULL;
ac2449
+	}
ac2449
+	if (values == NULL) {
ac2449
+		DBG2("ldap_build_cert_filter(): no values for certificate "
ac2449
+		     "attribute '%.*s'", q - p - 1, p + 1);
ac2449
+		return NULL;
ac2449
+	}
ac2449
+	DBG4("ldap_build_cert_filter(): building subfilter '%.*s'='%.*s'",
ac2449
+	     p - map, map, q - p - 1, p + 1);
ac2449
+	for (n = 0, buf_len = 0; values[n] != NULL; n++) {
ac2449
+		buf_len++;
ac2449
+		buf_len += (p - map);
ac2449
+		buf_len++;
ac2449
+		buf_len += strlen(values[n]);
ac2449
+		buf_len++;
ac2449
+	}
ac2449
+	buf_len += (n > 1) ? 4 : 1;
ac2449
+	buf = malloc(buf_len);
ac2449
+	if (buf == NULL) {
ac2449
+		DBG("ldap_build_cert_filter(): out of memory");
ac2449
+		free(certs[0]);
ac2449
+		return NULL;
ac2449
+	}
ac2449
+	i = 0;
ac2449
+	if (n > 1) {
ac2449
+		strcpy(buf, "(|");
ac2449
+		i += 2;
ac2449
+	}
ac2449
+	for (n = 0; values[n] != NULL; n++) {
ac2449
+		buf[i++] = '(';
ac2449
+		memcpy(buf + i, map, p - map);
ac2449
+		i += (p - map);
ac2449
+		buf[i++] = '=';
ac2449
+		len = strlen(values[n]);
ac2449
+		memcpy(buf + i, values[n], len);
ac2449
+		i += len;
ac2449
+		buf[i++] = ')';
ac2449
+	}
ac2449
+	if (n > 1) {
ac2449
+		buf[i++] = ')';
ac2449
+	}
ac2449
+	buf[i] = '\0';
ac2449
+	free(certs[0]);
ac2449
+	return buf;
ac2449
+}
ac2449
+
ac2449
+/* Build a filter for matching the passed-in certificate using the mapping
ac2449
+ * information, or against the configured attribute. */
ac2449
+static char *
ac2449
+ldap_build_cert_filter(const char *map, X509 *x509)
ac2449
+{
ac2449
+	char *buf = NULL, *tmp, *sub;
ac2449
+	const char *p;
ac2449
+	size_t length, n;
ac2449
+
ac2449
+	if (map == NULL) {
ac2449
+		DBG("ldap_build_cert_filter(): building default filter");
ac2449
+		return ldap_build_default_cert_filter(x509);
ac2449
+	}
ac2449
+	DBG1("ldap_build_cert_filter(): building filter '%s'", map);
ac2449
+	p = map;
ac2449
+	n = 0;
ac2449
+	while (*p != '\0') {
ac2449
+		sub = ldap_build_partial_cert_filter(p, x509);
ac2449
+		if (sub == NULL) {
ac2449
+			free(buf);
ac2449
+			return NULL;
ac2449
+		}
ac2449
+		if (buf != NULL) {
ac2449
+			length = strlen(buf) + strlen(sub) + 1;
ac2449
+			tmp = malloc(length);
ac2449
+			if (tmp == NULL) {
ac2449
+				free(buf);
ac2449
+				free(sub);
ac2449
+				return NULL;
ac2449
+			}
ac2449
+			snprintf(tmp, length, "%s%s", buf, sub);
ac2449
+			free(buf);
ac2449
+			free(sub);
ac2449
+			buf = tmp;
ac2449
+		} else {
ac2449
+			buf = sub;
ac2449
+		}
ac2449
+		n++;
ac2449
+		p += strcspn(p, "&";;
ac2449
+		p += strspn(p, "&";;
ac2449
+	}
ac2449
+	if (n > 1) {
ac2449
+		length = strlen(buf) + 4;
ac2449
+		tmp = malloc(length);
ac2449
+		if (tmp == NULL) {
ac2449
+			free(buf);
ac2449
+			return NULL;
ac2449
+		}
ac2449
+		snprintf(tmp, length, "(&%s)", buf);
ac2449
+		free(buf);
ac2449
+		buf = tmp;
ac2449
+	}
ac2449
+	return buf;
ac2449
+}
ac2449
+
ac2449
+/* Build a filter suitable for locating the entry for the named user. */
ac2449
+static char *
ac2449
+ldap_build_filter(const char *filter, const char *login, const char *map,
ac2449
+		  X509 *x509)
ac2449
+{
ac2449
+	char *buf, *user_filter, *escaped, *cert_filter;
ac2449
+	unsigned char *der;
ac2449
+	size_t buf_len, user_filter_len, der_len;
ac2449
+	unsigned int i;
ac2449
+
ac2449
+	/* If no user name is specified, this is a search across all users. */
ac2449
+	if (login != NULL) {
ac2449
+		escaped = ldap_encode_escapes(login, strlen(login));
ac2449
+	} else {
ac2449
+		escaped = strdup("*");
ac2449
+	}
ac2449
+	if (escaped == NULL) {
ac2449
+		DBG1("ldap_build_filter(): error escaping user name '%s'",
ac2449
+		     login);
ac2449
+		return NULL;
ac2449
+	}
ac2449
+ 
ac2449
+	/* Build a user filter using the supplied filter and user name. */
ac2449
+	user_filter_len = strlen(filter) + strlen(escaped) + 1;
ac2449
+	user_filter = malloc(user_filter_len);
ac2449
+	if (user_filter == NULL) {
ac2449
+		DBG("ldap_build_filter(): out of memory for user filter");
ac2449
+		free(escaped);
ac2449
+		return NULL;
ac2449
+	}
ac2449
+	snprintf(user_filter, user_filter_len, filter, escaped);
ac2449
+	free(escaped);
ac2449
+
ac2449
+	/* Build the part of the filter that's specific to the certificate. */
ac2449
+	cert_filter = ldap_build_cert_filter(map, x509);
ac2449
+	if (cert_filter == NULL) {
ac2449
+		DBG("ldap_build_filter(): error building certificate filter");
ac2449
+		free(user_filter);
ac2449
+		return NULL;
ac2449
+	}
ac2449
+
ac2449
+	/* Build a filter combining the user filter and the certificate. */
ac2449
+	buf_len = 3 + strlen(user_filter) + 2 + 2 + strlen(cert_filter) + 2;
ac2449
+	buf = malloc(buf_len);
ac2449
+	if (buf != NULL) {
ac2449
+		if (filter[0] == '(') {
ac2449
+			snprintf(buf, buf_len, "(&%s%s)", user_filter,
ac2449
+				 cert_filter);
ac2449
+		} else {
ac2449
+			snprintf(buf, buf_len, "(&(%s)%s)", user_filter,
ac2449
+				 cert_filter);
ac2449
+		}
ac2449
+	} else {
ac2449
+		DBG("ldap_build_filter(): out of memory");
ac2449
+	}
ac2449
+
ac2449
+	free(user_filter);
ac2449
+	free(cert_filter);
ac2449
+	return buf;
ac2449
+}
ac2449
+
ac2449
 
ac2449
 
ac2449
 /**
ac2449
 * Get certificate from LDAP-Server.
ac2449
 */
ac2449
-static int ldap_get_certificate(const char *login) {
ac2449
+static int ldap_get_certificate(const char *login, X509 *x509) {
ac2449
 	LDAP *ldap_connection;
ac2449
 	int ret, entries;
ac2449
 	LDAPMessage *res;
ac2449
 	LDAPMessage *entry;
ac2449
-	struct berval **bvals = NULL;
ac2449
+	struct berval **bvals = NULL, *bv;
ac2449
 	BerElement *ber = NULL;
ac2449
-	char *name = NULL;
ac2449
-	char filter_str[100];
ac2449
-	char *attrs[2];
ac2449
+	char *filter_str;
ac2449
+	char *attrs[3];
ac2449
 	int rv;
ac2449
-#ifndef HAVE_NSS
ac2449
-	void *bv_val;
ac2449
-#endif
ac2449
 
ac2449
 	char uri[4096];
ac2449
 	char uribuf[4096];
ac2449
 	char *uris[LDAP_CONFIG_URI_MAX + 1];
ac2449
 	const char *p;
ac2449
 	int current_uri = 0, start_uri = 0;
ac2449
+	const scconf_list *mapping;
ac2449
 
ac2449
 	char *buffer;
ac2449
 	size_t buflen;
ac2449
@@ -618,14 +920,16 @@ static int ldap_get_certificate(const ch
ac2449
 	uris[0] = NULL;
ac2449
 
ac2449
 	attrs[0] = (char *)attribute;
ac2449
-	attrs[1] = NULL;
ac2449
-
ac2449
-	DBG1("ldap_get_certificate(): begin login = %s", login);
ac2449
-
ac2449
-	/* Put the login to the %s in Filterstring */
ac2449
-	snprintf(filter_str, sizeof(filter_str), filter, login);
ac2449
+	attrs[1] = (char *)uid_attribute;
ac2449
+	attrs[2] = NULL;
ac2449
+	free((char *)uid_attribute_value);
ac2449
+	uid_attribute_value = NULL;
ac2449
 
ac2449
-	DBG1("ldap_get_certificate(): filter_str = %s", filter_str);
ac2449
+	if (login != NULL) {
ac2449
+		DBG1("ldap_get_certificate(): begin login = %s", login);
ac2449
+	} else {
ac2449
+		DBG("ldap_get_certificate(): begin login unknown");
ac2449
+	}
ac2449
 
ac2449
 	/* parse and split URI config entry */
ac2449
 	buffer = uribuf;
ac2449
@@ -709,14 +1013,55 @@ static int ldap_get_certificate(const ch
ac2449
     	     server again. Perhaps create a state file/smem/etc. ?
ac2449
     */
ac2449
 
ac2449
-	rv = ldap_search_s(
ac2449
-				ldap_connection,
ac2449
-				base,
ac2449
-				sscope[scope],
ac2449
-				filter_str,
ac2449
-				attrs,
ac2449
-				0,
ac2449
-				&res;;
ac2449
+	/* Search for matching entries. */
ac2449
+	for (mapping = attribute_map;; mapping = mapping->next) {
ac2449
+		/* Walk the list of mappings, and if we're out of those, let
ac2449
+		 * the ldap_build_filter() function just build one that uses
ac2449
+		 * the certificate. */
ac2449
+		if (mapping != NULL) {
ac2449
+			DBG1("ldap_get_certificate(): building filter_str "
ac2449
+			     "from template '%s'", mapping->data);
ac2449
+		} else {
ac2449
+			DBG("ldap_get_certificate(): building default "
ac2449
+			    "filter_str");
ac2449
+		}
ac2449
+		filter_str = ldap_build_filter(filter, login,
ac2449
+					       mapping ? mapping->data : NULL,
ac2449
+					       x509);
ac2449
+		if (filter_str == NULL) {
ac2449
+			DBG("ldap_get_certificate(): error building filter_str");
ac2449
+			if (mapping) {
ac2449
+				continue;
ac2449
+			}
ac2449
+			break;
ac2449
+		}
ac2449
+		DBG1("ldap_get_certificate(): searching with filter_str = %s",
ac2449
+		     filter_str);
ac2449
+		rv = ldap_search_s(ldap_connection,
ac2449
+				   base,
ac2449
+				   sscope[scope],
ac2449
+				   filter_str,
ac2449
+				   attrs,
ac2449
+				   0,
ac2449
+				   &res;;
ac2449
+		free(filter_str);
ac2449
+		/* The first successful search means we're done. */
ac2449
+		if ((rv == LDAP_SUCCESS) &&
ac2449
+		    (ldap_count_entries(ldap_connection, res) > 0)) {
ac2449
+			DBG("ldap_get_certificate(): found an entry");
ac2449
+			break;
ac2449
+		}
ac2449
+		DBG("ldap_get_certificate(): no matching entries");
ac2449
+		/* If this was the fallback (cert-only) search, we're done. */
ac2449
+		if (mapping == NULL) {
ac2449
+			break;
ac2449
+		}
ac2449
+	}
ac2449
+	if (filter_str == NULL) {
ac2449
+		DBG("ldap_get_certificate(): unable to build any filter_str");
ac2449
+		return(-8);
ac2449
+	}
ac2449
+
ac2449
 	if ( rv != LDAP_SUCCESS ) {
ac2449
 		DBG1("ldap_search_s() failed: %s", ldap_err2string(rv));
ac2449
 		ldap_unbind_s(ldap_connection);
ac2449
@@ -742,65 +1087,35 @@ static int ldap_get_certificate(const ch
ac2449
 			return(-4);
ac2449
 		}
ac2449
 
ac2449
-		/* Only first attribute is used. See comment above... */
ac2449
-		if ( NULL == (name = ldap_first_attribute(ldap_connection, res, &ber))){
ac2449
-			DBG("ldap_first_attribute() failed (rc=%d)");
ac2449
-			ldap_unbind_s(ldap_connection);
ac2449
-			return(-5);
ac2449
-		}
ac2449
-		DBG1("attribute name = %s", name);
ac2449
-
ac2449
-		bvals = ldap_get_values_len(ldap_connection, entry, name);
ac2449
+		/* Count the number of certificates in the entry. */
ac2449
+		DBG1("attribute name = %s", attribute);
ac2449
+		bvals = ldap_get_values_len(ldap_connection, entry, attribute);
ac2449
 		certcnt = ldap_count_values_len(bvals);
ac2449
-
ac2449
 		DBG1("number of user certificates = %d", certcnt);
ac2449
+		ldap_value_free_len(bvals);
ac2449
 
ac2449
-		ldap_x509 = malloc(sizeof(X509*) * certcnt );
ac2449
-		if (NULL == ldap_x509)
ac2449
-		{
ac2449
-			DBG("not enough memory");
ac2449
-			return(-7);
ac2449
-		}
ac2449
+		if (uid_attribute != NULL) {
ac2449
+			/* Try to retrieve the user's login name from the
ac2449
+			 * specified attribute in the entry. */
ac2449
+			bvals = ldap_get_values_len(ldap_connection, entry,
ac2449
+						    uid_attribute);
ac2449
+			DBG2("number of user names ('%s' values) = %d",
ac2449
+			     uid_attribute, ldap_count_values_len(bvals));
ac2449
+			if (ldap_count_values_len(bvals) == 1) {
ac2449
+				bv = bvals[0];
ac2449
+				uid_attribute_value = malloc(bv->bv_len + 1);
ac2449
+				if (uid_attribute_value != NULL) {
ac2449
+					memcpy(uid_attribute_value, bv->bv_val,
ac2449
+					       bv->bv_len);
ac2449
+					uid_attribute_value[bv->bv_len] = '\0';
ac2449
+				}
ac2449
+			}
ac2449
+			ldap_value_free_len(bvals);
ac2449
+ 		}
ac2449
 
ac2449
 		rv = 0;
ac2449
-		while(rv < certcnt )
ac2449
-		{
ac2449
-			/* SaW: not nifty, but otherwise gcc doesn't optimize */
ac2449
-#ifdef HAVE_NSS
ac2449
-			{
ac2449
-				SECItem derdata;
ac2449
-				derdata.data = bvals[rv]->bv_val;
ac2449
-				derdata.len = bvals[rv]->bv_len;
ac2449
 
ac2449
-				ldap_x509[rv] = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
ac2449
-					&derdata, NULL, 0, 1);
ac2449
-			}
ac2449
-#else
ac2449
-			bv_val = &bvals[rv]->bv_val;
ac2449
-			ldap_x509[rv] = d2i_X509(NULL, ((const unsigned char **) bv_val), bvals[rv]->bv_len);
ac2449
-#endif
ac2449
-			if (NULL == ldap_x509[rv]) {
ac2449
-				DBG1("d2i_X509() failed for certificate %d", rv);
ac2449
-				free(ldap_x509);
ac2449
-#ifdef HAVE_NSS
ac2449
-				{
ac2449
-					for (rv=0; rv
ac2449
-						if (ldap_x509[rv])
ac2449
-							CERT_DestroyCertificate(ldap_x509[rv]);
ac2449
-				}
ac2449
-#endif
ac2449
-				certcnt=0;
ac2449
-				ldap_msgfree(res);
ac2449
-				ldap_unbind_s(ldap_connection);
ac2449
-				return(-6);
ac2449
-			}else {
ac2449
-				DBG1("d2i_X509(): success for certificate %d", rv);
ac2449
-			}
ac2449
-			rv++;
ac2449
-		}
ac2449
 		ldap_msgfree(res);
ac2449
-		/* TODO: this leads to a segfault, but the doc said ... */
ac2449
-		/* ldap_value_free_len(bvals); */
ac2449
 	}
ac2449
 	if ( 0 != (ret = ldap_unbind_s(ldap_connection))) {
ac2449
 		DBG("ldap_unbind_s() failed.");
ac2449
@@ -815,6 +1130,7 @@ static int ldap_get_certificate(const ch
ac2449
 static int read_config(scconf_block *blk) {
ac2449
 	int debug = scconf_get_bool(blk,"debug",0);
ac2449
 	const char *ssltls;
ac2449
+	const scconf_list *map;
ac2449
 
ac2449
 	ldaphost = scconf_get_str(blk,"ldaphost",ldaphost);
ac2449
 	ldapport = scconf_get_int(blk,"ldapport",ldapport);
ac2449
@@ -824,6 +1140,8 @@ static int read_config(scconf_block *blk
ac2449
 	passwd = scconf_get_str(blk,"passwd",passwd);
ac2449
 	base = scconf_get_str(blk,"base",base);
ac2449
 	attribute = scconf_get_str(blk,"attribute",attribute);
ac2449
+	uid_attribute = scconf_get_str(blk,"uid_attribute",uid_attribute);
ac2449
+	attribute_map = scconf_find_list(blk,"attribute_map");
ac2449
 	filter = scconf_get_str(blk,"filter",filter);
ac2449
 	ignorecase = scconf_get_bool(blk,"ignorecase",ignorecase);
ac2449
 	searchtimeout = scconf_get_int(blk,"searchtimeout",searchtimeout);
ac2449
@@ -864,6 +1182,10 @@ DBG1("test ssltls = %s", ssltls);
ac2449
 	DBG1("passwd        = %s", passwd);
ac2449
 	DBG1("base          = %s", base);
ac2449
 	DBG1("attribute     = %s", attribute);
ac2449
+	DBG1("uid_attribute = %s", uid_attribute);
ac2449
+	for (map = attribute_map; map != NULL; map = map->next) {
ac2449
+		DBG1("attribute_map = %s", attribute_map->data);
ac2449
+	}
ac2449
 	DBG1("filter        = %s", filter);
ac2449
 	DBG1("searchtimeout = %d", searchtimeout);
ac2449
 	DBG1("ssl_on        = %d", ssl_on);
ac2449
@@ -895,35 +1217,16 @@ static int ldap_mapper_match_user(X509 *
ac2449
 	int match_found = 0;
ac2449
 	int i=0;
ac2449
 
ac2449
-	if ( 1 != ldap_get_certificate(login)){
ac2449
+	if ( 1 != ldap_get_certificate(login, x509)){
ac2449
 		DBG("ldap_get_certificate() failed");
ac2449
 		match_found = 0;
ac2449
 	} else {
ac2449
-		/* TODO: maybe compare public keys instead of hashes */
ac2449
-		while( i
ac2449
-#ifdef HAVE_NSS
ac2449
-			if ( x509 == ldap_x509[i]) {
ac2449
-#else
ac2449
-			if ( 0 == X509_cmp(x509, ldap_x509[i])) {
ac2449
-#endif
ac2449
-				DBG1("Certificate %d is matching", i);
ac2449
-				match_found = 1;
ac2449
-			} else {
ac2449
-				DBG1("Certificate %d is NOT matching", i);
ac2449
-			}
ac2449
-			i++;
ac2449
-		}
ac2449
-		if (certcnt)
ac2449
-		{
ac2449
-#ifdef HAVE_NSS
ac2449
-			int rv;
ac2449
-
ac2449
-			for (rv=0; rv
ac2449
-				if (ldap_x509[rv])
ac2449
-					CERT_DestroyCertificate(ldap_x509[rv]);
ac2449
-#endif
ac2449
-			free(ldap_x509);
ac2449
-		}
ac2449
+		if (login != NULL) {
ac2449
+			DBG1("Found matching entry for user: '%s'", login);
ac2449
+		} else {
ac2449
+			DBG("Found matching entry for user");
ac2449
+ 		}
ac2449
+		match_found = 1;
ac2449
 		certcnt=0;
ac2449
 	}
ac2449
 	return match_found;
ac2449
@@ -932,6 +1235,15 @@ static int ldap_mapper_match_user(X509 *
ac2449
 static char * ldap_mapper_find_user(X509 *x509, void *context) {
ac2449
 	struct passwd *pw = NULL;
ac2449
 	char *found=NULL;
ac2449
+
ac2449
+	if (uid_attribute != NULL) {
ac2449
+		if ((1 == ldap_mapper_match_user(x509, NULL, context)) &&
ac2449
+		    (uid_attribute_value != NULL)) {
ac2449
+			found = clone_str(uid_attribute_value);
ac2449
+		}
ac2449
+		return found;
ac2449
+	}
ac2449
+
ac2449
 	setpwent();
ac2449
 	while( (pw=getpwent()) !=NULL) {
ac2449
 	    int res;