src/server/daemon/ldap_auth.c

Sat, 31 Oct 2015 20:10:21 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 31 Oct 2015 20:10:21 +0100
changeset 109
8a0a7754f123
parent 101
7fbcdbad0baa
child 111
c93be34fde76
permissions
-rw-r--r--

experimental BSD support

/*
 * 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 <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);
}

mercurial