Sun, 12 Mar 2023 20:02:04 +0100
refactore ldap_auth to use resource pools
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2013 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifdef __gnu_linux__ #define _GNU_SOURCE #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/time.h> #include <cx/utils.h> #include <cx/hash_map.h> #include "ldap_auth.h" #include "ldap_resource.h" 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 }; // 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; // 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); } 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(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; } LDAP *ldap = res->data; 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 ldap; } 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(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]; int s = snprintf(filter, 127, "uid=%s", username); filter[s] = 0; LDAPMessage *result; struct timeval timeout; timeout.tv_sec = 8; timeout.tv_usec = 0; int r = ldap_search_ext_s( ld, config->basedn, LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, // server controls NULL, // client controls &timeout, 1, // size limit &result); if (r != LDAP_SUCCESS) { ws_ldap_close(ld); fprintf(stderr, "ldap_search_ext_s failed\n"); 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; } } ws_ldap_close(ld); return NULL; } 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(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]; int s = snprintf(filter, 127, "cn=%s", group); filter[s] = 0; LDAPMessage *result; struct timeval timeout; timeout.tv_sec = 8; timeout.tv_usec = 0; int r = ldap_search_ext_s( ld, config->basedn, LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, // server controls NULL, // client controls &timeout, 1, // size limit &result); if (r != LDAP_SUCCESS) { ws_ldap_close(ld); fprintf(stderr, "ldap_search_ext_s failed\n"); return NULL; } LDAPGroup *wsgroup = NULL; LDAPMessage *msg = ldap_first_entry(ld, result); if (msg) { // create group object wsgroup = malloc(sizeof(LDAPGroup)); wsgroup->name = strdup(group); wsgroup->members = NULL; wsgroup->nmembers = 0; wsgroup->update = 0; wsgroup->next = NULL; // get attributes BerElement *ber = NULL; char *attribute = attribute=ldap_first_attribute(ld, msg, &ber); while(attribute != NULL) { printf("attribute: %s\n", attribute); if(!strcasecmp(attribute, "memberuid")) { // get all memberuid values and add the users to the group obj struct berval **values = ldap_get_values_len(ld, msg, attribute); if(values) { int count = ldap_count_values_len(values); wsgroup->members = calloc(count, sizeof(LDAPMember)); wsgroup->nmembers = count; for(int i=0;i<count;i++) { cxstring member = cx_strn( values[i]->bv_val, values[i]->bv_len); wsgroup->members[i].name = cx_strdup(member).ptr; // TODO: uid? printf("added member: %.*s\n", (int)member.length, member.ptr); } } } attribute = ldap_next_attribute(ld, msg, ber); } if(ber) { //ldap_ber_free(ber, 0); } if(attribute) { ldap_memfree(attribute); } } ws_ldap_close(ld); return wsgroup; } int ldap_user_verify_password(User *u, const char *password) { LDAPUser *user = (LDAPUser*)u; //int r = ldap_simple_bind_s(user->ldap, user->userdn, password); struct berval cred; cred.bv_val = (char*)password; cred.bv_len = strlen(password); struct berval *server_cred; int r = ldap_sasl_bind_s( user->ldap, user->userdn, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &server_cred); if(r == LDAP_SUCCESS) { printf("ldap password ok\n"); return 1; } else { printf("ldap password not ok\n"); return 0; } } int ldap_user_check_group(User *u, const char *group_str) { LDAPUser *user = (LDAPUser*)u; int ret = 0; 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)) { printf("is member\n"); ret = 1; } } // TODO: free or cache group return ret; } void ldap_user_free(User *u) { LDAPUser *user = (LDAPUser*)u; ldap_memfree(user->userdn); // TODO: use connection pool ws_ldap_close(user->ldap); free(user); }