1 /*
2 * Kamailio LDAP Module
3 *
4 * Copyright (C) 2007 University of North Carolina
5 *
6 * Original author: Christian Schlatter, cs@unc.edu
7 *
8 *
9 * This file is part of Kamailio, a free SIP server.
10 *
11 * Kamailio is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version
15 *
16 * Kamailio is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 *
25 */
26
27
28 #include <unistd.h>
29 #include <stdarg.h>
30
31 #include "../../core/ut.h"
32
33 #include <ldap.h>
34
35 #include "ldap_api_fn.h"
36 #include "api.h"
37 #include "ldap_connect.h"
38 #include "ldap_escape.h"
39 #include "ld_session.h"
40
41 static LDAP *last_ldap_handle = NULL;
42 static LDAPMessage *last_ldap_result_holder = NULL;
43 static LDAPMessage *last_ldap_result = NULL;
44
45 int get_connected_ldap_session(char *_lds_name, struct ld_session **_lds);
46 int lds_search(char *_lds_name, char *_dn, int _scope, char *_filter,
47 char **_attrs, struct timeval *_search_timeout, int *_ld_result_count,
48 int *_ld_error);
49
50
load_ldap(ldap_api_t * api)51 int load_ldap(ldap_api_t *api)
52 {
53 if(api == NULL) {
54 return -1;
55 }
56
57 api->ldap_params_search = ldap_params_search;
58 api->ldap_url_search = ldap_url_search;
59 api->ldap_result_attr_vals = ldap_get_attr_vals;
60 api->ldap_value_free_len = ldap_value_free_len;
61 api->ldap_result_next = ldap_inc_result_pointer;
62 api->ldap_str2scope = ldap_str2scope;
63 api->ldap_rfc4515_escape = ldap_rfc4515_escape;
64 api->get_ldap_handle = get_ldap_handle;
65 api->get_last_ldap_result = get_last_ldap_result;
66
67 return 1;
68 }
69
get_ldap_handle(char * _lds_name,LDAP ** _ldap_handle)70 int get_ldap_handle(char *_lds_name, LDAP **_ldap_handle)
71 {
72 int rc;
73 struct ld_session *lds;
74
75 rc = get_connected_ldap_session(_lds_name, &lds);
76 if(rc == 0) {
77 *_ldap_handle = lds->handle;
78 }
79 return rc;
80 }
81
get_connected_ldap_session(char * _lds_name,struct ld_session ** _lds)82 int get_connected_ldap_session(char *_lds_name, struct ld_session **_lds)
83 {
84 /*
85 * get ld session
86 */
87 if((*_lds = get_ld_session(_lds_name)) == NULL) {
88 LM_ERR("[%s]: ldap_session not found\n", _lds_name);
89 return -1;
90 }
91
92 /* try to reconnect if ldap session handle is NULL */
93 if((*_lds)->handle == NULL) {
94 if(ldap_reconnect(_lds_name) == 0) {
95 if((*_lds = get_ld_session(_lds_name)) == NULL) {
96 LM_ERR("[%s]: ldap_session not found\n", _lds_name);
97 return -1;
98 }
99 } else {
100 if(last_ldap_result_holder != NULL) {
101 ldap_msgfree(last_ldap_result_holder);
102 last_ldap_result_holder = NULL;
103 last_ldap_result = NULL;
104 }
105 ldap_disconnect(_lds_name);
106 LM_ERR("[%s]: reconnect failed\n", _lds_name);
107 return -1;
108 }
109 }
110
111 /* free old last_ldap_result */
112 /*
113 * this is done now in lds_search
114 *
115
116 if (last_ldap_result != NULL) {
117 ldap_msgfree(last_ldap_result);
118 last_ldap_result = NULL;
119 }
120 */
121
122 return 0;
123 }
124
get_last_ldap_result(LDAP ** _last_ldap_handle,LDAPMessage ** _last_ldap_result)125 void get_last_ldap_result(
126 LDAP **_last_ldap_handle, LDAPMessage **_last_ldap_result)
127 {
128 *_last_ldap_handle = last_ldap_handle;
129 *_last_ldap_result = last_ldap_result;
130 }
131
ldap_params_search(int * _ld_result_count,char * _lds_name,char * _dn,int _scope,char ** _attrs,char * _filter,...)132 int ldap_params_search(int *_ld_result_count, char *_lds_name, char *_dn,
133 int _scope, char **_attrs, char *_filter, ...)
134 {
135 int rc;
136 static char filter_str[LDAP_MAX_FILTER_LEN];
137 char *filter_ptr = NULL;
138 va_list filter_vars;
139
140 /*
141 * check _scope
142 */
143 switch(_scope) {
144 case LDAP_SCOPE_ONELEVEL:
145 case LDAP_SCOPE_BASE:
146 case LDAP_SCOPE_SUBTREE:
147 break;
148 default:
149 LM_ERR("[%s]: invalid scope argument [%d]\n", _lds_name, _scope);
150 return -1;
151 }
152
153 if(_filter) {
154 /*
155 * vsnprintf
156 */
157 va_start(filter_vars, _filter);
158 rc = vsnprintf(
159 filter_str, (size_t)LDAP_MAX_FILTER_LEN, _filter, filter_vars);
160 va_end(filter_vars);
161
162 if(rc >= LDAP_MAX_FILTER_LEN) {
163 LM_ERR("[%s]: filter string too long (len [%d], max len [%d])\n",
164 _lds_name, rc, LDAP_MAX_FILTER_LEN);
165 return -1;
166 } else if(rc < 0) {
167 LM_ERR("vsnprintf failed\n");
168 return -1;
169 }
170 filter_ptr = filter_str;
171 }
172
173 /*
174 * ldap search
175 */
176 if(lds_search(_lds_name, _dn, _scope, filter_ptr, _attrs, NULL,
177 _ld_result_count, &rc)
178 != 0) {
179 /* try again if LDAP API ERROR */
180 if(LDAP_API_ERROR(rc) && (lds_search(_lds_name, _dn, _scope, filter_str,
181 _attrs, NULL, _ld_result_count, &rc)
182 != 0)) {
183 LM_ERR("[%s]: LDAP search (dn [%s], scope [%d],"
184 " filter [%s]) failed: %s\n",
185 _lds_name, _dn, _scope, filter_str, ldap_err2string(rc));
186 return -1;
187 }
188 }
189
190 LM_DBG("[%s]: [%d] LDAP entries found\n", _lds_name, *_ld_result_count);
191
192 return 0;
193 }
194
195
ldap_url_search(char * _ldap_url,int * _ld_result_count)196 int ldap_url_search(char *_ldap_url, int *_ld_result_count)
197 {
198 LDAPURLDesc *ludp;
199 int rc;
200
201 if(ldap_url_parse(_ldap_url, &ludp) != 0) {
202 LM_ERR("invalid LDAP URL [%s]\n", ZSW(_ldap_url));
203 if(ludp != NULL) {
204 ldap_free_urldesc(ludp);
205 }
206 return -2;
207 }
208 if(ludp->lud_host == NULL) {
209 LM_ERR("no ldap session name found in ldap URL [%s]\n", ZSW(_ldap_url));
210 return -2;
211 }
212
213
214 LM_DBG("LDAP URL parsed into session_name"
215 " [%s], base [%s], scope [%d], filter [%s]\n",
216 ZSW(ludp->lud_host), ZSW(ludp->lud_dn), ludp->lud_scope,
217 ZSW(ludp->lud_filter));
218
219 rc = ldap_params_search(_ld_result_count, ludp->lud_host, ludp->lud_dn,
220 ludp->lud_scope, ludp->lud_attrs, ludp->lud_filter);
221 ldap_free_urldesc(ludp);
222 return rc;
223 }
224
225
ldap_inc_result_pointer(void)226 int ldap_inc_result_pointer(void)
227 {
228 LDAPMessage *next_result = NULL;
229
230 /*
231 * check for last_ldap_result
232 */
233 if(last_ldap_result == NULL) {
234 LM_ERR("last_ldap_result == NULL\n");
235 return -1;
236 }
237 if(last_ldap_handle == NULL) {
238 LM_ERR("last_ldap_handle == NULL\n");
239 return -1;
240 }
241
242 /*
243 * get next LDAP result pointer
244 */
245 if((next_result = ldap_next_entry(last_ldap_handle, last_ldap_result))
246 == NULL) {
247 /* no more LDAP entries */
248 return 1;
249 }
250 last_ldap_result = next_result;
251 return 0;
252 }
253
254
ldap_get_attr_vals(str * _attr_name,struct berval *** _vals)255 int ldap_get_attr_vals(str *_attr_name, struct berval ***_vals)
256 {
257 BerElement *ber;
258 char *a;
259
260 /*
261 * check for last_ldap_result
262 */
263 if(last_ldap_result == NULL) {
264 LM_ERR("last_ldap_result == NULL\n");
265 return -1;
266 }
267 if(last_ldap_handle == NULL) {
268 LM_ERR("last_ldap_handle == NULL\n");
269 return -1;
270 }
271
272 /*
273 * search for attribute named _attr_name
274 */
275 *_vals = NULL;
276 for(a = ldap_first_attribute(last_ldap_handle, last_ldap_result, &ber);
277 a != NULL;
278 a = ldap_next_attribute(last_ldap_handle, last_ldap_result, ber)) {
279 if(strncmp(a, _attr_name->s, _attr_name->len) == 0) {
280 *_vals = ldap_get_values_len(last_ldap_handle, last_ldap_result, a);
281 ldap_memfree(a);
282 break;
283 }
284 ldap_memfree(a);
285 }
286
287 if(ber != NULL) {
288 ber_free(ber, 0);
289 }
290
291 if(*_vals != NULL) {
292 return 0;
293 } else {
294 return 1;
295 }
296 }
297
ldap_str2scope(char * scope_str)298 int ldap_str2scope(char *scope_str)
299 {
300 if(strcasecmp(scope_str, "one") == 0) {
301 return LDAP_SCOPE_ONELEVEL;
302
303 } else if(strcasecmp(scope_str, "onelevel") == 0) {
304 return LDAP_SCOPE_ONELEVEL;
305
306 } else if(strcasecmp(scope_str, "base") == 0) {
307 return LDAP_SCOPE_BASE;
308
309 } else if(strcasecmp(scope_str, "sub") == 0) {
310 return LDAP_SCOPE_SUBTREE;
311
312 } else if(strcasecmp(scope_str, "subtree") == 0) {
313 return LDAP_SCOPE_SUBTREE;
314 };
315
316 return (-1);
317 }
318 /*
319 * sets last_ldap_result and last_ldap_handle
320 */
lds_search(char * _lds_name,char * _dn,int _scope,char * _filter,char ** _attrs,struct timeval * _search_timeout,int * _ld_result_count,int * _ld_error)321 int lds_search(char *_lds_name, char *_dn, int _scope, char *_filter,
322 char **_attrs, struct timeval *_search_timeout, int *_ld_result_count,
323 int *_ld_error)
324 {
325 struct ld_session *lds;
326 #ifdef LDAP_PERF
327 struct timeval before_search = {0, 0}, after_search = {0, 0};
328 #endif
329
330 /*
331 * get ld_handle
332 */
333 if(get_connected_ldap_session(_lds_name, &lds) != 0) {
334 LM_ERR("[%s]: couldn't get ldap session\n", _lds_name);
335 return -1;
336 }
337
338 /*
339 * free last_ldap_result
340 */
341 if(last_ldap_result_holder != NULL) {
342 ldap_msgfree(last_ldap_result_holder);
343 last_ldap_result_holder = NULL;
344 last_ldap_result = NULL;
345 }
346
347
348 LM_DBG("[%s]: performing LDAP search: dn [%s],"
349 " scope [%d], filter [%s], client_timeout [%d] usecs\n",
350 _lds_name, _dn, _scope, _filter,
351 (int)(lds->client_search_timeout.tv_sec * 1000000
352 + lds->client_search_timeout.tv_usec));
353
354 #ifdef LDAP_PERF
355 gettimeofday(&before_search, NULL);
356 #endif
357
358 /*
359 * perform ldap search
360 */
361 *_ld_error = ldap_search_ext_s(lds->handle, _dn, _scope, _filter, _attrs, 0,
362 NULL, NULL, &lds->client_search_timeout, 0,
363 &last_ldap_result_holder);
364
365 #ifdef LDAP_PERF
366 gettimeofday(&after_search, NULL);
367
368 LM_INFO("[%s]: LDAP search took [%d] usecs\n", _lds_name,
369 (int)((after_search.tv_sec * 1000000 + after_search.tv_usec)
370 - (before_search.tv_sec * 1000000
371 + before_search.tv_usec)));
372 #endif
373
374 if(*_ld_error != LDAP_SUCCESS) {
375 if(last_ldap_result_holder != NULL) {
376 ldap_msgfree(last_ldap_result_holder);
377 last_ldap_result_holder = NULL;
378 }
379
380 if(LDAP_API_ERROR(*_ld_error)) {
381 ldap_disconnect(_lds_name);
382 }
383
384 LM_DBG("[%s]: ldap_search_ext_st failed: %s\n", _lds_name,
385 ldap_err2string(*_ld_error));
386 return -1;
387 }
388
389 last_ldap_handle = lds->handle;
390 *_ld_result_count =
391 ldap_count_entries(lds->handle, last_ldap_result_holder);
392 if(*_ld_result_count < 0) {
393 LM_DBG("[%s]: ldap_count_entries failed\n", _lds_name);
394 return -1;
395 }
396
397 last_ldap_result = last_ldap_result_holder;
398
399 return 0;
400 }
401