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