UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2023 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "ldap.h" 30 31 #include <time.h> 32 #include <limits.h> 33 34 #include "../util/util.h" 35 36 static int get_ldap_scope(const char *str) { 37 // search scope: base, onelevel, subtree, children 38 if(!strcmp(str, "base")) { 39 return LDAP_SCOPE_BASE; 40 } else if(!strcmp(str, "onelevel")) { 41 return LDAP_SCOPE_ONELEVEL; 42 } else if(!strcmp(str, "subtree")) { 43 return LDAP_SCOPE_SUBTREE; 44 } else if(!strcmp(str, "children")) { 45 return LDAP_SCOPE_CHILDREN; 46 } 47 return -1; 48 } 49 50 int service_ldap_search(pblock *pb, Session *sn, Request *rq) { 51 char *resource_name = pblock_findval("resource", pb); 52 char *basedn = pblock_findval("basedn", pb); 53 char *binddn = pblock_findval("bindnd", pb); 54 char *bindpw = pblock_findval("bindpw", pb); 55 char *filter = pblock_findval("filter", pb); 56 char *empty_query_error = pblock_findval("empty_filter_error", pb); 57 char *empty_result_error = pblock_findval("empty_result_error", pb); 58 char *scope_str = pblock_findval("scope", pb); 59 char *timeout_str = pblock_findval("timeout", pb); 60 char *sizelimit_str = pblock_findval("sizelimit", pb); 61 62 int status_empty_filter = WS_SAFS_LDAP_EMPTY_FILTER_ERROR; 63 int status_empty_result = WS_SAFS_LDAP_EMPTY_RESULT_ERROR; 64 65 if(empty_query_error) { 66 int64_t status = 0; 67 util_strtoint(empty_query_error, &status); 68 if(status < 200 || status > 999) { 69 log_ereport(LOG_MISCONFIG, "ldap-search: empty_query_error parameter must be an integer between 200 and 999"); 70 return REQ_ABORTED; 71 } 72 status_empty_filter = status; 73 } 74 if(empty_result_error) { 75 int64_t status = 0; 76 util_strtoint(empty_result_error, &status); 77 if(status < 200 || status > 999) { 78 log_ereport(LOG_MISCONFIG, "ldap-search: empty_result_error parameter must be an integer between 200 and 999"); 79 return REQ_ABORTED; 80 } 81 status_empty_result = status; 82 } 83 84 // should we sent an empty response in case of an empty query/result 85 // or the standard error message? 86 WSBool empty_query_response = status_empty_filter < 300 ? TRUE : FALSE; 87 WSBool empty_result_response = status_empty_result < 300 ? TRUE : FALSE; 88 89 int scope = WS_SAFS_LDAP_DEFAULT_SCOPE; 90 if(scope_str) { 91 scope = get_ldap_scope(scope_str); 92 if(scope < 0) { 93 log_ereport(LOG_MISCONFIG, "ldap-search: unknown scope %s", scope_str); 94 return REQ_ABORTED; 95 } 96 } 97 int timeout = WS_SAFS_LDAP_DEFAULT_TIMEOUT; 98 if(timeout_str) { 99 int64_t t; 100 if(util_strtoint(timeout_str, &t)) { 101 if(t < 0 || t > WS_SAFS_LDAP_MAX_TIMEOUT) { 102 log_ereport(LOG_MISCONFIG, "ldap-search: timeout out of range"); 103 return REQ_ABORTED; 104 } 105 timeout = t; 106 } else { 107 log_ereport(LOG_MISCONFIG, "ldap-search: timeout %s is not a number", timeout_str); 108 } 109 } 110 int sizelimit = WS_SAFS_LDAP_DEFAULT_SIZELIMIT; 111 if(sizelimit_str) { 112 int64_t v; 113 if(util_strtoint(sizelimit_str, &v)) { 114 if(v > INT_MAX) { 115 log_ereport(LOG_MISCONFIG, "ldap-search: sizelimit out of range"); 116 return REQ_ABORTED; 117 } 118 sizelimit = v; 119 } else { 120 log_ereport(LOG_MISCONFIG, "ldap-search: sizelimit %s is not a number", timeout_str); 121 } 122 } 123 124 125 if(!resource_name) { 126 log_ereport(LOG_MISCONFIG, "ldap-search: missing resource parameter"); 127 return REQ_ABORTED; 128 } 129 if(!basedn) { 130 log_ereport(LOG_MISCONFIG, "ldap-search: missing basedn parameter"); 131 return REQ_ABORTED; 132 } 133 134 if(!filter) { 135 // alternatively get filter from rq->vars 136 filter = pblock_findval("ldap_filter", rq->vars); 137 log_ereport(LOG_DEBUG, "ldap-search: no filter parameter, rq.vars ldap_filter: %s", filter); 138 if(!filter) { 139 // no ldap filter 140 protocol_status(sn, rq, status_empty_filter, NULL); 141 if(empty_query_response) { 142 pblock_nvinsert("content-length", "0", rq->srvhdrs); 143 http_start_response(sn, rq); 144 } else { 145 log_ereport(LOG_FAILURE, "ldap-search: no filter specified"); 146 } 147 return REQ_PROCEED; 148 } 149 } 150 151 // get the resource 152 ResourceData *resdata = resourcepool_lookup(sn, rq, resource_name, 0); 153 if(!resdata) { 154 log_ereport(LOG_FAILURE, "ldap-search: cannot get resource %s", resource_name); 155 return REQ_ABORTED; 156 } 157 LDAP *ldap = resdata->data; 158 159 // optionally, use binddn 160 if(binddn) { 161 struct berval *server_cred; 162 if(ws_ldap_bind(ldap, binddn, bindpw ? bindpw : "", &server_cred) != LDAP_SUCCESS) { 163 log_ereport(LOG_FAILURE, "ldap-search: resource %s: cannot bind %s", resource_name, binddn); 164 resourcepool_free(sn, rq, resdata); 165 return REQ_ABORTED; 166 } 167 } 168 169 170 // search 171 LDAPMessage *result; 172 struct timeval ts; 173 ts.tv_sec = timeout; 174 ts.tv_usec = 0; 175 int r = ldap_search_ext_s( 176 ldap, 177 basedn, 178 LDAP_SCOPE_SUBTREE, 179 filter, 180 NULL, 181 0, 182 NULL, // server controls 183 NULL, // client controls 184 &ts, 185 sizelimit, // size limit 186 &result); 187 188 if(r != LDAP_SUCCESS) { 189 if(result) { 190 ldap_msgfree(result); 191 } 192 log_ereport(LOG_FAILURE, "ldap-search: ldap error: %s", ldap_err2string(r)); 193 return REQ_ABORTED; 194 } 195 196 197 // send http header 198 protocol_status(sn, rq, 200, NULL); 199 200 201 LDAPMessage *msg = ldap_first_entry(ldap, result); 202 if(!msg) { 203 protocol_status(sn, rq, status_empty_result, NULL); 204 if(empty_result_response) { 205 pblock_nvinsert("content-length", "0", rq->srvhdrs); 206 } 207 } else { 208 protocol_status(sn, rq, 200, NULL); 209 } 210 http_start_response(sn, rq); 211 212 while(msg) { 213 // dn 214 char *dn = ldap_get_dn(ldap, msg); 215 if(dn) { 216 net_printf(sn->csd, "dn: %s\n", dn); 217 ldap_memfree(dn); 218 } 219 220 // get attributes 221 BerElement *ber = NULL; 222 char *attribute = attribute=ldap_first_attribute(ldap, msg, &ber); 223 while(attribute != NULL) { 224 struct berval **values = ldap_get_values_len(ldap, msg, attribute); 225 if(values) { 226 int count = ldap_count_values_len(values); 227 for(int i=0;i<count;i++) { 228 cxstring value = cx_strn(values[i]->bv_val, values[i]->bv_len); 229 net_printf(sn->csd, "%s: %.*s\n", attribute, (int)value.length, value.ptr); 230 } 231 ldap_value_free_len(values); 232 } 233 ldap_memfree(attribute); 234 attribute = ldap_next_attribute(ldap, msg, ber); 235 } 236 if(ber) { 237 ber_free(ber, 0); 238 } 239 net_printf(sn->csd, "\n"); 240 msg = ldap_next_entry(ldap, msg); 241 } 242 ldap_msgfree(result); 243 244 245 return REQ_PROCEED; 246 } 247