Mon, 26 Dec 2016 16:46:55 +0100
adds support for ssl cert chain files and improves ssl error handling
/* * 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 <ucx/utils.h> #include "ldap_auth.h" static void ws_ldap_close(LDAP *ldap) { #ifdef SOLARIS ldap_unbind(ldap); #else ldap_unbind_ext_s(ldap, NULL, NULL); #endif } AuthDB* create_ldap_authdb(char *name, LDAPConfig *conf) { LDAPAuthDB *authdb = malloc(sizeof(LDAPAuthDB)); authdb->authdb.name = strdup(name); authdb->authdb.get_user = ldap_get_user; authdb->authdb.use_cache = 1; authdb->config = *conf; if (!authdb->config.usersearch) { authdb->config.usersearch = "uid"; } if (!authdb->config.groupsearch) { authdb->config.groupsearch = "uniquemember"; } // initialize group cache authdb->groups.first = NULL; authdb->groups.last = NULL; authdb->groups.map = ucx_map_new(32); 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) { return NULL; } int ldapv = LDAP_VERSION3; ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapv); // 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; } return ld; } User* ldap_get_user(AuthDB *db, char *username) { LDAPAuthDB *authdb = (LDAPAuthDB*) db; LDAPConfig *config = &authdb->config; LDAP *ld = get_ldap_session(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 = malloc(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 = username; // must not be freed // 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(LDAPAuthDB *authdb, char *group) { printf("ldap_get_group: %s\n", group); LDAPConfig *config = &authdb->config; LDAP *ld = get_ldap_session(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++) { sstr_t member = sstrn( values[i]->bv_val, values[i]->bv_len); wsgroup->members[i].name = sstrdup(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, char *password) { LDAPUser *user = (LDAPUser*)u; //int r = ldap_simple_bind_s(user->ldap, user->userdn, password); struct berval cred; cred.bv_val = 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, char *group_str) { LDAPUser *user = (LDAPUser*)u; int ret = 0; LDAPGroup *group = ldap_get_group(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); }