# HG changeset patch # User Olaf Wintermann # Date 1678551291 -3600 # Node ID 0a29110b94eccca2dab788a610255b93cafdf40c # Parent 4fd523fff13bd89f274ee699990af603e1367f0a add ldap-query saf diff -r 4fd523fff13b -r 0a29110b94ec configure --- a/configure Sat Mar 11 13:57:30 2023 +0100 +++ b/configure Sat Mar 11 17:14:51 2023 +0100 @@ -344,7 +344,7 @@ do CFLAGS="$CFLAGS -DLINUX" - LDFLAGS="$LDFLAGS -lpthread -ldl -lm -lldap" + LDFLAGS="$LDFLAGS -lpthread -ldl -lm -lldap -llber" cat >> $TEMP_DIR/make.mk << __EOF__ # platform dependend source files PLATFORM_DAEMONOBJ = event_linux.o @@ -377,7 +377,7 @@ do CFLAGS="$CFLAGS -DBSD -I/usr/local/include" - LDFLAGS="$LDFLAGS -lpthread -lm -lldap" + LDFLAGS="$LDFLAGS -lpthread -lm -lldap -llber" cat >> $TEMP_DIR/make.mk << __EOF__ # platform dependend source files PLATFORM_DAEMONOBJ = event_bsd.o @@ -407,7 +407,7 @@ do CFLAGS="$CFLAGS -DBSD -DOSX" - LDFLAGS="$LDFLAGS -lpthread -ldl -lm -lldap" + LDFLAGS="$LDFLAGS -lpthread -ldl -lm -lldap -llber" cat >> $TEMP_DIR/make.mk << __EOF__ # platform dependend source files PLATFORM_DAEMONOBJ = event_bsd.o diff -r 4fd523fff13b -r 0a29110b94ec make/project.xml --- a/make/project.xml Sat Mar 11 13:57:30 2023 +0100 +++ b/make/project.xml Sat Mar 11 17:14:51 2023 +0100 @@ -10,7 +10,7 @@ -DLINUX - -lpthread -ldl -lm -lldap + -lpthread -ldl -lm -lldap -llber # platform dependend source files PLATFORM_DAEMONOBJ = event_linux.o @@ -28,7 +28,7 @@ -DBSD -I/usr/local/include - -lpthread -lm -lldap + -lpthread -lm -lldap -llber # platform dependend source files PLATFORM_DAEMONOBJ = event_bsd.o @@ -46,7 +46,7 @@ -DBSD -DOSX - -lpthread -ldl -lm -lldap + -lpthread -ldl -lm -lldap -llber # platform dependend source files PLATFORM_DAEMONOBJ = event_bsd.o diff -r 4fd523fff13b -r 0a29110b94ec src/server/config/serverconfig.c --- a/src/server/config/serverconfig.c Sat Mar 11 13:57:30 2023 +0100 +++ b/src/server/config/serverconfig.c Sat Mar 11 17:14:51 2023 +0100 @@ -227,8 +227,17 @@ */ static void config_arg_set_value(CxAllocator *a, ConfigParam *arg, CFGToken token) { - cxstring nv = cx_strchr(token.content, '='); + cxstring nv = (cxstring){NULL,0}; + WSBool quotes = token.content.ptr[0] == '\"'; + if(!quotes) { + nv = cx_strchr(token.content, '='); + } if(!nv.ptr) { + if(quotes) { + // remove quote + token.content.ptr++; + token.content.length -= 2; + } arg->value = cx_strdup_a(a, token.content); } else { intptr_t eq = (intptr_t)(nv.ptr - token.content.ptr); @@ -292,7 +301,7 @@ int text_start = 0; int err = 0; while((token = get_next_token(parser, content, &pos)).type != CFG_NO_TOKEN) { - //printf("%s [%.*s]\n", token_type_str(token.type), (int)token.content.length, token.content.ptr); + //printf("[%.*s]\n", (int)token.content.length, token.content.ptr); fflush(stdout); switch(token.type) { case CFG_NO_TOKEN: break; diff -r 4fd523fff13b -r 0a29110b94ec src/server/daemon/ldap_resource.c --- a/src/server/daemon/ldap_resource.c Sat Mar 11 13:57:30 2023 +0100 +++ b/src/server/daemon/ldap_resource.c Sat Mar 11 17:14:51 2023 +0100 @@ -57,26 +57,18 @@ #ifdef SOLARIS ld = ldap_init(config->hostname, config->port); - if(ld) { - ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldap_version); - } else { - log_ereport( - LOG_FAILURE, - "ldap_resource_create_connection failed: host: %s port: %d", - hostname, - 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) { - return NULL; + + if(ld) { + ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldap_version); } - return NULL; + return ld; } LDAP* ws_ldap_resource_create_uri_connection( @@ -191,7 +183,7 @@ if(!ldap) { log_ereport( LOG_FAILURE, - "Resource pool %s: %s: cannot create LDAP session", + "resource pool %s: %s: cannot create LDAP session", respool->name, respool->ldap_uri ? respool->ldap_uri : respool->host); return NULL; @@ -199,8 +191,9 @@ if(respool->bind) { struct berval *server_cred; - if(ldap_resource_bind(respool, ldap, &server_cred) != LDAP_SUCCESS) { - log_ereport(LOG_FAILURE, "Resource pool %s: bind failed", respool->name); + 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; } @@ -244,16 +237,20 @@ 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 = respool->bindpw; + cred.bv_val = (char*)bindpw; cred.bv_len = strlen(cred.bv_val); return ldap_sasl_bind_s( ldap, - respool->binddn, + binddn, LDAP_SASL_SIMPLE, &cred, NULL, NULL, server_cred); } + diff -r 4fd523fff13b -r 0a29110b94ec src/server/daemon/ldap_resource.h --- a/src/server/daemon/ldap_resource.h Sat Mar 11 13:57:30 2023 +0100 +++ b/src/server/daemon/ldap_resource.h Sat Mar 11 17:14:51 2023 +0100 @@ -128,6 +128,8 @@ int ldap_resource_bind(LDAPResourcePool *respool, LDAP *ldap, struct berval **server_cred); +int ws_ldap_bind(LDAP *ldap, const char *binddn, const char *bindpw, struct berval **server_cred); + #ifdef __cplusplus diff -r 4fd523fff13b -r 0a29110b94ec src/server/daemon/ws-fn.c --- a/src/server/daemon/ws-fn.c Sat Mar 11 13:57:30 2023 +0100 +++ b/src/server/daemon/ws-fn.c Sat Mar 11 17:14:51 2023 +0100 @@ -38,6 +38,7 @@ #include "../safs/common.h" #include "../safs/addlog.h" #include "../safs/cgi.h" +#include "../safs/ldap.h" #include "../webdav/webdav.h" #include "../admin/admin.h" @@ -70,6 +71,7 @@ { "set-variable", set_variable, NULL, NULL, 0}, { "common-log", common_log, NULL, NULL, 0}, { "send-cgi", send_cgi, NULL, NULL, 0}, + { "ldap-query", ldap_query_saf, NULL, NULL, 0}, { "webdav-init", webdav_init, NULL, NULL, 0}, { "webdav-service", webdav_service, NULL, NULL, 0}, {NULL, NULL, NULL, NULL, 0} diff -r 4fd523fff13b -r 0a29110b94ec src/server/plugins/postgresql/vfs.c --- a/src/server/plugins/postgresql/vfs.c Sat Mar 11 13:57:30 2023 +0100 +++ b/src/server/plugins/postgresql/vfs.c Sat Mar 11 17:14:51 2023 +0100 @@ -162,7 +162,7 @@ } } - // get the resource first (most likely to fail due to misconfig) + // get the resource first (most likely failure reason is misconfig) ResourceData *resdata = resourcepool_lookup(sn, rq, resource_pool, 0); if(!resdata) { log_ereport(LOG_MISCONFIG, "postgresql vfs: resource pool %s not found", resource_pool); diff -r 4fd523fff13b -r 0a29110b94ec src/server/safs/ldap.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/safs/ldap.c Sat Mar 11 17:14:51 2023 +0100 @@ -0,0 +1,243 @@ +/* + * 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 +#include + +#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 ldap_query_saf(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 *ldap_query = pblock_findval("query", pb); + char *empty_query_error = pblock_findval("empty_query_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_query = WS_SAFS_LDAP_EMPTY_QUERY_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-query: empty_query_error parameter must be an integer between 200 and 999"); + return REQ_ABORTED; + } + status_empty_query = 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-query: 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_query < 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-query: 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-query: timeout out of range"); + return REQ_ABORTED; + } + timeout = t; + } else { + log_ereport(LOG_MISCONFIG, "ldap-query: timeout %s is not a number", timeout_str); + } + } + int sizelimit = WS_SAFS_LDAP_DEFAULT_SIZELIMIT; + if(timeout_str) { + int64_t v; + if(util_strtoint(timeout_str, &v)) { + if(v > INT_MAX) { + log_ereport(LOG_MISCONFIG, "ldap-query: sizelimit out of range"); + return REQ_ABORTED; + } + sizelimit = v; + } else { + log_ereport(LOG_MISCONFIG, "ldap-query: sizelimit %s is not a number", timeout_str); + } + } + + + if(!resource_name) { + log_ereport(LOG_MISCONFIG, "ldap-query: missing resource parameter"); + return REQ_ABORTED; + } + if(!basedn) { + log_ereport(LOG_MISCONFIG, "ldap-query: missing basedn parameter"); + return REQ_ABORTED; + } + + if(!ldap_query) { + // alternatively get query from rq->vars + ldap_query = pblock_findval("ldap_query", rq->vars); + if(!ldap_query) { + // no ldap query + protocol_status(sn, rq, status_empty_query, NULL); + if(empty_query_response) { + pblock_nvinsert("content-length", "0", rq->srvhdrs); + http_start_response(sn, rq); + } + return REQ_PROCEED; + } + } + + // get the resource + ResourceData *resdata = resourcepool_lookup(sn, rq, resource_name, 0); + if(!resdata) { + log_ereport(LOG_FAILURE, "ldap-query: 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-query: 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, + ldap_query, + 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-query: 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;ibv_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; +} diff -r 4fd523fff13b -r 0a29110b94ec src/server/safs/ldap.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/safs/ldap.h Sat Mar 11 17:14:51 2023 +0100 @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#ifndef WS_SAFS_LDAP_H +#define WS_SAFS_LDAP_H + +#include "../public/nsapi.h" + +#include "../daemon/ldap_resource.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WS_SAFS_LDAP_DEFAULT_SCOPE LDAP_SCOPE_SUBTREE +#define WS_SAFS_LDAP_DEFAULT_TIMEOUT 30 +#define WS_SAFS_LDAP_DEFAULT_SIZELIMIT 1000 + +#define WS_SAFS_LDAP_MAX_TIMEOUT 32767 + +#define WS_SAFS_LDAP_EMPTY_QUERY_ERROR 404 +#define WS_SAFS_LDAP_EMPTY_RESULT_ERROR 404 + +/* + * ldap-query + * + * Sends an ldap query result as ldif to the client. If no query parameter is + * specified, the SAFs tries to use the "ldap_query" parameter from rq->vars. + * + * required parameters: + * resource name of the ldap resource pool + * basedn ldap basedn + * + * optional parameters: + * binddn bind ldap session to binddn + * bindpw binddn password + * query ldap search query + * scope search scope: base, onelevel, subtree, children + * timeout timeout in seconds default: 30 + * sizelimit maximum number of result entries defazkt: 1000 + * empty_query_error status code if the query is empty/null default: 404 + * empty_result_error status code if the result is empty default: 404 + * + * If the query or result is empty and the status code is 2xx, an empty + * response is sent to the client. + */ +int ldap_query_saf(pblock *pb, Session *sn, Request *rq); + + +#ifdef __cplusplus +} +#endif + +#endif /* WS_SAFS_LDAP_H */ + diff -r 4fd523fff13b -r 0a29110b94ec src/server/safs/objs.mk --- a/src/server/safs/objs.mk Sat Mar 11 13:57:30 2023 +0100 +++ b/src/server/safs/objs.mk Sat Mar 11 17:14:51 2023 +0100 @@ -41,6 +41,7 @@ SAFOBJ += addlog.o SAFOBJ += cgi.o SAFOBJ += cgiutils.o +SAFOBJ += ldap.o SAFOBJS = $(SAFOBJ:%=$(SAFS_OBJPRE)%) SAFSOURCE = $(SAFOBJ:%.o=safs/%.c)