refactor ldap user authentication, use new filter config

Mon, 13 Mar 2023 22:39:51 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 13 Mar 2023 22:39:51 +0100
changeset 470
467ed0f559af
parent 469
9a36a6b52e4c
child 471
9aa5ae3258f5

refactor ldap user authentication, use new filter config

src/server/daemon/acl.c file | annotate | diff | comparison | revisions
src/server/daemon/ldap_auth.c file | annotate | diff | comparison | revisions
src/server/daemon/ldap_auth.h file | annotate | diff | comparison | revisions
--- a/src/server/daemon/acl.c	Mon Mar 13 20:53:46 2023 +0100
+++ b/src/server/daemon/acl.c	Mon Mar 13 22:39:51 2023 +0100
@@ -179,18 +179,9 @@
     ACLList *acl = acl_evallist(list, user, access_mask, NULL);
     if(acl) {
         acl_set_error_status(sn, rq, acl, user);
-        // TODO: don't free the user here #51
-        if(user) {
-            user->free(user);
-        }
         return REQ_ABORTED;
     }
 
-    // access allowed, we can free the user
-    if(user) {
-        user->free(user);
-    }
-
     return REQ_PROCEED;
 }
 
--- a/src/server/daemon/ldap_auth.c	Mon Mar 13 20:53:46 2023 +0100
+++ b/src/server/daemon/ldap_auth.c	Mon Mar 13 22:39:51 2023 +0100
@@ -57,7 +57,7 @@
     NULL, // basedn
     NULL, // binddn
     NULL, // bindpw
-    "(&(objectclass=inetorgperson)(!(cn=%s)(uid=%s)))", // userSearchFilter
+    "(&(objectclass=inetorgperson)(|(cn=%s)(uid=%s)))", // userSearchFilter
     ws_ldap_default_uid_attr, // uidAttributes
     1, // numUidAttributes
     "(&(|(objectclass=groupOfNames)(objectclass=groupOfUniqueNames))(cn=%s))", // groupSearchFilter
@@ -83,7 +83,7 @@
     NULL, // basedn
     NULL, // binddn
     NULL, // bindpw
-    "(&(objectclass=inetorgperson)(!(cn=%s)(uid=%s)))", // userSearchFilter
+    "(&(objectclass=inetorgperson)(|(cn=%s)(uid=%s)))", // userSearchFilter
     ws_ad_default_uid_attr, // uidAttributes
     1, // numUidAttributes
     "", // groupSearchFilter
@@ -157,19 +157,21 @@
     cxstring memberAttributes = serverconfig_object_directive_value(node, cx_str("MemberAttributes"));
     cxstring memberType = serverconfig_object_directive_value(node, cx_str("MemberType")); 
     cxstring enableGroups = serverconfig_object_directive_value(node, cx_str("EnableGroups"));	 
-    cxstring userNameIsDn = serverconfig_object_directive_value(node, cx_str("UserNameIsDn"));	 
+    cxstring userNameIsDn = serverconfig_object_directive_value(node, cx_str("UserNameIsDn"));
     
     if(!resource.ptr) {
         // TODO: create resource pool
     } else {
-        authdb->config.resource = resource.ptr;
+        authdb->config.resource = cx_strdup_a(cfg->a, resource).ptr;
+        if(!authdb->config.resource) return NULL;
     }
     
     if(!basedn.ptr) {
         log_ereport(LOG_FAILURE, "ldap authdb %s: basedn is required", name);
         return NULL;
     }
-    authdb->config.basedn = basedn.ptr;
+    authdb->config.basedn = cx_strdup_a(cfg->a, basedn).ptr;
+    if(!authdb->config.basedn) return NULL;
     
     // optional config
     if(binddn.ptr) {
@@ -178,36 +180,47 @@
             return NULL;
         }
         
-        authdb->config.binddn = binddn.ptr;
-        authdb->config.bindpw = bindpw.ptr;
+        authdb->config.binddn = cx_strdup_a(cfg->a, binddn).ptr;
+        authdb->config.bindpw = cx_strdup_a(cfg->a, bindpw).ptr;
+        
+        if(!authdb->config.binddn || !authdb->config.bindpw) {
+            return NULL;
+        }
     }
