|
|
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;
|