refactore ldap_auth to use resource pools

Sun, 12 Mar 2023 20:02:04 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 12 Mar 2023 20:02:04 +0100
changeset 467
4d038bc6f86e
parent 466
019c22775f7c
child 468
73e80eb953f5

refactore ldap_auth to use resource pools

src/server/daemon/acl.c file | annotate | diff | comparison | revisions
src/server/daemon/auth.c file | annotate | diff | comparison | revisions
src/server/daemon/config.c file | annotate | diff | comparison | revisions
src/server/daemon/keyfile_auth.c file | annotate | diff | comparison | revisions
src/server/daemon/keyfile_auth.h 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
src/server/public/auth.h file | annotate | diff | comparison | revisions
src/server/safs/auth.c file | annotate | diff | comparison | revisions
--- a/src/server/daemon/acl.c	Sun Mar 12 11:42:17 2023 +0100
+++ b/src/server/daemon/acl.c	Sun Mar 12 20:02:04 2023 +0100
@@ -112,7 +112,7 @@
         char *pw;
         if(!basicauth_getuser(sn, rq, &usr, &pw)) {
             int pwok;
-            user = authdb_get_and_verify(list->defaultauthdb, usr, pw, &pwok);
+            user = authdb_get_and_verify(list->defaultauthdb, sn, rq, usr, pw, &pwok);
             if(!user) {
                 // wrong user or wrong password
                 return NULL;
--- a/src/server/daemon/auth.c	Sun Mar 12 11:42:17 2023 +0100
+++ b/src/server/daemon/auth.c	Sun Mar 12 20:02:04 2023 +0100
@@ -283,34 +283,18 @@
  * from public/auth.h
  */
 
-User* authdb_get_user(AuthDB *db, const char *user) {
+User* authdb_get_user(AuthDB *db, Session *sn, Request *rq, const char *user) {
     if(db->use_cache) {
         User *u = auth_cache_get(db->name, user);
         if(u) {
             return u;
         }
     }
-    return db->get_user(db, user);
+    return db->get_user(db, sn, rq, user);
 }
 
-User* authdb_get_and_verify(AuthDB *db, const char *user, const char *password, int *pw) {
-    User *u = NULL;
-    // try getting the user from the cache
-    if(db->use_cache) {
-        u = auth_cache_get(db->name, user);
-        if(u) {
-            if(u->verify_password(u, password)) {
-                *pw = 1;
-            } else {
-                *pw = 0;
-                u->free(u);
-                u = NULL;
-            }
-            return u;
-        }
-    }
-    // user not cached
-    u = db->get_user(db, user);
+User* authdb_get_and_verify(AuthDB *db, Session *sn, Request *rq, const char *user, const char *password, int *pw) {
+    User *u = authdb_get_user(db, sn, rq, user);
     if(u) {
         if(u->verify_password(u, password)) {
             if(db->use_cache) {
--- a/src/server/daemon/config.c	Sun Mar 12 11:42:17 2023 +0100
+++ b/src/server/daemon/config.c	Sun Mar 12 20:02:04 2023 +0100
@@ -602,21 +602,7 @@
     AuthDB *authdb = NULL;
     
     if(!cx_strcmp(type, cx_str("ldap"))) {
-        LDAPConfig conf;
-        
-        cxstring host = serverconfig_object_directive_value(obj, cx_str("Host"));
-        cxstring port = serverconfig_object_directive_value( obj, cx_str("Port"));
-        cxstring basedn = serverconfig_object_directive_value(obj, cx_str("BaseDN"));
-        cxstring binddn = serverconfig_object_directive_value(obj, cx_str("BindDN"));
-        cxstring basepw = serverconfig_object_directive_value(obj, cx_str("BindPW"));
-        
-        conf.hostname = cx_strdup_a(cfg->a, host).ptr;
-        conf.port = atoi(port.ptr);
-        conf.basedn = cx_strdup_a(cfg->a, basedn).ptr;
-        conf.binddn = cx_strdup_a(cfg->a, binddn).ptr;
-        conf.bindpw = cx_strdup_a(cfg->a, basepw).ptr;
-        
-        authdb = create_ldap_authdb(cfg, name.ptr, &conf);      
+        authdb = create_ldap_authdb(cfg, name.ptr, obj);      
     } else if(!cx_strcmp(type, cx_str("keyfile"))) {
         // we only need the file parameter
         cxstring file = serverconfig_object_directive_value(obj, cx_str("File"));
--- a/src/server/daemon/keyfile_auth.c	Sun Mar 12 11:42:17 2023 +0100
+++ b/src/server/daemon/keyfile_auth.c	Sun Mar 12 20:02:04 2023 +0100
@@ -108,7 +108,7 @@
 
 // authdb functions
 
-User* keyfile_get_user(AuthDB *db, const char *user) {
+User* keyfile_get_user(AuthDB *db, Session *sn, Request *rq, const char *user) {
     Keyfile *keyfile = (Keyfile*)db;
     return cxMapGet(keyfile->users, cx_hash_key_str(user));
 }
--- a/src/server/daemon/keyfile_auth.h	Sun Mar 12 11:42:17 2023 +0100
+++ b/src/server/daemon/keyfile_auth.h	Sun Mar 12 20:02:04 2023 +0100
@@ -71,7 +71,7 @@
         cxmutstr *groups,
         size_t ngroups);
 
-User* keyfile_get_user(AuthDB *db, const char *user);
+User* keyfile_get_user(AuthDB *db, Session *sn, Request *rq, const char *user);
 int keyfile_user_verify_password(User *user, const char *password);
 int keyfile_user_check_group(User *user, const char *group);
 void keyfile_user_free(User *user);
--- a/src/server/daemon/ldap_auth.c	Sun Mar 12 11:42:17 2023 +0100
+++ b/src/server/daemon/ldap_auth.c	Sun Mar 12 20:02:04 2023 +0100
@@ -39,86 +39,138 @@
 #include <cx/hash_map.h>
 
 #include "ldap_auth.h"
+#include "ldap_resource.h"
 
-static void ws_ldap_close(LDAP *ldap) {
-#ifdef SOLARIS
-    ldap_unbind(ldap);
-#else
-    ldap_unbind_ext_s(ldap, NULL, NULL);
-#endif
-}
+
+static LDAPConfig ws_ldap_default_config = {
+    NULL, // resource
+    NULL, // basedn
+    NULL, // binddn
+    NULL, // bindpw
+    "(&(objectclass=inetorgperson)(!(cn=%s)(uid=%s)))", // userSearchFilter
+    {"uid"}, // uidAttributes
+    1, // numUidAttributes
+    "(&(|(objectclass=groupOfNames)(objectclass=groupOfUniqueNames))(cn=%s))", // groupSearchFilter
+    {"member", "uniqueMember"}, // memberAttributes
+    2, // numMemberAttributes
+    WS_LDAP_GROUP_MEMBER_DN, // groupMemberType
+    TRUE, // enableGroups
+    FALSE // userNameIsDN
+};
 
-AuthDB* create_ldap_authdb(ServerConfiguration *cfg, const char *name, LDAPConfig *conf) {
+// TODO
+static LDAPConfig ws_ldap_ad_config = {
+    NULL, // resource
+    NULL, // basedn
+    NULL, // binddn
+    NULL, // bindpw
+    "(&(objectclass=inetorgperson)(!(cn=%s)(uid=%s)))", // userSearchFilter
+    {"uid"}, // uidAttributes
+    1, // numUidAttributes
+    "", // groupSearchFilter
+    {"uniqueMember", "member"}, // memberAttributes
+    2, // numMemberAttributes
+    WS_LDAP_GROUP_MEMBER_DN, // groupMemberType
+    TRUE, // enableGroups
+    FALSE // userNameIsDN
+};
+
+static LDAPConfig ws_ldap_posix_config = {
+    NULL, // resource
+    NULL, // basedn
+    NULL, // binddn
+    NULL, // bindpw
+    "(&(objectclass=posixAccount)(uid=%s))", // userSearchFilter
+    {"uid"}, // uidAttributes
+    1, // numUidAttributes
+    "(&(objectclass=posixGroup)(cn=%s))", // groupSearchFilter
+    {"memberUid"}, // memberAttributes
+    1, // numMemberAttributes
+    WS_LDAP_GROUP_MEMBER_UID, // groupMemberType
+    TRUE, // enableGroups
+    FALSE // userNameIsDN
+};
+
+AuthDB* create_ldap_authdb(ServerConfiguration *cfg, const char *name, ConfigNode *node) {
     LDAPAuthDB *authdb = cxMalloc(cfg->a, sizeof(LDAPAuthDB));
+    if(!authdb) {
+        return NULL;
+    }
     authdb->authdb.name = pool_strdup(cfg->pool, name);
+    if(!authdb->authdb.name) {
+        return NULL;
+    }
     authdb->authdb.get_user = ldap_get_user;
     authdb->authdb.use_cache = 1;
-    authdb->config = *conf;
-
-    if (!authdb->config.usersearch) {
-        authdb->config.usersearch = "uid";
+    
+    // initialize default ldap config
+    cxstring dirtype = serverconfig_object_directive_value(node, cx_str("DirectoryType"));
+    LDAPConfig *default_config;
+    if(!dirtype.ptr) {
+        default_config = &ws_ldap_default_config;
+    } else if(!cx_strcmp(dirtype, cx_str("ldap"))) {
+        default_config = &ws_ldap_default_config;
+    } else if(!cx_strcmp(dirtype, cx_str("posix"))) {
+        default_config = &ws_ldap_posix_config;
+    } else if(!cx_strcmp(dirtype, cx_str("ad"))) {
+        default_config = &ws_ldap_ad_config;
+    } else {
+        log_ereport(LOG_FAILURE, "cannot create ldap authdb %s: unknown directory type %s", name, dirtype.ptr);
     }
-    if (!authdb->config.groupsearch) {
-        authdb->config.groupsearch = "uniquemember";
+    memcpy(&authdb->config, default_config, sizeof(LDAPConfig));
+    
+    // custom config
+    cxstring resource = serverconfig_object_directive_value(node, cx_str("Resource"));
+    cxstring basedn = serverconfig_object_directive_value(node, cx_str("Basedn"));
+    cxstring binddn = serverconfig_object_directive_value(node, cx_str("Binddn"));
+    cxstring bindpw = serverconfig_object_directive_value(node, cx_str("Bindpw"));
+    cxstring usersearchfilter = serverconfig_object_directive_value(node, cx_str("UserSearchFilter"));
+    // TODO ...
+    
+    if(!resource.ptr) {
+        // TODO: create resource pool
+    } else {
+        authdb->config.resource = resource.ptr;
     }
     
     // initialize group cache
     authdb->groups.first = NULL;
     authdb->groups.last = NULL;
     authdb->groups.map = cxHashMapCreate(cfg->a, 32);
+    if(!authdb->groups.map) {
+        return NULL;
+    }
 
     return (AuthDB*) authdb;
 }
 
-LDAP* get_ldap_session(LDAPAuthDB *authdb) {
-    LDAPConfig *config = &authdb->config;
-    LDAP *ld = NULL;
-    
-#ifdef SOLARIS
-    ld = ldap_init(config->hostname, config->port);
-#else
-    char *ldap_uri = NULL;
-    asprintf(&ldap_uri, "ldap://%s:%d", config->hostname, config->port);
-    int init_ret = ldap_initialize(&ld, ldap_uri);
-    free(ldap_uri);
-    if(init_ret) {
-        fprintf(stderr, "ldap_initialize failed\n");
-    }
-#endif
-    if(!ld) {
+LDAP* get_ldap_session(Session *sn, Request *rq, LDAPAuthDB *authdb) {
+    ResourceData *res = resourcepool_lookup(sn, rq, authdb->config.resource, 0);
+    if(!res) {
+        log_ereport(LOG_FAILURE, "AuthDB %s: cannot get resource %s", authdb->authdb.name, authdb->config.resource);
         return NULL;
     }
     
-    int ldapv = LDAP_VERSION3;
-    ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapv);
+    LDAP *ldap = res->data;
     
-    // admin bind
-    struct berval cred;
-    cred.bv_val = config->bindpw;
-    cred.bv_len = strlen(config->bindpw);
-    struct berval *server_cred;
-    int r = ldap_sasl_bind_s(
-            ld,
-            config->binddn,
-            LDAP_SASL_SIMPLE,
-            &cred,
-            NULL,
-            NULL,
-            &server_cred);
-    if (r != LDAP_SUCCESS) {
-        ws_ldap_close(ld);
-        fprintf(stderr, "ldap_simple_bind_s failed: %s\n", ldap_err2string(r));
-        return NULL;
+    if(authdb->config.binddn) {
+        struct berval *server_cred;
+        int r = ws_ldap_bind(ldap, authdb->config.binddn, authdb->config.bindpw, &server_cred);
+        if(r != LDAP_SUCCESS) {
+            log_ereport(LOG_FAILURE, "AuthDB %s: bind to %s failed: %s", authdb->config.binddn, ldap_err2string(r));
+            resourcepool_free(sn, rq, res);
+            return NULL;
+        }
     }
     
-    return ld;
+    return ldap;
 }
 
-User* ldap_get_user(AuthDB *db, const char *username) {
+User* ldap_get_user(AuthDB *db, Session *sn, Request *rq, const char *username) {
     LDAPAuthDB *authdb = (LDAPAuthDB*) db;
     LDAPConfig *config = &authdb->config;
 
-    LDAP *ld = get_ldap_session(authdb);
+    LDAP *ld = get_ldap_session(sn, rq, authdb);
     if (ld == NULL) {
         fprintf(stderr, "ldap_init failed\n");
         return NULL;
@@ -156,13 +208,15 @@
 
     LDAPMessage *msg = ldap_first_entry(ld, result);
     if (msg) {
-        LDAPUser *user = malloc(sizeof(LDAPUser));
+        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 = (char*)username; // must not be freed TODO: maybe copy
+            user->user.name = pool_strdup(sn->pool, username);
+            user->sn = sn;
+            user->rq = rq;
             
             // TODO: get uid/gid from ldap
             user->user.uid = -1;
@@ -181,12 +235,12 @@
     return NULL;
 }
 
-LDAPGroup* ldap_get_group(LDAPAuthDB *authdb, const char *group) {
+LDAPGroup* ldap_get_group(Session *sn, Request *rq, LDAPAuthDB *authdb, const char *group) {
     printf("ldap_get_group: %s\n", group);
     
     LDAPConfig *config = &authdb->config;
     
-    LDAP *ld = get_ldap_session(authdb);
+    LDAP *ld = get_ldap_session(sn, rq, authdb);
     if (ld == NULL) {
         fprintf(stderr, "ldap_init failed\n");
         return NULL;
@@ -302,7 +356,7 @@
     
     int ret = 0;
     
-    LDAPGroup *group = ldap_get_group(user->authdb, group_str);
+    LDAPGroup *group = ldap_get_group(user->sn, user->rq, user->authdb, group_str);
     for(int i=0;i<group->nmembers;i++) {
         char *member = group->members[i].name;
         if(!strcmp(member, u->name)) {
--- a/src/server/daemon/ldap_auth.h	Sun Mar 12 11:42:17 2023 +0100
+++ b/src/server/daemon/ldap_auth.h	Sun Mar 12 20:02:04 2023 +0100
@@ -47,15 +47,96 @@
 typedef struct ldap_member      LDAPMember;
 typedef struct ldap_group_cache LDAPGroupCache;
 
+/*
+
+ * 
+ * WS_LDAP_GROUP_MEMBER_UID: the member attribute contains the user uid
+ *    e.g. member attribute of posixGroup
+ *    memberUid: user
+ */
+enum WSLdapGroupMemberType {
+    /*
+     * the member attribute contains the full user dn
+     * for example object class groupOfUniqueNames attribute uniqueMember
+     * uniqueMember: uid=user,ou=People,dc=example,dc=com
+     */
+    WS_LDAP_GROUP_MEMBER_DN = 0,
+    
+    /*
+     * the member attribute contains the user uid
+     * for example object class posixGroup attribute memberUid
+     * memberUid: user
+     */
+    WS_LDAP_GROUP_MEMBER_UID
+};
+
 struct ldap_config {
-    char   *hostname;
-    int    port;
-    int    ssl;
-    char   *basedn;
-    char   *binddn;
-    char   *bindpw;
-    char   *usersearch;
-    char   *groupsearch;
+    /*
+     * ldap resource pool name
+     */
+    const char *resource;
+    
+    /*
+     * ldap basedn
+     */
+    const char *basedn;
+    
+    /*
+     * default bind dn for search operations
+     */
+    const char *binddn;
+    
+    /*
+     * password for default binddn
+     */
+    const char *bindpw;
+    
+    /*
+     * the ldap filter used to resolve user names to DN
+     * this can be specified in the config file directly or it will 
+     * auto-generated later, so it must always be a non-empty string
+     */
+    const char *userSearchFilter;
+    
+    /*
+     * array of user id attributes
+     */
+    char *uidAttributes[10];
+    
+    /*
+     * number of uid attributes
+     */
+    size_t numUidAttributes;
+    
+    /*
+     * same as userSearchFilter, but for groups
+     */
+    const char *groupSearchFilter;
+    
+    /*
+     * array of attributes that represent group members
+     */
+    char *memberAttributes[10];
+    
+    /*
+     * number of group member attributes
+     */
+    size_t numMemberAttributes;
+    
+    /*
+     * value type of the group member attribute
+     */
+    enum WSLdapGroupMemberType groupMemberType;
+    
+    /*
+     * enables/disables support for ldap groups
+     */
+    WSBool enableGroups;
+    
+    /*
+     * use the full DN internally as user name
+     */
+    WSBool userNameIsDN;
 };
 
 struct ldap_group_cache {
@@ -74,6 +155,8 @@
     User         user;
     LDAPAuthDB   *authdb;
     LDAP         *ldap;
+    Session      *sn;
+    Request      *rq;
     char         *userdn;
     int          uid;
     int          gid;
@@ -92,13 +175,37 @@
     LDAPGroup   *next;
 };
 
-AuthDB* create_ldap_authdb(ServerConfiguration *cfg, const char *name, LDAPConfig *conf);
-
-LDAP* get_ldap_session(LDAPAuthDB *authdb);
+/*
+ * Creates an LDAP AuthDB
+ * 
+ * Config parameters (from ConfigNode *node):
+ * Resource           ldap resource pool name
+ * Basedn             ldap base dn
+ * Binddn             binddn for search operations
+ * Bindpw             binddn password
+ * DirectoryType      type of the directory service (ldap|ad) which acts as
+ *                    config preset for filter and attribute settings
+ * UserSearchFilter   ldap search filter for user dn resolution
+ * UidAttributes      comma separated list of attributes, that contain the uid
+ * GroupSearchFilter  ldap search filter for group resolution
+ * MemberAttributes   comma separated list of group member attributes
+ * MemberType         member attribute type (dn|uid)
+ * EnableGroups       enable or disable support for groups
+ * UserNameIsDn       should the uid or the dn used internally as user name
+ * 
+ * 
+ * If no Resource parameter is specified, a resource pool is automatically
+ * created with the name _<authdbname>_ldap and all parameters from the
+ * ConfigNode are passed to resourcepool_new(). That means, all ldap
+ * resource pool parameters can also specified in the AuthDB object.
+ */
+AuthDB* create_ldap_authdb(ServerConfiguration *cfg, const char *name, ConfigNode *node);
 
-User* ldap_get_user(AuthDB *sb, const char *username);
+LDAP* get_ldap_session(Session *sn, Request *rq, LDAPAuthDB *authdb);
 
-LDAPGroup* ldap_get_group(LDAPAuthDB *authdb, const char *group);
+User* ldap_get_user(AuthDB *sb, Session *sn, Request *rq, const char *username);
+
+LDAPGroup* ldap_get_group(Session *sn, Request *rq, LDAPAuthDB *authdb, const char *group);
 
 int ldap_user_verify_password(User *user, const char *password);
 int ldap_user_check_group(User *user, const char *group);
--- a/src/server/public/auth.h	Sun Mar 12 11:42:17 2023 +0100
+++ b/src/server/public/auth.h	Sun Mar 12 20:02:04 2023 +0100
@@ -31,6 +31,8 @@
 
 #include <sys/types.h>
 
+#include "nsapi.h"
+
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -49,11 +51,11 @@
  * param1: authentication database
  * param2: user
  */
-typedef User*(*authdb_get_user_f)(AuthDB*, const char*);
+typedef User*(*authdb_get_user_f)(AuthDB*, Session*, Request*, const char*);
 
 struct auth_db {
     char                *name;
-    /* User* get_user(AuthDB *db, char *username) */
+    /* User* get_user(AuthDB *db, Session *sn, Request *rq, char *username) */
     authdb_get_user_f   get_user;
     int                 use_cache;
 };
@@ -93,8 +95,8 @@
 };
 
 
-User* authdb_get_user(AuthDB *db, const char *user);
-User* authdb_get_and_verify(AuthDB *db, const char *user, const char *password, int *pw);
+User* authdb_get_user(AuthDB *db, Session *sn, Request *rq, const char *user);
+User* authdb_get_and_verify(AuthDB *db, Session *sn, Request *rq, const char *user, const char *password, int *pw);
 
 #ifdef	__cplusplus
 }
--- a/src/server/safs/auth.c	Sun Mar 12 11:42:17 2023 +0100
+++ b/src/server/safs/auth.c	Sun Mar 12 20:02:04 2023 +0100
@@ -245,7 +245,7 @@
     ServerConfiguration *config = session_get_config(sn);
     AuthDB *authdb = cxMapGet(config->authdbs, cx_hash_key_str(db));
     
-    User *auth_user = authdb->get_user(authdb, user);
+    User *auth_user = authdb->get_user(authdb, sn, rq, user);
     if(auth_user && !auth_user->verify_password(auth_user, pw)) {
         fprintf(stderr, "authdb user not authenticated: %s\n", user);
         free(user);

mercurial