1 /*	$NetBSD: ldapmap.c,v 1.3 2021/08/14 16:14:58 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2000-2021 The OpenLDAP Foundation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENT:
18  * This work was initially developed by Pierangelo Masarati for
19  * inclusion in OpenLDAP Software.
20  */
21 
22 #include <portable.h>
23 
24 #define LDAP_DEPRECATED 1
25 #include "rewrite-int.h"
26 #include "rewrite-map.h"
27 
28 typedef enum {
29 	MAP_LDAP_UNKNOWN,
30 	MAP_LDAP_EVERYTIME,
31 	MAP_LDAP_NOW,
32 	MAP_LDAP_LATER
33 } bindwhen_t;
34 
35 /*
36  * LDAP map data structure
37  */
38 struct ldap_map_data {
39 	char                           *lm_url;
40 	LDAPURLDesc                    *lm_lud;
41 	int				lm_version;
42 	char                           *lm_binddn;
43 	struct berval			lm_cred;
44 
45 	bindwhen_t			lm_when;
46 
47 	LDAP                           *lm_ld;
48 
49 	int                             lm_wantdn;
50 	char				*lm_attrs[ 2 ];
51 
52 #ifdef USE_REWRITE_LDAP_PVT_THREADS
53 	ldap_pvt_thread_mutex_t         lm_mutex;
54 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
55 };
56 
57 static void
map_ldap_free(struct ldap_map_data * data)58 map_ldap_free(
59 		struct ldap_map_data *data
60 )
61 {
62 	assert( data != NULL );
63 
64 	if ( data->lm_url != NULL ) {
65 		free( data->lm_url );
66 	}
67 
68 	if ( data->lm_lud != NULL ) {
69 		ldap_free_urldesc( data->lm_lud );
70 	}
71 
72 	if ( data->lm_binddn != NULL ) {
73 		free( data->lm_binddn );
74 	}
75 
76 	if ( data->lm_cred.bv_val != NULL ) {
77 		memset( data->lm_cred.bv_val, 0, data->lm_cred.bv_len );
78 		free( data->lm_cred.bv_val );
79 		data->lm_cred.bv_val = NULL;
80 		data->lm_cred.bv_len = 0;
81 	}
82 
83 	if ( data->lm_when != MAP_LDAP_EVERYTIME && data->lm_ld != NULL ) {
84 		ldap_unbind_ext( data->lm_ld, NULL, NULL );
85 	}
86 
87 	free( data );
88 }
89 
90 static void *
map_ldap_parse(const char * fname,int lineno,int argc,char ** argv)91 map_ldap_parse(
92 		const char *fname,
93 		int lineno,
94 		int argc,
95 		char **argv
96 )
97 {
98 	struct ldap_map_data *data;
99 	char *p, *uri;
100 
101 	assert( fname != NULL );
102 	assert( argv != NULL );
103 
104 	data = calloc( sizeof( struct ldap_map_data ), 1 );
105 	if ( data == NULL ) {
106 		return NULL;
107 	}
108 
109 	if ( argc < 1 ) {
110 		Debug( LDAP_DEBUG_ANY,
111 				"[%s:%d] ldap map needs URI\n",
112 				fname, lineno );
113 		free( data );
114 		return NULL;
115 	}
116 
117 	uri = argv[ 0 ];
118 	if ( strncasecmp( uri, "uri=", STRLENOF( "uri=" ) ) == 0 ) {
119 		uri += STRLENOF( "uri=" );
120 	}
121 
122 	data->lm_url = strdup( uri );
123 	if ( data->lm_url == NULL ) {
124 		map_ldap_free( data );
125 		return NULL;
126 	}
127 
128 	if ( ldap_url_parse( uri, &data->lm_lud ) != REWRITE_SUCCESS ) {
129 		Debug( LDAP_DEBUG_ANY,
130 				"[%s:%d] illegal URI '%s'\n",
131 				fname, lineno, argv[ 0 ] );
132 		map_ldap_free( data );
133 		return NULL;
134 	}
135 
136 	/* trim everything after [host][:port] */
137 	p = strchr( data->lm_url, '/' );
138 	assert( p[ 1 ] == '/' );
139 	if ( ( p = strchr( p + 2, '/' ) ) != NULL ) {
140 		p[ 0 ] = '\0';
141 	}
142 
143 	if ( data->lm_lud->lud_attrs == NULL ) {
144 		data->lm_attrs[ 0 ] = LDAP_NO_ATTRS;
145 		data->lm_wantdn = 1;
146 
147 	} else {
148 		if ( data->lm_lud->lud_attrs[ 1 ] != NULL ) {
149 			Debug( LDAP_DEBUG_ANY,
150 				"[%s:%d] only one attribute allowed in URI\n",
151 				fname, lineno );
152 			map_ldap_free( data );
153 			return NULL;
154 		}
155 
156 		if ( strcasecmp( data->lm_lud->lud_attrs[ 0 ], "dn" ) == 0
157 			|| strcasecmp( data->lm_lud->lud_attrs[ 0 ], "entryDN" ) == 0 )
158 		{
159 			ldap_memfree( data->lm_lud->lud_attrs[ 0 ] );
160 			ldap_memfree( data->lm_lud->lud_attrs );
161 			data->lm_lud->lud_attrs = NULL;
162 			data->lm_attrs[ 0 ] = LDAP_NO_ATTRS;
163 			data->lm_wantdn = 1;
164 
165 		} else {
166 			data->lm_attrs[ 0 ] = data->lm_lud->lud_attrs[ 0 ];
167 		}
168 	}
169 
170 	data->lm_attrs[ 1 ] = NULL;
171 
172 	/* safe defaults */
173 	data->lm_version = LDAP_VERSION3;
174 
175 	for ( argc--, argv++; argc > 0; argc--, argv++ ) {
176 		if ( strncasecmp( argv[ 0 ], "binddn=", STRLENOF( "binddn=" ) ) == 0 ) {
177 			char *p = argv[ 0 ] + STRLENOF( "binddn=" );
178 			int l;
179 
180 			if ( p[ 0 ] == '\"' || p [ 0 ] == '\'' ) {
181 				l = strlen( p ) - 2;
182 				p++;
183 				if ( p[ l ] != p[ 0 ] ) {
184 					map_ldap_free( data );
185 					return NULL;
186 				}
187 			} else {
188 				l = strlen( p );
189 			}
190 
191 			data->lm_binddn = strdup( p );
192 			if ( data->lm_binddn == NULL ) {
193 				map_ldap_free( data );
194 				return NULL;
195 			}
196 
197 			if ( data->lm_binddn[ l ] == '\"'
198 					|| data->lm_binddn[ l ] == '\'' ) {
199 				data->lm_binddn[ l ] = '\0';
200 			}
201 
202 			/* deprecated */
203 		} else if ( strncasecmp( argv[ 0 ], "bindpw=", STRLENOF( "bindpw=" ) ) == 0 ) {
204 			ber_str2bv( argv[ 0 ] + STRLENOF( "bindpw=" ), 0, 1, &data->lm_cred );
205 			if ( data->lm_cred.bv_val == NULL ) {
206 				map_ldap_free( data );
207 				return NULL;
208 			}
209 
210 		} else if ( strncasecmp( argv[ 0 ], "credentials=", STRLENOF( "credentials=" ) ) == 0 ) {
211 			ber_str2bv( argv[ 0 ] + STRLENOF( "credentials=" ), 0, 1, &data->lm_cred );
212 			if ( data->lm_cred.bv_val == NULL ) {
213 				map_ldap_free( data );
214 				return NULL;
215 			}
216 
217 		} else if ( strncasecmp( argv[ 0 ], "bindwhen=", STRLENOF( "bindwhen=" ) ) == 0 ) {
218 			char *p = argv[ 0 ] + STRLENOF( "bindwhen=" );
219 
220 			if ( strcasecmp( p, "now" ) == 0 ) {
221 				int rc;
222 
223 				data->lm_when = MAP_LDAP_NOW;
224 
225 				/*
226 				 * Init LDAP handler ...
227 				 */
228 				rc = ldap_initialize( &data->lm_ld, data->lm_url );
229 				if ( rc != LDAP_SUCCESS ) {
230 					map_ldap_free( data );
231 					return NULL;
232 				}
233 
234 				ldap_set_option( data->lm_ld,
235 					LDAP_OPT_PROTOCOL_VERSION,
236 					(void *)&data->lm_version );
237 
238 #ifdef USE_REWRITE_LDAP_PVT_THREADS
239 				ldap_pvt_thread_mutex_init( &data->lm_mutex );
240 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
241 
242 			} else if ( strcasecmp( p, "later" ) == 0 ) {
243 				data->lm_when = MAP_LDAP_LATER;
244 
245 #ifdef USE_REWRITE_LDAP_PVT_THREADS
246 				ldap_pvt_thread_mutex_init( &data->lm_mutex );
247 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
248 
249 			} else if ( strcasecmp( p, "everytime" ) == 0 ) {
250 				data->lm_when = MAP_LDAP_EVERYTIME;
251 			} else {
252 				/* ignore ... */
253 			}
254 
255 		} else if ( strncasecmp( argv[ 0 ], "version=", STRLENOF( "version=" ) ) == 0 ) {
256 			if ( lutil_atoi( &data->lm_version, argv[ 0 ] + STRLENOF( "version=" ) ) ) {
257 				map_ldap_free( data );
258 				return NULL;
259 			}
260 
261 			switch ( data->lm_version ) {
262 			case LDAP_VERSION2:
263 			case LDAP_VERSION3:
264 				break;
265 
266 			default:
267 				Debug( LDAP_DEBUG_ANY,
268 					"[%s:%d] unknown version %s\n",
269 					fname, lineno, p );
270 				map_ldap_free( data );
271 				return NULL;
272 			}
273 
274 		} else {
275 			Debug( LDAP_DEBUG_ANY,
276 				"[%s:%d] unknown option %s (ignored)\n",
277 				fname, lineno, argv[0] );
278 		}
279 	}
280 
281 	if ( data->lm_when == MAP_LDAP_UNKNOWN ) {
282 		data->lm_when = MAP_LDAP_EVERYTIME;
283 	}
284 
285 	return ( void * )data;
286 }
287 
288 static int
map_ldap_apply(void * private,const char * filter,struct berval * val)289 map_ldap_apply(
290 		void *private,
291 		const char *filter,
292 		struct berval *val
293 
294 )
295 {
296 	LDAP *ld;
297 	LDAPMessage *res = NULL, *entry;
298 	int rc;
299 	struct ldap_map_data *data = private;
300 	LDAPURLDesc *lud = data->lm_lud;
301 
302 	int first_try = 1, set_version = 0;
303 
304 	assert( private != NULL );
305 	assert( filter != NULL );
306 	assert( val != NULL );
307 
308 	val->bv_val = NULL;
309 	val->bv_len = 0;
310 
311 	if ( data->lm_when == MAP_LDAP_EVERYTIME ) {
312 		rc = ldap_initialize( &ld, data->lm_url );
313 		set_version = 1;
314 
315 	} else {
316 #ifdef USE_REWRITE_LDAP_PVT_THREADS
317 		ldap_pvt_thread_mutex_lock( &data->lm_mutex );
318 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
319 
320 		rc = LDAP_SUCCESS;
321 
322 		if ( data->lm_when == MAP_LDAP_LATER && data->lm_ld == NULL ) {
323 			rc = ldap_initialize( &data->lm_ld, data->lm_url );
324 			set_version = 1;
325 		}
326 
327 		ld = data->lm_ld;
328 	}
329 
330 	if ( rc != LDAP_SUCCESS ) {
331 		rc = REWRITE_ERR;
332 		goto rc_return;
333 	}
334 
335 do_bind:;
336 	if ( set_version ) {
337 		ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
338 			(void *)&data->lm_version );
339 		set_version = 0;
340 	}
341 
342 	if ( data->lm_binddn != NULL ) {
343 		rc = ldap_sasl_bind_s( ld, data->lm_binddn,
344 			LDAP_SASL_SIMPLE, &data->lm_cred,
345 			NULL, NULL, NULL );
346 		if ( rc == LDAP_SERVER_DOWN && first_try ) {
347 			first_try = 0;
348 			if ( ldap_initialize( &ld, data->lm_url ) != LDAP_SUCCESS ) {
349 				rc = REWRITE_ERR;
350 				goto rc_return;
351 			}
352 			set_version = 1;
353 			goto do_bind;
354 
355 		} else if ( rc != REWRITE_SUCCESS ) {
356 			rc = REWRITE_ERR;
357 			goto rc_return;
358 		}
359 	}
360 
361 	rc = ldap_search_ext_s( ld, lud->lud_dn, lud->lud_scope, ( char * )filter,
362 			data->lm_attrs, 0, NULL, NULL, NULL, 1, &res );
363 	if ( rc == LDAP_SERVER_DOWN && first_try ) {
364 		first_try = 0;
365                 if ( ldap_initialize( &ld, data->lm_url ) != LDAP_SUCCESS ) {
366 			rc = REWRITE_ERR;
367 			goto rc_return;
368 		}
369 		set_version = 1;
370 		goto do_bind;
371 
372 	} else if ( rc != LDAP_SUCCESS ) {
373 		rc = REWRITE_ERR;
374 		goto rc_return;
375 	}
376 
377 	if ( ldap_count_entries( ld, res ) != 1 ) {
378 		ldap_msgfree( res );
379 		rc = REWRITE_ERR;
380 		goto rc_return;
381 	}
382 
383 	entry = ldap_first_entry( ld, res );
384 	assert( entry != NULL );
385 
386 	if ( data->lm_wantdn == 1 ) {
387 		/*
388 		 * dn is newly allocated, so there's no need to strdup it
389 		 */
390 		val->bv_val = ldap_get_dn( ld, entry );
391 		val->bv_len = strlen( val->bv_val );
392 
393 	} else {
394 		struct berval **values;
395 
396 		values = ldap_get_values_len( ld, entry, data->lm_attrs[ 0 ] );
397 		if ( values != NULL ) {
398 			if ( values[ 0 ] != NULL && values[ 0 ]->bv_val != NULL ) {
399 #if 0
400 				/* NOTE: in principle, multiple values
401 				 * should not be acceptable according
402 				 * to the current API; ignore by now */
403 				if ( values[ 1 ] != NULL ) {
404 					/* error */
405 				}
406 #endif
407 				ber_dupbv( val, values[ 0 ] );
408 			}
409 			ldap_value_free_len( values );
410 		}
411 	}
412 
413 	ldap_msgfree( res );
414 
415 	if ( val->bv_val == NULL ) {
416 		rc = REWRITE_ERR;
417 		goto rc_return;
418 	}
419 
420 rc_return:;
421 	if ( data->lm_when == MAP_LDAP_EVERYTIME ) {
422 		if ( ld != NULL ) {
423 			ldap_unbind_ext( ld, NULL, NULL );
424 		}
425 
426 	} else {
427 		data->lm_ld = ld;
428 #ifdef USE_REWRITE_LDAP_PVT_THREADS
429 		ldap_pvt_thread_mutex_unlock( &data->lm_mutex );
430 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
431 	}
432 
433 	return rc;
434 }
435 
436 static int
map_ldap_destroy(void * private)437 map_ldap_destroy(
438 		void *private
439 )
440 {
441 	struct ldap_map_data *data = private;
442 
443 	assert( private != NULL );
444 
445 	map_ldap_free( data );
446 
447 	return 0;
448 }
449 
450 const rewrite_mapper rewrite_ldap_mapper = {
451 	"ldap",
452 	map_ldap_parse,
453 	map_ldap_apply,
454 	map_ldap_destroy
455 };
456 
457