1/*	$NetBSD: config.c,v 1.3 2021/08/14 16:15:01 christos Exp $	*/
2
3/* $OpenLDAP$ */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1999-2021 The OpenLDAP Foundation.
7 * Portions Copyright 1999 Dmitry Kovalev.
8 * Portions Copyright 2002 Pierangelo Masarati.
9 * Portions Copyright 2004 Mark Adamson.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted only as authorized by the OpenLDAP
14 * Public License.
15 *
16 * A copy of this license is available in the file LICENSE in the
17 * top-level directory of the distribution or, alternatively, at
18 * <http://www.OpenLDAP.org/license.html>.
19 */
20/* ACKNOWLEDGEMENTS:
21 * This work was initially developed by Dmitry Kovalev for inclusion
22 * by OpenLDAP Software.  Additional significant contributors include
23 * Pierangelo Masarati.
24 */
25
26#include <sys/cdefs.h>
27__RCSID("$NetBSD: config.c,v 1.3 2021/08/14 16:15:01 christos Exp $");
28
29#include "portable.h"
30
31#include <stdio.h>
32#include "ac/string.h"
33#include <sys/types.h>
34
35#include "slap.h"
36#include "slap-config.h"
37#include "ldif.h"
38#include "lutil.h"
39#include "proto-sql.h"
40
41static int
42create_baseObject(
43	BackendDB	*be,
44	const char	*fname,
45	int		lineno );
46
47static int
48read_baseObject(
49	BackendDB	*be,
50	const char	*fname );
51
52static ConfigDriver sql_cf_gen;
53
54enum {
55	BSQL_CONCAT_PATT = 1,
56	BSQL_CREATE_NEEDS_SEL,
57	BSQL_UPPER_NEEDS_CAST,
58	BSQL_HAS_LDAPINFO_DN_RU,
59	BSQL_FAIL_IF_NO_MAPPING,
60	BSQL_ALLOW_ORPHANS,
61	BSQL_BASE_OBJECT,
62	BSQL_LAYER,
63	BSQL_SUBTREE_SHORTCUT,
64	BSQL_FETCH_ALL_ATTRS,
65	BSQL_FETCH_ATTRS,
66	BSQL_CHECK_SCHEMA,
67	BSQL_ALIASING_KEYWORD,
68	BSQL_AUTOCOMMIT
69};
70
71static ConfigTable sqlcfg[] = {
72	{ "dbhost", "hostname", 2, 2, 0, ARG_STRING|ARG_OFFSET,
73		(void *)offsetof(struct backsql_info, sql_dbhost),
74		"( OLcfgDbAt:6.1 NAME 'olcDbHost' "
75			"DESC 'Hostname of SQL server' "
76			"EQUALITY caseExactMatch "
77			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
78	{ "dbname", "name", 2, 2, 0, ARG_STRING|ARG_OFFSET,
79		(void *)offsetof(struct backsql_info, sql_dbname),
80		"( OLcfgDbAt:6.2 NAME 'olcDbName' "
81			"DESC 'Name of SQL database' "
82			"EQUALITY caseExactMatch "
83			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
84	{ "dbuser", "username", 2, 2, 0, ARG_STRING|ARG_OFFSET,
85		(void *)offsetof(struct backsql_info, sql_dbuser),
86		"( OLcfgDbAt:6.3 NAME 'olcDbUser' "
87			"DESC 'Username for SQL session' "
88			"EQUALITY caseExactMatch "
89			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
90	{ "dbpasswd", "password", 2, 2, 0, ARG_STRING|ARG_OFFSET,
91		(void *)offsetof(struct backsql_info, sql_dbpasswd),
92		"( OLcfgDbAt:6.4 NAME 'olcDbPass' "
93			"DESC 'Password for SQL session' "
94			"EQUALITY caseExactMatch "
95			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
96	{ "concat_pattern", "pattern", 2, 2, 0,
97		ARG_STRING|ARG_MAGIC|BSQL_CONCAT_PATT, (void *)sql_cf_gen,
98		"( OLcfgDbAt:6.20 NAME 'olcSqlConcatPattern' "
99			"DESC 'Pattern used to concatenate strings' "
100			"EQUALITY caseExactMatch "
101			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
102	{ "subtree_cond", "SQL expression", 2, 0, 0, ARG_BERVAL|ARG_QUOTE|ARG_OFFSET,
103		(void *)offsetof(struct backsql_info, sql_subtree_cond),
104		"( OLcfgDbAt:6.21 NAME 'olcSqlSubtreeCond' "
105			"DESC 'Where-clause template for a subtree search condition' "
106			"EQUALITY caseExactMatch "
107			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
108	{ "children_cond", "SQL expression", 2, 0, 0, ARG_BERVAL|ARG_QUOTE|ARG_OFFSET,
109		(void *)offsetof(struct backsql_info, sql_children_cond),
110		"( OLcfgDbAt:6.22 NAME 'olcSqlChildrenCond' "
111			"DESC 'Where-clause template for a children search condition' "
112			"EQUALITY caseExactMatch "
113			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
114	{ "dn_match_cond", "SQL expression", 2, 0, 0, ARG_BERVAL|ARG_QUOTE|ARG_OFFSET,
115		(void *)offsetof(struct backsql_info, sql_dn_match_cond),
116		"( OLcfgDbAt:6.23 NAME 'olcSqlDnMatchCond' "
117			"DESC 'Where-clause template for a DN match search condition' "
118			"EQUALITY caseExactMatch "
119			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
120	{ "oc_query", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
121		(void *)offsetof(struct backsql_info, sql_oc_query),
122		"( OLcfgDbAt:6.24 NAME 'olcSqlOcQuery' "
123			"DESC 'Query used to collect objectClass mapping data' "
124			"EQUALITY caseExactMatch "
125			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
126	{ "at_query", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
127		(void *)offsetof(struct backsql_info, sql_at_query),
128		"( OLcfgDbAt:6.25 NAME 'olcSqlAtQuery' "
129			"DESC 'Query used to collect attributeType mapping data' "
130			"EQUALITY caseExactMatch "
131			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
132	{ "insentry_stmt", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
133		(void *)offsetof(struct backsql_info, sql_insentry_stmt),
134		"( OLcfgDbAt:6.26 NAME 'olcSqlInsEntryStmt' "
135			"DESC 'Statement used to insert a new entry' "
136			"EQUALITY caseExactMatch "
137			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
138	{ "create_needs_select", "yes|no", 2, 2, 0,
139		ARG_ON_OFF|ARG_MAGIC|BSQL_CREATE_NEEDS_SEL, (void *)sql_cf_gen,
140		"( OLcfgDbAt:6.27 NAME 'olcSqlCreateNeedsSelect' "
141			"DESC 'Whether entry creation needs a subsequent select' "
142			"EQUALITY booleanMatch "
143			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
144	{ "upper_func", "SQL function name", 2, 2, 0, ARG_BERVAL|ARG_OFFSET,
145		(void *)offsetof(struct backsql_info, sql_upper_func),
146		"( OLcfgDbAt:6.28 NAME 'olcSqlUpperFunc' "
147			"DESC 'Function that converts a value to uppercase' "
148			"EQUALITY caseExactMatch "
149			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
150	{ "upper_needs_cast", "yes|no", 2, 2, 0,
151		ARG_ON_OFF|ARG_MAGIC|BSQL_UPPER_NEEDS_CAST, (void *)sql_cf_gen,
152		"( OLcfgDbAt:6.29 NAME 'olcSqlUpperNeedsCast' "
153			"DESC 'Whether olcSqlUpperFunc needs an explicit cast' "
154			"EQUALITY booleanMatch "
155			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
156	{ "strcast_func", "SQL function name", 2, 2, 0, ARG_BERVAL|ARG_OFFSET,
157		(void *)offsetof(struct backsql_info, sql_strcast_func),
158		"( OLcfgDbAt:6.30 NAME 'olcSqlStrcastFunc' "
159			"DESC 'Function that converts a value to a string' "
160			"EQUALITY caseExactMatch "
161			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
162	{ "delentry_stmt", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
163		(void *)offsetof(struct backsql_info, sql_delentry_stmt),
164		"( OLcfgDbAt:6.31 NAME 'olcSqlDelEntryStmt' "
165			"DESC 'Statement used to delete an existing entry' "
166			"EQUALITY caseExactMatch "
167			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
168	{ "renentry_stmt", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
169		(void *)offsetof(struct backsql_info, sql_renentry_stmt),
170		"( OLcfgDbAt:6.32 NAME 'olcSqlRenEntryStmt' "
171			"DESC 'Statement used to rename an entry' "
172			"EQUALITY caseExactMatch "
173			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
174	{ "delobjclasses_stmt", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
175		(void *)offsetof(struct backsql_info, sql_delobjclasses_stmt),
176		"( OLcfgDbAt:6.33 NAME 'olcSqlDelObjclassesStmt' "
177			"DESC 'Statement used to delete the ID of an entry' "
178			"EQUALITY caseExactMatch "
179			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
180	{ "has_ldapinfo_dn_ru", "yes|no", 2, 2, 0,
181		ARG_ON_OFF|ARG_MAGIC|BSQL_HAS_LDAPINFO_DN_RU, (void *)sql_cf_gen,
182		"( OLcfgDbAt:6.34 NAME 'olcSqlHasLDAPinfoDnRu' "
183			"DESC 'Whether the dn_ru column is present' "
184			"EQUALITY booleanMatch "
185			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
186	{ "fail_if_no_mapping", "yes|no", 2, 2, 0,
187		ARG_ON_OFF|ARG_MAGIC|BSQL_FAIL_IF_NO_MAPPING, (void *)sql_cf_gen,
188		"( OLcfgDbAt:6.35 NAME 'olcSqlFailIfNoMapping' "
189			"DESC 'Whether to fail on unknown attribute mappings' "
190			"EQUALITY booleanMatch "
191			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
192	{ "allow_orphans", "yes|no", 2, 2, 0,
193		ARG_ON_OFF|ARG_MAGIC|BSQL_ALLOW_ORPHANS, (void *)sql_cf_gen,
194		"( OLcfgDbAt:6.36 NAME 'olcSqlAllowOrphans' "
195			"DESC 'Whether to allow adding entries with no parent' "
196			"EQUALITY booleanMatch "
197			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
198	{ "baseobject", "[file]", 1, 2, 0,
199		ARG_STRING|ARG_MAGIC|BSQL_BASE_OBJECT, (void *)sql_cf_gen,
200		"( OLcfgDbAt:6.37 NAME 'olcSqlBaseObject' "
201			"DESC 'Manage an in-memory baseObject entry' "
202			"EQUALITY caseExactMatch "
203			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
204	{ "sqllayer", "name", 2, 0, 0,
205		ARG_MAGIC|BSQL_LAYER, (void *)sql_cf_gen,
206		"( OLcfgDbAt:6.38 NAME 'olcSqlLayer' "
207			"DESC 'Helper used to map DNs between LDAP and SQL' "
208			"EQUALITY caseExactMatch "
209			"SYNTAX OMsDirectoryString )", NULL, NULL },
210	{ "use_subtree_shortcut", "yes|no", 2, 2, 0,
211		ARG_ON_OFF|ARG_MAGIC|BSQL_SUBTREE_SHORTCUT, (void *)sql_cf_gen,
212		"( OLcfgDbAt:6.39 NAME 'olcSqlUseSubtreeShortcut' "
213			"DESC 'Collect all entries when searchBase is DB suffix' "
214			"EQUALITY booleanMatch "
215			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
216	{ "fetch_all_attrs", "yes|no", 2, 2, 0,
217		ARG_ON_OFF|ARG_MAGIC|BSQL_FETCH_ALL_ATTRS, (void *)sql_cf_gen,
218		"( OLcfgDbAt:6.40 NAME 'olcSqlFetchAllAttrs' "
219			"DESC 'Require all attributes to always be loaded' "
220			"EQUALITY booleanMatch "
221			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
222	{ "fetch_attrs", "attrlist", 2, 0, 0,
223		ARG_MAGIC|BSQL_FETCH_ATTRS, (void *)sql_cf_gen,
224		"( OLcfgDbAt:6.41 NAME 'olcSqlFetchAttrs' "
225			"DESC 'Set of attributes to always fetch' "
226			"EQUALITY caseIgnoreMatch "
227			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
228	{ "check_schema", "yes|no", 2, 2, 0,
229		ARG_ON_OFF|ARG_MAGIC|BSQL_CHECK_SCHEMA, (void *)sql_cf_gen,
230		"( OLcfgDbAt:6.42 NAME 'olcSqlCheckSchema' "
231			"DESC 'Check schema after modifications' "
232			"EQUALITY booleanMatch "
233			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
234	{ "aliasing_keyword", "string", 2, 2, 0,
235		ARG_STRING|ARG_MAGIC|BSQL_ALIASING_KEYWORD, (void *)sql_cf_gen,
236		"( OLcfgDbAt:6.43 NAME 'olcSqlAliasingKeyword' "
237			"DESC 'The aliasing keyword' "
238			"EQUALITY caseExactMatch "
239			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
240	{ "aliasing_quote", "string", 2, 2, 0, ARG_BERVAL|ARG_OFFSET,
241		(void *)offsetof(struct backsql_info, sql_aliasing_quote),
242		"( OLcfgDbAt:6.44 NAME 'olcSqlAliasingQuote' "
243			"DESC 'Quoting char of the aliasing keyword' "
244			"EQUALITY caseIgnoreMatch "
245			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
246	{ "autocommit", "yes|no", 2, 2, 0,
247		ARG_ON_OFF|ARG_MAGIC|BSQL_AUTOCOMMIT, (void *)sql_cf_gen,
248		"( OLcfgDbAt:6.45 NAME 'olcSqlAutocommit' "
249			"EQUALITY booleanMatch "
250			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
251	{ "id_query", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
252		(void *)offsetof(struct backsql_info, sql_id_query),
253		"( OLcfgDbAt:6.46 NAME 'olcSqlIdQuery' "
254			"DESC 'Query used to collect entryID mapping data' "
255			"EQUALITY caseExactMatch "
256			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
257	{ NULL, NULL, 0, 0, 0, ARG_IGNORED,
258		NULL, NULL, NULL, NULL }
259};
260
261static ConfigOCs sqlocs[] = {
262	{
263		"( OLcfgDbOc:6.1 "
264		"NAME 'olcSqlConfig' "
265		"DESC 'SQL backend configuration' "
266		"SUP olcDatabaseConfig "
267		"MUST olcDbName "
268		"MAY ( olcDbHost $ olcDbUser $ olcDbPass $ olcSqlConcatPattern $ "
269		"olcSqlSubtreeCond $ olcsqlChildrenCond $ olcSqlDnMatchCond $ "
270		"olcSqlOcQuery $ olcSqlAtQuery $ olcSqlInsEntryStmt $ "
271		"olcSqlCreateNeedsSelect $ olcSqlUpperFunc $ olcSqlUpperNeedsCast $ "
272		"olcSqlStrCastFunc $ olcSqlDelEntryStmt $ olcSqlRenEntryStmt $ "
273		"olcSqlDelObjClassesStmt $ olcSqlHasLDAPInfoDnRu $ "
274		"olcSqlFailIfNoMapping $ olcSqlAllowOrphans $ olcSqlBaseObject $ "
275		"olcSqlLayer $ olcSqlUseSubtreeShortcut $ olcSqlFetchAllAttrs $ "
276		"olcSqlFetchAttrs $ olcSqlCheckSchema $ olcSqlAliasingKeyword $ "
277		"olcSqlAliasingQuote $ olcSqlAutocommit $ olcSqlIdQuery ) )",
278			Cft_Database, sqlcfg },
279	{ NULL, Cft_Abstract, NULL }
280};
281
282static int
283sql_cf_gen( ConfigArgs *c )
284{
285	backsql_info 	*bi = (backsql_info *)c->be->be_private;
286	int rc = 0;
287
288	if ( c->op == SLAP_CONFIG_EMIT ) {
289		switch( c->type ) {
290		case BSQL_CONCAT_PATT:
291			if ( bi->sql_concat_patt ) {
292				c->value_string = ch_strdup( bi->sql_concat_patt );
293			} else {
294				rc = 1;
295			}
296			break;
297		case BSQL_CREATE_NEEDS_SEL:
298			if ( bi->sql_flags & BSQLF_CREATE_NEEDS_SELECT )
299				c->value_int = 1;
300			break;
301		case BSQL_UPPER_NEEDS_CAST:
302			if ( bi->sql_flags & BSQLF_UPPER_NEEDS_CAST )
303				c->value_int = 1;
304			break;
305		case BSQL_HAS_LDAPINFO_DN_RU:
306			if ( !(bi->sql_flags & BSQLF_DONTCHECK_LDAPINFO_DN_RU) )
307				return 1;
308			if ( bi->sql_flags & BSQLF_HAS_LDAPINFO_DN_RU )
309				c->value_int = 1;
310			break;
311		case BSQL_FAIL_IF_NO_MAPPING:
312			if ( bi->sql_flags & BSQLF_FAIL_IF_NO_MAPPING )
313				c->value_int = 1;
314			break;
315		case BSQL_ALLOW_ORPHANS:
316			if ( bi->sql_flags & BSQLF_ALLOW_ORPHANS )
317				c->value_int = 1;
318			break;
319		case BSQL_SUBTREE_SHORTCUT:
320			if ( bi->sql_flags & BSQLF_USE_SUBTREE_SHORTCUT )
321				c->value_int = 1;
322			break;
323		case BSQL_FETCH_ALL_ATTRS:
324			if ( bi->sql_flags & BSQLF_FETCH_ALL_ATTRS )
325				c->value_int = 1;
326			break;
327		case BSQL_CHECK_SCHEMA:
328			if ( bi->sql_flags & BSQLF_CHECK_SCHEMA )
329				c->value_int = 1;
330			break;
331		case BSQL_AUTOCOMMIT:
332			if ( bi->sql_flags & BSQLF_AUTOCOMMIT_ON )
333				c->value_int = 1;
334			break;
335		case BSQL_BASE_OBJECT:
336			if ( bi->sql_base_ob_file ) {
337				c->value_string = ch_strdup( bi->sql_base_ob_file );
338			} else if ( bi->sql_baseObject ) {
339				c->value_string = ch_strdup( "TRUE" );
340			} else {
341				rc = 1;
342			}
343			break;
344		case BSQL_LAYER:
345			if ( bi->sql_api ) {
346				backsql_api *ba;
347				struct berval bv;
348				char *ptr;
349				int i;
350				for ( ba = bi->sql_api; ba; ba = ba->ba_next ) {
351					bv.bv_len = strlen( ba->ba_name );
352					if ( ba->ba_argc ) {
353						for ( i = 0; i<ba->ba_argc; i++ )
354							bv.bv_len += strlen( ba->ba_argv[i] ) + 3;
355					}
356					bv.bv_val = ch_malloc( bv.bv_len + 1 );
357					ptr = lutil_strcopy( bv.bv_val, ba->ba_name );
358					if ( ba->ba_argc ) {
359						for ( i = 0; i<ba->ba_argc; i++ ) {
360							*ptr++ = ' ';
361							*ptr++ = '"';
362							ptr = lutil_strcopy( ptr, ba->ba_argv[i] );
363							*ptr++ = '"';
364						}
365					}
366					ber_bvarray_add( &c->rvalue_vals, &bv );
367				}
368			} else {
369				rc = 1;
370			}
371			break;
372		case BSQL_ALIASING_KEYWORD:
373			if ( !BER_BVISNULL( &bi->sql_aliasing )) {
374				struct berval bv;
375				bv = bi->sql_aliasing;
376				bv.bv_len--;
377				value_add_one( &c->rvalue_vals, &bv );
378			} else {
379				rc = 1;
380			}
381			break;
382		case BSQL_FETCH_ATTRS:
383			if ( bi->sql_anlist ||
384				( bi->sql_flags & (BSQLF_FETCH_ALL_USERATTRS|
385								   BSQLF_FETCH_ALL_OPATTRS)))
386			{
387				char buf[BUFSIZ*2], *ptr;
388				struct berval bv;
389#   define WHATSLEFT    ((ber_len_t) (&buf[sizeof( buf )] - ptr))
390				ptr = buf;
391				if ( bi->sql_anlist ) {
392					ptr = anlist_unparse( bi->sql_anlist, ptr, WHATSLEFT );
393					if ( ptr == NULL )
394						return 1;
395				}
396				if ( bi->sql_flags & BSQLF_FETCH_ALL_USERATTRS ) {
397					if ( WHATSLEFT <= STRLENOF( ",*" )) return 1;
398					if ( ptr != buf ) *ptr++ = ',';
399					*ptr++ = '*';
400				}
401				if ( bi->sql_flags & BSQLF_FETCH_ALL_OPATTRS ) {
402					if ( WHATSLEFT <= STRLENOF( ",+" )) return 1;
403					if ( ptr != buf ) *ptr++ = ',';
404					*ptr++ = '+';
405				}
406				bv.bv_val = buf;
407				bv.bv_len = ptr - buf;
408				value_add_one( &c->rvalue_vals, &bv );
409			}
410			break;
411		}
412		return rc;
413	} else if ( c->op == LDAP_MOD_DELETE ) {	/* FIXME */
414		return -1;
415	}
416
417	switch( c->type ) {
418	case BSQL_CONCAT_PATT:
419		if ( backsql_split_pattern( c->argv[ 1 ], &bi->sql_concat_func, 2 ) ) {
420			snprintf( c->cr_msg, sizeof( c->cr_msg ),
421				"%s: unable to parse pattern \"%s\"",
422				c->log, c->argv[ 1 ] );
423			Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg );
424			return -1;
425		}
426		bi->sql_concat_patt = c->value_string;
427		break;
428	case BSQL_CREATE_NEEDS_SEL:
429		if ( c->value_int )
430			bi->sql_flags |= BSQLF_CREATE_NEEDS_SELECT;
431		else
432			bi->sql_flags &= ~BSQLF_CREATE_NEEDS_SELECT;
433		break;
434	case BSQL_UPPER_NEEDS_CAST:
435		if ( c->value_int )
436			bi->sql_flags |= BSQLF_UPPER_NEEDS_CAST;
437		else
438			bi->sql_flags &= ~BSQLF_UPPER_NEEDS_CAST;
439		break;
440	case BSQL_HAS_LDAPINFO_DN_RU:
441		bi->sql_flags |= BSQLF_DONTCHECK_LDAPINFO_DN_RU;
442		if ( c->value_int )
443			bi->sql_flags |= BSQLF_HAS_LDAPINFO_DN_RU;
444		else
445			bi->sql_flags &= ~BSQLF_HAS_LDAPINFO_DN_RU;
446		break;
447	case BSQL_FAIL_IF_NO_MAPPING:
448		if ( c->value_int )
449			bi->sql_flags |= BSQLF_FAIL_IF_NO_MAPPING;
450		else
451			bi->sql_flags &= ~BSQLF_FAIL_IF_NO_MAPPING;
452		break;
453	case BSQL_ALLOW_ORPHANS:
454		if ( c->value_int )
455			bi->sql_flags |= BSQLF_ALLOW_ORPHANS;
456		else
457			bi->sql_flags &= ~BSQLF_ALLOW_ORPHANS;
458		break;
459	case BSQL_SUBTREE_SHORTCUT:
460		if ( c->value_int )
461			bi->sql_flags |= BSQLF_USE_SUBTREE_SHORTCUT;
462		else
463			bi->sql_flags &= ~BSQLF_USE_SUBTREE_SHORTCUT;
464		break;
465	case BSQL_FETCH_ALL_ATTRS:
466		if ( c->value_int )
467			bi->sql_flags |= BSQLF_FETCH_ALL_ATTRS;
468		else
469			bi->sql_flags &= ~BSQLF_FETCH_ALL_ATTRS;
470		break;
471	case BSQL_CHECK_SCHEMA:
472		if ( c->value_int )
473			bi->sql_flags |= BSQLF_CHECK_SCHEMA;
474		else
475			bi->sql_flags &= ~BSQLF_CHECK_SCHEMA;
476		break;
477	case BSQL_AUTOCOMMIT:
478		if ( c->value_int )
479			bi->sql_flags |= BSQLF_AUTOCOMMIT_ON;
480		else
481			bi->sql_flags &= ~BSQLF_AUTOCOMMIT_ON;
482		break;
483	case BSQL_BASE_OBJECT:
484		if ( c->be->be_nsuffix == NULL ) {
485			snprintf( c->cr_msg, sizeof( c->cr_msg ),
486				"%s: suffix must be set", c->log );
487			Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg );
488			rc = ARG_BAD_CONF;
489			break;
490		}
491		if ( bi->sql_baseObject ) {
492			Debug( LDAP_DEBUG_CONFIG,
493				"%s: "
494				"\"baseObject\" already provided (will be overwritten)\n",
495				c->log );
496			entry_free( bi->sql_baseObject );
497		}
498		if ( c->argc == 2 && !strcmp( c->argv[1], "TRUE" ))
499			c->argc = 1;
500		switch( c->argc ) {
501		case 1:
502			return create_baseObject( c->be, c->fname, c->lineno );
503
504		case 2:
505			rc = read_baseObject( c->be, c->argv[ 1 ] );
506			if ( rc == 0 ) {
507				ch_free( bi->sql_base_ob_file );
508				bi->sql_base_ob_file = c->value_string;
509			}
510			return rc;
511
512		default:
513			snprintf( c->cr_msg, sizeof( c->cr_msg ),
514				"%s: trailing values in directive", c->log );
515			Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg );
516			return 1;
517		}
518		break;
519	case BSQL_LAYER:
520		if ( backsql_api_config( bi, c->argv[ 1 ], c->argc - 2, &c->argv[ 2 ] ) )
521		{
522			snprintf( c->cr_msg, sizeof( c->cr_msg ),
523				"%s: unable to load sql layer", c->log );
524			Debug( LDAP_DEBUG_ANY, "%s \"%s\"\n",
525				c->cr_msg, c->argv[1] );
526			return 1;
527		}
528		break;
529	case BSQL_ALIASING_KEYWORD:
530		if ( ! BER_BVISNULL( &bi->sql_aliasing ) ) {
531			ch_free( bi->sql_aliasing.bv_val );
532		}
533
534		ber_str2bv( c->argv[ 1 ], strlen( c->argv[ 1 ] ) + 1, 1,
535			&bi->sql_aliasing );
536		/* add a trailing space... */
537		bi->sql_aliasing.bv_val[ bi->sql_aliasing.bv_len - 1] = ' ';
538		break;
539	case BSQL_FETCH_ATTRS: {
540		char		*str, *s, *next;
541		const char	*delimstr = ",";
542
543		str = ch_strdup( c->argv[ 1 ] );
544		for ( s = ldap_pvt_strtok( str, delimstr, &next );
545				s != NULL;
546				s = ldap_pvt_strtok( NULL, delimstr, &next ) )
547		{
548			if ( strlen( s ) == 1 ) {
549				if ( *s == '*' ) {
550					bi->sql_flags |= BSQLF_FETCH_ALL_USERATTRS;
551					c->argv[ 1 ][ s - str ] = ',';
552
553				} else if ( *s == '+' ) {
554					bi->sql_flags |= BSQLF_FETCH_ALL_OPATTRS;
555					c->argv[ 1 ][ s - str ] = ',';
556				}
557			}
558		}
559		ch_free( str );
560		bi->sql_anlist = str2anlist( bi->sql_anlist, c->argv[ 1 ], delimstr );
561		if ( bi->sql_anlist == NULL ) {
562			return -1;
563		}
564		}
565		break;
566	}
567	return rc;
568}
569
570/*
571 * Read the entries specified in fname and merge the attributes
572 * to the user defined baseObject entry. Note that if we find any errors
573 * what so ever, we will discard the entire entries, print an
574 * error message and return.
575 */
576static int
577read_baseObject(
578	BackendDB	*be,
579	const char	*fname )
580{
581	backsql_info 	*bi = (backsql_info *)be->be_private;
582	LDIFFP		*fp;
583	int		rc = 0, lmax = 0, ldifrc;
584	unsigned long	lineno = 0;
585	char		*buf = NULL;
586
587	assert( fname != NULL );
588
589	fp = ldif_open( fname, "r" );
590	if ( fp == NULL ) {
591		Debug( LDAP_DEBUG_ANY,
592			"could not open back-sql baseObject "
593			"attr file \"%s\" - absolute path?\n",
594			fname );
595		perror( fname );
596		return LDAP_OTHER;
597	}
598
599	bi->sql_baseObject = entry_alloc();
600	if ( bi->sql_baseObject == NULL ) {
601		Debug( LDAP_DEBUG_ANY,
602			"read_baseObject_file: entry_alloc failed" );
603		ldif_close( fp );
604		return LDAP_NO_MEMORY;
605	}
606	bi->sql_baseObject->e_name = be->be_suffix[0];
607	bi->sql_baseObject->e_nname = be->be_nsuffix[0];
608	bi->sql_baseObject->e_attrs = NULL;
609
610	while (( ldifrc = ldif_read_record( fp, &lineno, &buf, &lmax )) > 0 ) {
611		Entry		*e = str2entry( buf );
612		Attribute	*a;
613
614		if( e == NULL ) {
615			fprintf( stderr, "back-sql baseObject: "
616					"could not parse entry (line=%lu)\n",
617					lineno );
618			rc = LDAP_OTHER;
619			break;
620		}
621
622		/* make sure the DN is the database's suffix */
623		if ( !be_issuffix( be, &e->e_nname ) ) {
624			fprintf( stderr,
625				"back-sql: invalid baseObject - "
626				"dn=\"%s\" (line=%lu)\n",
627				e->e_name.bv_val, lineno );
628			entry_free( e );
629			rc = LDAP_OTHER;
630			break;
631		}
632
633		/*
634		 * we found a valid entry, so walk thru all the attributes in the
635		 * entry, and add each attribute type and description to baseObject
636		 */
637		for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
638			if ( attr_merge( bi->sql_baseObject, a->a_desc,
639						a->a_vals,
640						( a->a_nvals == a->a_vals ) ?
641						NULL : a->a_nvals ) )
642			{
643				rc = LDAP_OTHER;
644				break;
645			}
646		}
647
648		entry_free( e );
649		if ( rc ) {
650			break;
651		}
652	}
653
654	if ( ldifrc < 0 )
655		rc = LDAP_OTHER;
656
657	if ( rc ) {
658		entry_free( bi->sql_baseObject );
659		bi->sql_baseObject = NULL;
660	}
661
662	ch_free( buf );
663
664	ldif_close( fp );
665
666	Debug( LDAP_DEBUG_CONFIG, "back-sql baseObject file \"%s\" read.\n",
667			fname );
668
669	return rc;
670}
671
672static int
673create_baseObject(
674	BackendDB	*be,
675	const char	*fname,
676	int		lineno )
677{
678	backsql_info 	*bi = (backsql_info *)be->be_private;
679	LDAPRDN		rdn;
680	char		*p;
681	int		rc, iAVA;
682	char		buf[1024];
683
684	snprintf( buf, sizeof(buf),
685			"dn: %s\n"
686			"objectClass: extensibleObject\n"
687			"description: builtin baseObject for back-sql\n"
688			"description: all entries mapped "
689				"in table \"ldap_entries\" "
690				"must have "
691				"\"" BACKSQL_BASEOBJECT_IDSTR "\" "
692				"in the \"parent\" column",
693			be->be_suffix[0].bv_val );
694
695	bi->sql_baseObject = str2entry( buf );
696	if ( bi->sql_baseObject == NULL ) {
697		Debug( LDAP_DEBUG_TRACE,
698			"<==backsql_db_config (%s line %d): "
699			"unable to parse baseObject entry\n",
700			fname, lineno );
701		return 1;
702	}
703
704	if ( BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ) {
705		return 0;
706	}
707
708	rc = ldap_bv2rdn( &be->be_suffix[ 0 ], &rdn, (char **)&p,
709			LDAP_DN_FORMAT_LDAP );
710	if ( rc != LDAP_SUCCESS ) {
711		Debug(LDAP_DEBUG_TRACE,
712		      "<==backsql_db_config (%s line %d): unable to extract RDN " "from baseObject DN \"%s\" (%d: %s)\n",
713		      fname, lineno, be->be_suffix[0].bv_val, rc,
714		      ldap_err2string(rc) );
715		return 1;
716	}
717
718	for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
719		LDAPAVA				*ava = rdn[ iAVA ];
720		AttributeDescription		*ad = NULL;
721		slap_syntax_transform_func	*transf = NULL;
722		struct berval			bv = BER_BVNULL;
723		const char			*text = NULL;
724
725		assert( ava != NULL );
726
727		rc = slap_bv2ad( &ava->la_attr, &ad, &text );
728		if ( rc != LDAP_SUCCESS ) {
729			Debug(LDAP_DEBUG_TRACE,
730			      "<==backsql_db_config (%s line %d): AttributeDescription of naming " "attribute #%d from baseObject " "DN \"%s\": %d: %s\n",
731			      fname, lineno, iAVA, be->be_suffix[0].bv_val,
732			      rc, ldap_err2string(rc) );
733			return 1;
734		}
735
736		transf = ad->ad_type->sat_syntax->ssyn_pretty;
737		if ( transf ) {
738			/*
739	 		 * transform value by pretty function
740			 *	if value is empty, use empty_bv
741			 */
742			rc = ( *transf )( ad->ad_type->sat_syntax,
743				ava->la_value.bv_len
744					? &ava->la_value
745					: (struct berval *) &slap_empty_bv,
746				&bv, NULL );
747
748			if ( rc != LDAP_SUCCESS ) {
749				Debug(LDAP_DEBUG_TRACE,
750				      "<==backsql_db_config (%s line %d): " "prettying of attribute #%d " "from baseObject " "DN \"%s\" failed: %d: %s\n",
751				      fname, lineno, iAVA,
752				      be->be_suffix[0].bv_val, rc,
753				      ldap_err2string(rc) );
754				return 1;
755			}
756		}
757
758		if ( !BER_BVISNULL( &bv ) ) {
759			if ( ava->la_flags & LDAP_AVA_FREE_VALUE ) {
760				ber_memfree( ava->la_value.bv_val );
761			}
762			ava->la_value = bv;
763			ava->la_flags |= LDAP_AVA_FREE_VALUE;
764		}
765
766		attr_merge_normalize_one( bi->sql_baseObject,
767				ad, &ava->la_value, NULL );
768	}
769
770	ldap_rdnfree( rdn );
771
772	return 0;
773}
774
775int backsql_init_cf( BackendInfo *bi )
776{
777	int rc;
778
779	bi->bi_cf_ocs = sqlocs;
780	rc = config_register_schema( sqlcfg, sqlocs );
781	if ( rc ) return rc;
782	return 0;
783}
784