Sun, 12 Mar 2023 20:02:04 +0100
refactore ldap_auth to use resource pools
--- 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);