src/server/safs/ldap.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 465
d22ff46c171c
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.
 */

#include "ldap.h"

#include <time.h>
#include <limits.h>

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

static int get_ldap_scope(const char *str) {
    // search scope: base, onelevel, subtree, children
    if(!strcmp(str, "base")) {
        return LDAP_SCOPE_BASE;
    } else if(!strcmp(str, "onelevel")) {
        return LDAP_SCOPE_ONELEVEL;
    } else if(!strcmp(str, "subtree")) {
        return LDAP_SCOPE_SUBTREE;
    } else if(!strcmp(str, "children")) {
        return LDAP_SCOPE_CHILDREN;
    }
    return -1;
}

int service_ldap_search(pblock *pb, Session *sn, Request *rq) {
    char *resource_name = pblock_findval("resource", pb);
    char *basedn = pblock_findval("basedn", pb);
    char *binddn = pblock_findval("bindnd", pb);
    char *bindpw = pblock_findval("bindpw", pb);
    char *filter = pblock_findval("filter", pb);
    char *empty_query_error = pblock_findval("empty_filter_error", pb);
    char *empty_result_error = pblock_findval("empty_result_error", pb);
    char *scope_str = pblock_findval("scope", pb);
    char *timeout_str = pblock_findval("timeout", pb);
    char *sizelimit_str = pblock_findval("sizelimit", pb);
    
    int status_empty_filter = WS_SAFS_LDAP_EMPTY_FILTER_ERROR;
    int status_empty_result = WS_SAFS_LDAP_EMPTY_RESULT_ERROR;
    
    if(empty_query_error) {
        int64_t status = 0;
        util_strtoint(empty_query_error, &status);
        if(status < 200 || status > 999) {
            log_ereport(LOG_MISCONFIG, "ldap-search: empty_query_error parameter must be an integer between 200 and 999");
            return REQ_ABORTED;
        }
        status_empty_filter = status;
    }
    if(empty_result_error) {
        int64_t status = 0;
        util_strtoint(empty_result_error, &status);
        if(status < 200 || status > 999) {
            log_ereport(LOG_MISCONFIG, "ldap-search: empty_result_error parameter must be an integer between 200 and 999");
            return REQ_ABORTED;
        }
        status_empty_result = status;
    }
    
    // should we sent an empty response in case of an empty query/result
    // or the standard error message?
    WSBool empty_query_response = status_empty_filter < 300 ? TRUE : FALSE;
    WSBool empty_result_response = status_empty_result < 300 ? TRUE : FALSE;
    
    int scope = WS_SAFS_LDAP_DEFAULT_SCOPE;
    if(scope_str) {
        scope = get_ldap_scope(scope_str);
        if(scope < 0) {
            log_ereport(LOG_MISCONFIG, "ldap-search: unknown scope %s", scope_str);
            return REQ_ABORTED;
        }
    }
    int timeout = WS_SAFS_LDAP_DEFAULT_TIMEOUT;
    if(timeout_str) {
        int64_t t;
        if(util_strtoint(timeout_str, &t)) {
            if(t < 0 || t > WS_SAFS_LDAP_MAX_TIMEOUT) {
                log_ereport(LOG_MISCONFIG, "ldap-search: timeout out of range");
                return REQ_ABORTED;
            }
            timeout = t;
        } else {
            log_ereport(LOG_MISCONFIG, "ldap-search: timeout %s is not a number", timeout_str);
        }
    }
    int sizelimit = WS_SAFS_LDAP_DEFAULT_SIZELIMIT;
    if(sizelimit_str) {
        int64_t v;
        if(util_strtoint(sizelimit_str, &v)) {
            if(v > INT_MAX) {
                log_ereport(LOG_MISCONFIG, "ldap-search: sizelimit out of range");
                return REQ_ABORTED;
            }
            sizelimit = v;
        } else {
            log_ereport(LOG_MISCONFIG, "ldap-search: sizelimit %s is not a number", timeout_str);
        }
    }
    
    
    if(!resource_name) {
        log_ereport(LOG_MISCONFIG, "ldap-search: missing resource parameter");
        return REQ_ABORTED;
    }
    if(!basedn) {
        log_ereport(LOG_MISCONFIG, "ldap-search: missing basedn parameter");
        return REQ_ABORTED;
    }
    
    if(!filter) {
        // alternatively get filter from rq->vars
        filter = pblock_findval("ldap_filter", rq->vars);
        log_ereport(LOG_DEBUG, "ldap-search: no filter parameter, rq.vars ldap_filter: %s", filter);
        if(!filter) {
            // no ldap filter
            protocol_status(sn, rq, status_empty_filter, NULL);
            if(empty_query_response) {
                pblock_nvinsert("content-length", "0", rq->srvhdrs);
                http_start_response(sn, rq);
            } else {
                log_ereport(LOG_FAILURE, "ldap-search: no filter specified");
            }
            return REQ_PROCEED;
        }
    }
    
    // get the resource
    ResourceData *resdata = resourcepool_lookup(sn, rq, resource_name, 0);
    if(!resdata) {
        log_ereport(LOG_FAILURE, "ldap-search: cannot get resource %s", resource_name);
        return REQ_ABORTED;
    }
    LDAP *ldap = resdata->data;
    
    // optionally, use binddn
    if(binddn) {
        struct berval *server_cred;
        if(ws_ldap_bind(ldap, binddn, bindpw ? bindpw : "", &server_cred) != LDAP_SUCCESS) {
            log_ereport(LOG_FAILURE, "ldap-search: resource %s: cannot bind %s", resource_name, binddn);
            resourcepool_free(sn, rq, resdata);
            return REQ_ABORTED;
        }
    }
    
    
    // search
    LDAPMessage *result;
    struct timeval ts;
    ts.tv_sec = timeout;
    ts.tv_usec = 0;
    int r = ldap_search_ext_s(
            ldap,
            basedn,
            LDAP_SCOPE_SUBTREE,
            filter,
            NULL,
            0,
            NULL,        // server controls
            NULL,        // client controls
            &ts,
            sizelimit,   // size limit
            &result);
    
    if(r != LDAP_SUCCESS) {
        if(result) {
            ldap_msgfree(result);
        }
        log_ereport(LOG_FAILURE, "ldap-search: ldap error: %s", ldap_err2string(r));
        return REQ_ABORTED;
    }
    
    
    // send http header
    protocol_status(sn, rq, 200, NULL);
    
    
    LDAPMessage *msg = ldap_first_entry(ldap, result);
    if(!msg) {
        protocol_status(sn, rq, status_empty_result, NULL);
        if(empty_result_response) {
            pblock_nvinsert("content-length", "0", rq->srvhdrs);
        }
    } else {
        protocol_status(sn, rq, 200, NULL);
    }
    http_start_response(sn, rq);    
    
    while(msg) {
        // dn
        char *dn = ldap_get_dn(ldap, msg);
        if(dn) {
            net_printf(sn->csd, "dn: %s\n", dn);
            ldap_memfree(dn);
        }
        
        // get attributes
        BerElement *ber = NULL;
        char *attribute = attribute=ldap_first_attribute(ldap, msg, &ber);
        while(attribute != NULL) {
            struct berval **values = ldap_get_values_len(ldap, msg, attribute);
            if(values) {
                int count = ldap_count_values_len(values);
                for(int i=0;i<count;i++) {
                    cxstring value = cx_strn(values[i]->bv_val, values[i]->bv_len);
                    net_printf(sn->csd, "%s: %.*s\n", attribute, (int)value.length, value.ptr);
                }
                ldap_value_free_len(values);
            }
            ldap_memfree(attribute);
            attribute = ldap_next_attribute(ldap, msg, ber); 
        }
        if(ber) {
            ber_free(ber, 0);
        }
        net_printf(sn->csd, "\n");
        msg = ldap_next_entry(ldap, msg);
    }
    ldap_msgfree(result);
    
    
    return REQ_PROCEED;
}

mercurial