+
     
     if(userSearchFilter.ptr) {
-        authdb->config.userSearchFilter = userSearchFilter.ptr;
+        authdb->config.userSearchFilter = cx_strdup_a(cfg->a, userSearchFilter).ptr;
     }
     if(uidAttributes.ptr) {
-        authdb->config.numUidAttributes = cx_strsplit_a(
-                cfg->a,
-                uidAttributes,
-                cx_str(","),
-                1024,
-                &authdb->config.uidAttributes);
+        cxmutstr uidAttributesCopy = cx_strdup_a(cfg->a, uidAttributes);
+        if(uidAttributesCopy.ptr) {
+            authdb->config.numUidAttributes = cx_strsplit_a(
+                    cfg->a,
+                    cx_strcast(uidAttributesCopy),
+                    cx_str(","),
+                    1024,
+                    &authdb->config.uidAttributes);
+        }
     }
     if(groupSearchFilter.ptr) {
         authdb->config.groupSearchFilter = groupSearchFilter.ptr;
     }
     if(memberAttributes.ptr) {
-        authdb->config.numMemberAttributes = cx_strsplit_a(
-                cfg->a,
-                memberAttributes,
-                cx_str(","),
-                1024,
-                &authdb->config.memberAttributes);
+        cxmutstr memberAttributesCopy = cx_strdup_a(cfg->a, memberAttributes);
+        if(memberAttributesCopy.ptr) {
+            authdb->config.numMemberAttributes = cx_strsplit_a(
+                    cfg->a,
+                    cx_strcast(memberAttributesCopy),
+                    cx_str(","),
+                    1024,
+                    &authdb->config.memberAttributes);
+        }
     }
     if(memberType.ptr) {
         if(!cx_strcmp(memberType, cx_str("dn"))) {
             authdb->config.groupMemberType = WS_LDAP_GROUP_MEMBER_DN;
-        } else if(cx_strcmp(memberType, cx_str("uid"))) {
+        } else if(!cx_strcmp(memberType, cx_str("uid"))) {
             authdb->config.groupMemberType = WS_LDAP_GROUP_MEMBER_UID;
         } else {
             log_ereport(LOG_FAILURE, "ldap authdb %s: unknown MemberType %s", name, memberType.ptr);
@@ -257,72 +270,177 @@
     return ldap;
 }
 
+static LDAPUser* ldap_msg_to_user(
+        Session *sn,
+        Request *rq,
+        LDAPAuthDB *authdb,
+        LDAP *ldap,
+        LDAPMessage *msg)
+{
+    CxAllocator *a = pool_allocator(sn->pool);
+    
+    LDAPUser *user = pool_malloc(sn->pool, sizeof(LDAPUser));
+    if(!user) {
+        return NULL;
+    }
+    
+    // get dn
+    char *ldap_dn = ldap_get_dn(ldap, msg);
+    if(!ldap_dn) {
+        return NULL;
+    }
+    char *dn = pool_strdup(sn->pool, ldap_dn);
+    ldap_memfree(ldap_dn);
+    if(!dn) {
+        return NULL;
+    }
+    
+    // get uid
+    char *uid = NULL;
+
+    // values of configured UidAttributes
+    size_t numUidAttributes = authdb->config.numUidAttributes;
+    cxmutstr *uid_values = pool_calloc(sn->pool, authdb->config.numUidAttributes, sizeof(cxmutstr));
+    if(!uid_values) {
+        return NULL;
+    }
+
+
+    BerElement *ber = NULL;
+    char *attribute = ldap_first_attribute(ldap, msg, &ber);
+    while(attribute) {
+        cxstring attr = cx_str(attribute);
+        for(int i=0;i<numUidAttributes;i++) {
+            // check if the attribute is one of the uid attributes
+            if(!uid_values[i].ptr && !cx_strcmp(attr, authdb->config.uidAttributes[i])) {
+                // copy value to uid_values
+                struct berval **values = ldap_get_values_len(ldap, msg, attribute);
+                if(values) {
+                    int count = ldap_count_values_len(values);
+                    if(count > 0) {
+                        cxstring attr_val = cx_strn(values[0]->bv_val, values[0]->bv_len);
+                        uid_values[i] = cx_strdup_a(a, attr_val);
+                    } else {
+                        log_ereport(LOG_FAILURE, "ldap user: dn: %s   attribute %s: no values", dn, attribute);
+                    }
+                    ldap_value_free_len(values);
+                }
+            }
+        }
+        if(uid_values[0].ptr) {
+            // if we found a value for the first attribute, we can use that
+            break;
+        }
+
+        ldap_memfree(attribute);
+        attribute = ldap_next_attribute(ldap, msg, ber); 
+    }
+
+    
+    // use first value as uid
+    for(int i=0;i<numUidAttributes;i++) {
+        if(uid_values[i].ptr) {
+            if(!uid) {
+                uid = uid_values[i].ptr;
+            } else {
+                cxFree(a, uid_values[i].ptr);
+            }
+        }
+    }
+    pool_free(sn->pool, uid_values);
+    
+   // get user name
+    char *username;
+    if(authdb->config.userNameIsDN) {
+        username = dn;
+    }  else {
+        username = uid;
+    }
+    
+    if(!username) {
+        return NULL;
+    }
+    
+    user->authdb = authdb;
+    user->user.verify_password = ldap_user_verify_password;
+    user->user.check_group = ldap_user_check_group;
+    user->user.free = ldap_user_free;
+    user->user.name = username;
+    user->sn = sn;
+    user->rq = rq;
+
+    // TODO: get uid/gid from ldap
+    user->user.uid = -1;
+    user->user.gid = -1;
+
+    user->ldap = ldap;
+    user->userdn = dn;
+    user->uid_attr = uid;
+    
+    return user;
+}
+
 User* ldap_get_user(AuthDB *db, Session *sn, Request *rq, const char *username) {
     LDAPAuthDB *authdb = (LDAPAuthDB*) db;
     LDAPConfig *config = &authdb->config;
+    CxAllocator *a = pool_allocator(sn->pool);
 
     LDAP *ld = get_ldap_session(sn, rq, authdb);
     if (ld == NULL) {
-        fprintf(stderr, "ldap_init failed\n");
         return NULL;
     }
 
     // get the user dn
-    // TODO: use config for filter
-    // TODO: use asprintf
-    char filter[128];
-    snprintf(filter, 128, "(uid=%s)", username);
+    cxstring userSearch = cx_str(config->userSearchFilter);
+    cxmutstr filter = cx_strreplace_a(a, userSearch, cx_str("%s"), cx_str(username));
+    if(!filter.ptr) {
+        return NULL;
+    }
 
+    log_ereport(LOG_DEBUG, "ldap_get_user: filter: %s", filter.ptr);
+    
     LDAPMessage *result;
     struct timeval timeout;
-    timeout.tv_sec = 8;
+    timeout.tv_sec = 8; // TODO: add config parameter for timeout
     timeout.tv_usec = 0;
     int r = ldap_search_ext_s(
             ld,
             config->basedn,
             LDAP_SCOPE_SUBTREE,
-            filter,
+            filter.ptr,
             NULL,
             0,
             NULL,        // server controls
             NULL,        // client controls
             &timeout,
-            1,           // size limit
+            2,           // size limit
             &result);
-    if (r != LDAP_SUCCESS) {
-        //ws_ldap_close(ld);
-        
+    cxFree(a, filter.ptr);
+    if(r != LDAP_SUCCESS) {
+        if(result) {
+            ldap_msgfree(result);
+        }
         log_ereport(LOG_FAILURE, "ldap_get_user: search failed: %s", ldap_err2string(r));
         return NULL;
     }
+    if(!result) {
+        // not sure if this can happen
+        log_ereport(LOG_FAILURE, "ldap_get_user: search failed: no result");
+        return NULL;
+    }
 
     LDAPMessage *msg = ldap_first_entry(ld, result);
-    if (msg) {
-        LDAPUser *user = pool_malloc(sn->pool, sizeof(LDAPUser));
-        if (user != NULL) {
-            user->authdb = authdb;
-            user->user.verify_password = ldap_user_verify_password;
-            user->user.check_group = ldap_user_check_group;
-            user->user.free = ldap_user_free;
-            user->user.name = pool_strdup(sn->pool, username);
-            user->sn = sn;
-            user->rq = rq;
-            
-            // TODO: get uid/gid from ldap
-            user->user.uid = -1;
-            user->user.gid = -1;
-
-            user->ldap = ld;
-            user->userdn = ldap_get_dn(ld, msg);
-
-            ldap_msgfree(result);
-
-            return (User*)user;
+    LDAPUser *user = NULL;
+    if(msg) {
+        if(ldap_count_entries(ld, msg) > 1) {
+            log_ereport(LOG_FAILURE, "ldap_get_user: more than one search result");
+        } else {
+            user = ldap_msg_to_user(sn, rq, authdb, ld, msg);
         }
     }
+    ldap_msgfree(result);
 
-    //ws_ldap_close(ld);
-    return NULL;
+    return (User*)user;
 }
 
 LDAPGroup* ldap_get_group(Session *sn, Request *rq, LDAPAuthDB *authdb, const char *group) {
@@ -433,10 +551,10 @@
             NULL,
             &server_cred);
     if(r == LDAP_SUCCESS) {
-        printf("ldap password ok\n");
+        log_ereport(LOG_VERBOSE, "ldap user %s password ok", user->userdn);
         return 1;
     } else {
-        printf("ldap password not ok\n");
+        log_ereport(LOG_VERBOSE, "ldap user %s password not ok", user->userdn);
         return 0;
     }
 }
--- a/src/server/daemon/ldap_auth.h	Mon Mar 13 20:53:46 2023 +0100
+++ b/src/server/daemon/ldap_auth.h	Mon Mar 13 22:39:51 2023 +0100
@@ -158,6 +158,7 @@
     Session      *sn;
     Request      *rq;
     char         *userdn;
+    char         *uid_attr;
     int          uid;
     int          gid;
 };

mercurial