Wed, 27 Nov 2024 23:00:07 +0100
add TODO to use a future ucx feature
464 | 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 | ||
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
50 | int service_ldap_search(pblock *pb, Session *sn, Request *rq) { |
464 | 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); | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
55 | char *filter = pblock_findval("filter", pb); |
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
56 | char *empty_query_error = pblock_findval("empty_filter_error", pb); |
464 | 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 | ||
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
62 | int status_empty_filter = WS_SAFS_LDAP_EMPTY_FILTER_ERROR; |
464 | 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) { | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
69 | log_ereport(LOG_MISCONFIG, "ldap-search: empty_query_error parameter must be an integer between 200 and 999"); |
464 | 70 | return REQ_ABORTED; |
71 | } | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
72 | status_empty_filter = status; |
464 | 73 | } |
74 | if(empty_result_error) { | |
75 | int64_t status = 0; | |
76 | util_strtoint(empty_result_error, &status); | |
77 | if(status < 200 || status > 999) { | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
78 | log_ereport(LOG_MISCONFIG, "ldap-search: empty_result_error parameter must be an integer between 200 and 999"); |
464 | 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? | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
86 | WSBool empty_query_response = status_empty_filter < 300 ? TRUE : FALSE; |
464 | 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) { | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
93 | log_ereport(LOG_MISCONFIG, "ldap-search: unknown scope %s", scope_str); |
464 | 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) { | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
102 | log_ereport(LOG_MISCONFIG, "ldap-search: timeout out of range"); |
464 | 103 | return REQ_ABORTED; |
104 | } | |
105 | timeout = t; | |
106 | } else { | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
107 | log_ereport(LOG_MISCONFIG, "ldap-search: timeout %s is not a number", timeout_str); |
464 | 108 | } |
109 | } | |
110 | int sizelimit = WS_SAFS_LDAP_DEFAULT_SIZELIMIT; | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
111 | if(sizelimit_str) { |
464 | 112 | int64_t v; |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
113 | if(util_strtoint(sizelimit_str, &v)) { |
464 | 114 | if(v > INT_MAX) { |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
115 | log_ereport(LOG_MISCONFIG, "ldap-search: sizelimit out of range"); |
464 | 116 | return REQ_ABORTED; |
117 | } | |
118 | sizelimit = v; | |
119 | } else { | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
120 | log_ereport(LOG_MISCONFIG, "ldap-search: sizelimit %s is not a number", timeout_str); |
464 | 121 | } |
122 | } | |
123 | ||
124 | ||
125 | if(!resource_name) { | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
126 | log_ereport(LOG_MISCONFIG, "ldap-search: missing resource parameter"); |
464 | 127 | return REQ_ABORTED; |
128 | } | |
129 | if(!basedn) { | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
130 | log_ereport(LOG_MISCONFIG, "ldap-search: missing basedn parameter"); |
464 | 131 | return REQ_ABORTED; |
132 | } | |
133 | ||
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
134 | if(!filter) { |
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
135 | // alternatively get filter from rq->vars |
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
136 | filter = pblock_findval("ldap_filter", rq->vars); |
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
137 | log_ereport(LOG_DEBUG, "ldap-search: no filter parameter, rq.vars ldap_filter: %s", filter); |
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
138 | if(!filter) { |
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
139 | // no ldap filter |
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
140 | protocol_status(sn, rq, status_empty_filter, NULL); |
464 | 141 | if(empty_query_response) { |
142 | pblock_nvinsert("content-length", "0", rq->srvhdrs); | |
143 | http_start_response(sn, rq); | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
144 | } else { |
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
145 | log_ereport(LOG_FAILURE, "ldap-search: no filter specified"); |
464 | 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) { | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
154 | log_ereport(LOG_FAILURE, "ldap-search: cannot get resource %s", resource_name); |
464 | 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) { | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
163 | log_ereport(LOG_FAILURE, "ldap-search: resource %s: cannot bind %s", resource_name, binddn); |
464 | 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, | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
179 | filter, |
464 | 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 | } | |
465
d22ff46c171c
rename ldap-query to ldap-search, rename query parameter to filter
Olaf Wintermann <olaf.wintermann@gmail.com>
parents:
464
diff
changeset
|
192 | log_ereport(LOG_FAILURE, "ldap-search: ldap error: %s", ldap_err2string(r)); |
464 | 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 | } |