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