src/server/daemon/ldap_resource.c

Wed, 27 Nov 2024 23:00:07 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 27 Nov 2024 23:00:07 +0100
changeset 563
6ca97c99173e
parent 475
d01ea909d906
permissions
-rw-r--r--

add TODO to use a future ucx feature

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2023 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 "ldap_resource.h"

#include "../util/util.h"



static ResourceType ldap_resource_type = {
    (resource_pool_init_func)ldap_resourcepool_init,
    (resource_pool_destroy_func)ldap_resourcepool_destroy,
    (resource_pool_createresource_func)ldap_resourcepool_createresource,
    (resource_pool_freeresource_func)ldap_resourcepool_freeresource,
    (resource_pool_prepare_func)ldap_resourcepool_prepare,
    (resource_pool_finish_func)ldap_resourcepool_finish,
    (resource_pool_getresourcedata_func)ldap_resourcepool_getresourcedata
};


ResourceType* ldap_get_resource_type(void) {
    return &ldap_resource_type;
}

LDAP* ws_ldap_resource_create_connection(
        const char *hostname,
        int port,
        int ssl,
        int ldap_version)
{
    LDAP *ld = NULL;
    
#ifdef SOLARIS
    ld = ldap_init(config->hostname, config->port);
#else
    char *ldap_uri = NULL;
    asprintf(&ldap_uri, "ldap://%s:%d", hostname, port);
    ld = ws_ldap_resource_create_uri_connection(ldap_uri, ldap_version);
    free(ldap_uri);
#endif
    
    if(ld) {
        ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
    }
    
    return ld;
}

LDAP* ws_ldap_resource_create_uri_connection(
        const char *uri,
        int ldap_version)
{
#ifdef SOLARIS
    log_ereport(LOG_FAILURE, "ldap_resource_create_connection_uri is not implemented on Solaris yet");
    return NULL;
#else
    
    LDAP *ld = NULL;
    int init_ret = ldap_initialize(&ld, uri);
    if(init_ret) {
        log_ereport(
                LOG_FAILURE,
                "ldap_resource_create_connection failed: uri: %s",
                uri);
    }
    return ld;
#endif
}

void ws_ldap_close(LDAP *ldap) {
#ifdef SOLARIS
    ldap_unbind(ldap);
#else
    ldap_unbind_ext_s(ldap, NULL, NULL);
#endif
}


/*
 * Validates settings from the pb pblock
 * and creates an LDAPResourcePool object
 * 
 * LDAPResourcePool contains all settings necessary for creating
 * ldap connections.
 */
void * ldap_resourcepool_init(pool_handle_t *pool, const char *rpname, pblock *pb) {
    char *ldap_uri = pblock_findval("Uri", pb);
    char *host = pblock_findval("Host", pb);
    char *port = pblock_findval("Port", pb);
    char *binddn = pblock_findval("Binddn", pb);
    char *bindpw = pblock_findval("Bindpw", pb);
    char *bind = pblock_findval("Bind", pb);
    
    log_ereport(
            LOG_INFORM,
            "create ldap resource pool: name=%s : %s%s%s%s : binddn=%s",
            rpname,
            ldap_uri ? "uri=" : "host=",
            ldap_uri ? ldap_uri : host,
            ldap_uri || !port ? "" : ":",
            !port ? "" : port,
            binddn ? binddn : "''");
    
    if(!ldap_uri && !host) {
        log_ereport(LOG_MISCONFIG, "Resource pool %s: No host or ldap uri specified", rpname);
        return NULL;
    }
    if(ldap_uri && host) {
        log_ereport(LOG_MISCONFIG, "Resource pool %s: Either Uri or Host must be specified, not both", rpname);
        return NULL;
    }
    
    int64_t port_i = 0;
    if(host) {
        if(port) {
            if(util_strtoint(port, &port_i)) {
                if(port_i < 1 || port_i > 65535) {
                    log_ereport(LOG_MISCONFIG, "Resource pool %s: Port %s is out of range", rpname, port);
                }
            } else {
                log_ereport(LOG_MISCONFIG, "Resource pool %s: Port '%s' is not a valid number", rpname, port);
            }
        } else {
            port_i = LDAP_PORT;
        }
    }
    
    LDAPResourcePool *ldap_pool = pool_malloc(pool, sizeof(LDAPResourcePool));
    if(!ldap_pool) {
        return NULL;
    }
    
    ldap_pool->name = rpname;
    ldap_pool->pool = pool;
    ldap_pool->ldap_uri = ldap_uri;
    ldap_pool->host = host;
    ldap_pool->port = (int)port_i;
    ldap_pool->binddn = binddn;
    ldap_pool->bindpw = bindpw;
    WSBool bind_def = binddn != NULL;
    ldap_pool->bind = bind ? util_getboolean(bind, bind_def) : bind_def;
    
    return ldap_pool;
}

void   ldap_resourcepool_destroy(LDAPResourcePool *pool) {
    // unused
}

void * ldap_resourcepool_createresource(LDAPResourcePool *respool) {
    LDAP *ldap = NULL;
    if(respool->ldap_uri) {
        ldap = ws_ldap_resource_create_uri_connection(respool->ldap_uri, LDAP_VERSION3);
    } else {
        ldap = ws_ldap_resource_create_connection(respool->host, respool->port, FALSE, LDAP_VERSION3);
    }
    
    if(!ldap) {
        log_ereport(
                LOG_FAILURE,
                "resource pool %s: %s: cannot create LDAP session",
                respool->name,
                respool->ldap_uri ? respool->ldap_uri : respool->host);
        return NULL;
    }
    
    if(respool->bind) {
        struct berval *server_cred;
        int r = ldap_resource_bind(respool, ldap, &server_cred);
        if(r != LDAP_SUCCESS) {
            log_ereport(LOG_FAILURE, "resource pool %s: bind failed: %s", respool->name, ldap_err2string(r));
            ws_ldap_close(ldap);
            return NULL;
        }
    }
    
    LDAPResource *res = pool_malloc(respool->pool, sizeof(LDAPResource));
    if(!res) {
        ws_ldap_close(ldap);
        log_ereport(LOG_CATASTROPHE, "ldap_resourcepool_createresource: OOM");
        return NULL;
    }
    res->ldap = ldap;
    res->res_pool = respool;
    
    return res;
}

void   ldap_resourcepool_freeresource(LDAPResourcePool *pool, LDAPResource *res) {
    if(res->ldap) {
        ws_ldap_close(res->ldap);
    }
    pool_free(pool->pool, res);
}

int    ldap_resourcepool_prepare(LDAPResourcePool *pool, LDAPResource *res) {
    // unused
    return 0;
}

int    ldap_resourcepool_finish(LDAPResourcePool *pool, LDAPResource *res) {
    // unused
    return 0;
}

void * ldap_resourcepool_getresourcedata(LDAPResource *res) {
    return res->ldap;
}


int ldap_resource_bind(LDAPResourcePool *respool, LDAP *ldap, struct berval **server_cred) {
    if(!respool->binddn) {
        return -1;
    }
    return ws_ldap_bind(ldap, respool->binddn, respool->bindpw, server_cred);
}

int ws_ldap_bind(LDAP *ldap, const char *binddn, const char *bindpw, struct berval **server_cred) {
    struct berval cred;
    cred.bv_val = (char*)bindpw;
    cred.bv_len = strlen(cred.bv_val);
    return ldap_sasl_bind_s(
            ldap,
            binddn,
            LDAP_SASL_SIMPLE,
            &cred,
            NULL,
            NULL,
            server_cred);
}

mercurial