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