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 
41 static int
42 create_baseObject(
43 	BackendDB	*be,
44 	const char	*fname,
45 	int		lineno );
46 
47 static int
48 read_baseObject(
49 	BackendDB	*be,
50 	const char	*fname );
51 
52 static ConfigDriver sql_cf_gen;
53 
54 enum {
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 
71 static 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 
261 static 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 
282 static int
sql_cf_gen(ConfigArgs * c)283 sql_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  */
576 static int
read_baseObject(BackendDB * be,const char * fname)577 read_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 
672 static int
create_baseObject(BackendDB * be,const char * fname,int lineno)673 create_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 
backsql_init_cf(BackendInfo * bi)775 int 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