1/*	$NetBSD$	*/
2
3/* root_dse.c - Provides the Root DSA-Specific Entry */
4/* OpenLDAP: pkg/ldap/servers/slapd/root_dse.c,v 1.113.2.12 2010/04/13 20:23:18 kurt Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 1999-2010 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18
19#include "portable.h"
20
21#include <stdio.h>
22
23#include <ac/string.h>
24
25#include "slap.h"
26#include <ldif.h>
27#include "lber_pvt.h"
28
29#ifdef LDAP_SLAPI
30#include "slapi/slapi.h"
31#endif
32
33static struct berval	builtin_supportedFeatures[] = {
34	BER_BVC(LDAP_FEATURE_MODIFY_INCREMENT),		/* Modify/increment */
35	BER_BVC(LDAP_FEATURE_ALL_OP_ATTRS),		/* All Op Attrs (+) */
36	BER_BVC(LDAP_FEATURE_OBJECTCLASS_ATTRS),	/* OCs in Attrs List (@class) */
37	BER_BVC(LDAP_FEATURE_ABSOLUTE_FILTERS),		/* (&) and (|) search filters */
38	BER_BVC(LDAP_FEATURE_LANGUAGE_TAG_OPTIONS),	/* Language Tag Options */
39	BER_BVC(LDAP_FEATURE_LANGUAGE_RANGE_OPTIONS),	/* Language Range Options */
40#ifdef LDAP_DEVEL
41	BER_BVC(LDAP_FEATURE_SUBORDINATE_SCOPE),	/* "children" search scope */
42#endif
43	BER_BVNULL
44};
45static struct berval	*supportedFeatures;
46
47static Entry	*usr_attr = NULL;
48
49/*
50 * allow modules to register functions that muck with the root DSE entry
51 */
52
53typedef struct entry_info_t {
54	SLAP_ENTRY_INFO_FN	func;
55	void			*arg;
56	struct entry_info_t	*next;
57} entry_info_t;
58
59static entry_info_t *extra_info;
60
61int
62entry_info_register( SLAP_ENTRY_INFO_FN func, void *arg )
63{
64	entry_info_t	*ei = ch_calloc( 1, sizeof( entry_info_t ) );
65
66	ei->func = func;
67	ei->arg = arg;
68
69	ei->next = extra_info;
70	extra_info = ei;
71
72	return 0;
73}
74
75int
76entry_info_unregister( SLAP_ENTRY_INFO_FN func, void *arg )
77{
78	entry_info_t	**eip;
79
80	for ( eip = &extra_info; *eip != NULL; eip = &(*eip)->next ) {
81		if ( (*eip)->func == func && (*eip)->arg == arg ) {
82			entry_info_t	*ei = *eip;
83
84			*eip = ei->next;
85
86			ch_free( ei );
87
88			return 0;
89		}
90	}
91
92	return -1;
93}
94
95void
96entry_info_destroy( void )
97{
98	entry_info_t	**eip;
99
100	for ( eip = &extra_info; *eip != NULL;  ) {
101		entry_info_t	*ei = *eip;
102
103		eip = &(*eip)->next;
104
105		ch_free( ei );
106	}
107}
108
109/*
110 * Allow modules to register supported features
111 */
112
113static int
114supported_feature_init( void )
115{
116	int		i;
117
118	if ( supportedFeatures != NULL ) {
119		return 0;
120	}
121
122	for ( i = 0; !BER_BVISNULL( &builtin_supportedFeatures[ i ] ); i++ )
123		;
124
125	supportedFeatures = ch_calloc( sizeof( struct berval ), i + 1 );
126	if ( supportedFeatures == NULL ) {
127		return -1;
128	}
129
130	for ( i = 0; !BER_BVISNULL( &builtin_supportedFeatures[ i ] ); i++ ) {
131		ber_dupbv( &supportedFeatures[ i ], &builtin_supportedFeatures[ i ] );
132	}
133	BER_BVZERO( &supportedFeatures[ i ] );
134
135	return 0;
136}
137
138int
139supported_feature_destroy( void )
140{
141	int		i;
142
143	if ( supportedFeatures == NULL ) {
144		return 0;
145	}
146
147	for ( i = 0; !BER_BVISNULL( &supportedFeatures[ i ] ); i++ ) {
148		ch_free( supportedFeatures[ i ].bv_val );
149	}
150
151	ch_free( supportedFeatures );
152	supportedFeatures = NULL;
153
154	return 0;
155}
156
157int
158supported_feature_load( struct berval *f )
159{
160	struct berval	*tmp;
161	int		i;
162
163	supported_feature_init();
164
165	for ( i = 0; !BER_BVISNULL( &supportedFeatures[ i ] ); i++ )
166		;
167
168	tmp = ch_realloc( supportedFeatures, sizeof( struct berval ) * ( i + 2 ) );
169	if ( tmp == NULL ) {
170		return -1;
171	}
172	supportedFeatures = tmp;
173
174	ber_dupbv( &supportedFeatures[ i ], f );
175	BER_BVZERO( &supportedFeatures[ i + 1 ] );
176
177	return 0;
178}
179
180int
181root_dse_info(
182	Connection *conn,
183	Entry **entry,
184	const char **text )
185{
186	Entry		*e;
187	struct berval val;
188#ifdef LDAP_SLAPI
189	struct berval *bv;
190#endif
191	int		i, j;
192	char ** supportedSASLMechanisms;
193	BackendDB *be;
194
195	AttributeDescription *ad_structuralObjectClass
196		= slap_schema.si_ad_structuralObjectClass;
197	AttributeDescription *ad_objectClass
198		= slap_schema.si_ad_objectClass;
199	AttributeDescription *ad_namingContexts
200		= slap_schema.si_ad_namingContexts;
201#ifdef LDAP_SLAPI
202	AttributeDescription *ad_supportedExtension
203		= slap_schema.si_ad_supportedExtension;
204#endif
205	AttributeDescription *ad_supportedLDAPVersion
206		= slap_schema.si_ad_supportedLDAPVersion;
207	AttributeDescription *ad_supportedSASLMechanisms
208		= slap_schema.si_ad_supportedSASLMechanisms;
209	AttributeDescription *ad_supportedFeatures
210		= slap_schema.si_ad_supportedFeatures;
211	AttributeDescription *ad_monitorContext
212		= slap_schema.si_ad_monitorContext;
213	AttributeDescription *ad_configContext
214		= slap_schema.si_ad_configContext;
215	AttributeDescription *ad_ref
216		= slap_schema.si_ad_ref;
217
218	e = entry_alloc();
219	if( e == NULL ) {
220		Debug( LDAP_DEBUG_ANY,
221			"root_dse_info: entry_alloc failed", 0, 0, 0 );
222		return LDAP_OTHER;
223	}
224
225	e->e_attrs = NULL;
226	e->e_name.bv_val = ch_strdup( LDAP_ROOT_DSE );
227	e->e_name.bv_len = sizeof( LDAP_ROOT_DSE )-1;
228	e->e_nname.bv_val = ch_strdup( LDAP_ROOT_DSE );
229	e->e_nname.bv_len = sizeof( LDAP_ROOT_DSE )-1;
230
231	/* the DN is an empty string so no pretty/normalization is needed */
232	assert( !e->e_name.bv_len );
233	assert( !e->e_nname.bv_len );
234
235	e->e_private = NULL;
236
237	/* FIXME: is this really needed? */
238	BER_BVSTR( &val, "top" );
239	if( attr_merge_one( e, ad_objectClass, &val, NULL ) ) {
240fail:
241		entry_free( e );
242		return LDAP_OTHER;
243	}
244
245	BER_BVSTR( &val, "OpenLDAProotDSE" );
246	if( attr_merge_one( e, ad_objectClass, &val, NULL ) ) {
247		goto fail;
248	}
249	if( attr_merge_one( e, ad_structuralObjectClass, &val, NULL ) ) {
250		goto fail;
251	}
252
253	LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
254		if ( be->be_suffix == NULL
255				|| be->be_nsuffix == NULL ) {
256			/* no suffix! */
257			continue;
258		}
259		if ( SLAP_MONITOR( be )) {
260			if( attr_merge_one( e, ad_monitorContext,
261					&be->be_suffix[0],
262					&be->be_nsuffix[0] ) )
263			{
264				goto fail;
265			}
266			continue;
267		}
268		if ( SLAP_CONFIG( be )) {
269			if( attr_merge_one( e, ad_configContext,
270					&be->be_suffix[0],
271					& be->be_nsuffix[0] ) )
272			{
273				goto fail;
274			}
275			continue;
276		}
277		if ( SLAP_GLUE_SUBORDINATE( be ) && !SLAP_GLUE_ADVERTISE( be ) ) {
278			continue;
279		}
280		for ( j = 0; be->be_suffix[j].bv_val != NULL; j++ ) {
281			if( attr_merge_one( e, ad_namingContexts,
282					&be->be_suffix[j], NULL ) )
283			{
284				goto fail;
285			}
286		}
287	}
288
289	/* altServer unsupported */
290
291	/* supportedControl */
292	if ( controls_root_dse_info( e ) != 0 ) {
293		goto fail;
294	}
295
296	/* supportedExtension */
297	if ( exop_root_dse_info( e ) != 0 ) {
298		goto fail;
299	}
300
301#ifdef LDAP_SLAPI
302	/* netscape supportedExtension */
303	for ( i = 0; (bv = slapi_int_get_supported_extop(i)) != NULL; i++ ) {
304		if( attr_merge_one( e, ad_supportedExtension, bv, NULL ) ) {
305			goto fail;
306		}
307	}
308#endif /* LDAP_SLAPI */
309
310	/* supportedFeatures */
311	if ( supportedFeatures == NULL ) {
312		supported_feature_init();
313	}
314
315	if( attr_merge( e, ad_supportedFeatures, supportedFeatures, NULL ) ) {
316		goto fail;
317	}
318
319	/* supportedLDAPVersion */
320		/* don't publish version 2 as we don't really support it
321		 * (even when configured to accept version 2 Bind requests)
322		 * and the value would never be used by true LDAPv2 (or LDAPv3)
323		 * clients.
324		 */
325	for ( i=LDAP_VERSION3; i<=LDAP_VERSION_MAX; i++ ) {
326		char buf[sizeof("255")];
327		snprintf(buf, sizeof buf, "%d", i);
328		val.bv_val = buf;
329		val.bv_len = strlen( val.bv_val );
330		if( attr_merge_one( e, ad_supportedLDAPVersion, &val, NULL ) ) {
331			goto fail;
332		}
333	}
334
335	/* supportedSASLMechanism */
336	supportedSASLMechanisms = slap_sasl_mechs( conn );
337
338	if( supportedSASLMechanisms != NULL ) {
339		for ( i=0; supportedSASLMechanisms[i] != NULL; i++ ) {
340			val.bv_val = supportedSASLMechanisms[i];
341			val.bv_len = strlen( val.bv_val );
342			if( attr_merge_one( e, ad_supportedSASLMechanisms, &val, NULL ) ) {
343				ldap_charray_free( supportedSASLMechanisms );
344				goto fail;
345			}
346		}
347		ldap_charray_free( supportedSASLMechanisms );
348	}
349
350	if ( default_referral != NULL ) {
351		if( attr_merge( e, ad_ref, default_referral, NULL /* FIXME */ ) ) {
352			goto fail;
353		}
354	}
355
356	if( usr_attr != NULL) {
357		Attribute *a;
358		for( a = usr_attr->e_attrs; a != NULL; a = a->a_next ) {
359			if( attr_merge( e, a->a_desc, a->a_vals,
360				(a->a_nvals == a->a_vals) ? NULL : a->a_nvals ) )
361			{
362				goto fail;
363			}
364		}
365	}
366
367	if ( extra_info ) {
368		entry_info_t	*ei = extra_info;
369
370		for ( ; ei; ei = ei->next ) {
371			ei->func( ei->arg, e );
372		}
373	}
374
375	*entry = e;
376	return LDAP_SUCCESS;
377}
378
379int
380root_dse_init( void )
381{
382	return 0;
383}
384
385int
386root_dse_destroy( void )
387{
388	if ( usr_attr ) {
389		entry_free( usr_attr );
390		usr_attr = NULL;
391	}
392
393	return 0;
394}
395
396/*
397 * Read the entries specified in fname and merge the attributes
398 * to the user defined rootDSE. Note thaat if we find any errors
399 * what so ever, we will discard the entire entries, print an
400 * error message and return.
401 */
402int
403root_dse_read_file( const char *fname )
404{
405	struct LDIFFP	*fp;
406	int rc = 0, lineno = 0, lmax = 0, ldifrc;
407	char	*buf = NULL;
408
409	if ( (fp = ldif_open( fname, "r" )) == NULL ) {
410		Debug( LDAP_DEBUG_ANY,
411			"root_dse_read_file: could not open rootdse attr file \"%s\" - absolute path?\n",
412			fname, 0, 0 );
413		perror( fname );
414		return EXIT_FAILURE;
415	}
416
417	usr_attr = entry_alloc();
418	if( usr_attr == NULL ) {
419		Debug( LDAP_DEBUG_ANY,
420			"root_dse_read_file: entry_alloc failed", 0, 0, 0 );
421		ldif_close( fp );
422		return LDAP_OTHER;
423	}
424	usr_attr->e_attrs = NULL;
425
426	while(( ldifrc = ldif_read_record( fp, &lineno, &buf, &lmax )) > 0 ) {
427		Entry *e = str2entry( buf );
428		Attribute *a;
429
430		if( e == NULL ) {
431			Debug( LDAP_DEBUG_ANY, "root_dse_read_file: "
432				"could not parse entry (file=\"%s\" line=%d)\n",
433				fname, lineno, 0 );
434			rc = LDAP_OTHER;
435			break;
436		}
437
438		/* make sure the DN is the empty DN */
439		if( e->e_nname.bv_len ) {
440			Debug( LDAP_DEBUG_ANY,
441				"root_dse_read_file: invalid rootDSE "
442				"- dn=\"%s\" (file=\"%s\" line=%d)\n",
443				e->e_dn, fname, lineno );
444			entry_free( e );
445			rc = LDAP_OTHER;
446			break;
447		}
448
449		/*
450		 * we found a valid entry, so walk thru all the attributes in the
451		 * entry, and add each attribute type and description to the
452		 * usr_attr entry
453		 */
454
455		for(a = e->e_attrs; a != NULL; a = a->a_next) {
456			if( attr_merge( usr_attr, a->a_desc, a->a_vals,
457				(a->a_nvals == a->a_vals) ? NULL : a->a_nvals ) )
458			{
459				rc = LDAP_OTHER;
460				break;
461			}
462		}
463
464		entry_free( e );
465		if (rc) break;
466	}
467
468	if ( ldifrc < 0 )
469		rc = LDAP_OTHER;
470
471	if (rc) {
472		entry_free( usr_attr );
473		usr_attr = NULL;
474	}
475
476	ch_free( buf );
477
478	ldif_close( fp );
479
480	Debug(LDAP_DEBUG_CONFIG, "rootDSE file=\"%s\" read.\n", fname, 0, 0);
481	return rc;
482}
483
484int
485slap_discover_feature(
486	slap_bindconf	*sb,
487	const char	*attr,
488	const char	*val )
489{
490	LDAP		*ld = NULL;
491	LDAPMessage	*res = NULL, *entry;
492	int		rc, i;
493	struct berval	bv_val,
494			**values = NULL;
495	char		*attrs[ 2 ] = { NULL, NULL };
496
497	rc = slap_client_connect( &ld, sb );
498	if ( rc != LDAP_SUCCESS ) {
499		goto done;
500	}
501
502	attrs[ 0 ] = (char *) attr;
503	rc = ldap_search_ext_s( ld, "", LDAP_SCOPE_BASE, "(objectClass=*)",
504			attrs, 0, NULL, NULL, NULL, 0, &res );
505	if ( rc != LDAP_SUCCESS ) {
506		goto done;
507	}
508
509	entry = ldap_first_entry( ld, res );
510	if ( entry == NULL ) {
511		goto done;
512	}
513
514	values = ldap_get_values_len( ld, entry, attrs[ 0 ] );
515	if ( values == NULL ) {
516		rc = LDAP_NO_SUCH_ATTRIBUTE;
517		goto done;
518	}
519
520	ber_str2bv( val, 0, 0, &bv_val );
521	for ( i = 0; values[ i ] != NULL; i++ ) {
522		if ( bvmatch( &bv_val, values[ i ] ) ) {
523			rc = LDAP_COMPARE_TRUE;
524			goto done;
525		}
526	}
527
528	rc = LDAP_COMPARE_FALSE;
529
530done:;
531	if ( values != NULL ) {
532		ldap_value_free_len( values );
533	}
534
535	if ( res != NULL ) {
536		ldap_msgfree( res );
537	}
538
539	ldap_unbind_ext( ld, NULL, NULL );
540
541	return rc;
542}
543
544