]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/config.c
Initial config support for back-meta
[openldap] / servers / slapd / back-meta / config.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2012 The OpenLDAP Foundation.
5  * Portions Copyright 2001-2003 Pierangelo Masarati.
6  * Portions Copyright 1999-2003 Howard Chu.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by the Howard Chu for inclusion
19  * in OpenLDAP Software and subsequently enhanced by Pierangelo
20  * Masarati.
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/string.h>
28 #include <ac/socket.h>
29
30 #include "slap.h"
31 #include "config.h"
32 #include "lutil.h"
33 #include "../back-ldap/back-ldap.h"
34 #include "back-meta.h"
35
36 static ConfigDriver meta_back_cf_gen;
37
38 static int ldap_back_map_config(
39         ConfigArgs *c,
40         struct ldapmap  *oc_map,
41         struct ldapmap  *at_map );
42
43 enum {
44         LDAP_BACK_CFG_URI = 1,
45         LDAP_BACK_CFG_TLS,
46         LDAP_BACK_CFG_ACL_AUTHCDN,
47         LDAP_BACK_CFG_ACL_PASSWD,
48         LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
49         LDAP_BACK_CFG_IDASSERT_BIND,
50         LDAP_BACK_CFG_REBIND,
51         LDAP_BACK_CFG_CHASE,
52         LDAP_BACK_CFG_T_F,
53         LDAP_BACK_CFG_TIMEOUT,
54         LDAP_BACK_CFG_IDLE_TIMEOUT,
55         LDAP_BACK_CFG_CONN_TTL,
56         LDAP_BACK_CFG_NETWORK_TIMEOUT,
57         LDAP_BACK_CFG_VERSION,
58         LDAP_BACK_CFG_SINGLECONN,
59         LDAP_BACK_CFG_USETEMP,
60         LDAP_BACK_CFG_CONNPOOLMAX,
61         LDAP_BACK_CFG_CANCEL,
62         LDAP_BACK_CFG_QUARANTINE,
63         LDAP_BACK_CFG_ST_REQUEST,
64         LDAP_BACK_CFG_NOREFS,
65         LDAP_BACK_CFG_NOUNDEFFILTER,
66
67         LDAP_BACK_CFG_REWRITE,
68
69         LDAP_BACK_CFG_SUFFIXM,
70         LDAP_BACK_CFG_MAP,
71         LDAP_BACK_CFG_SUBTREE_EX,
72         LDAP_BACK_CFG_SUBTREE_IN,
73         LDAP_BACK_CFG_DEFAULT_T,
74         LDAP_BACK_CFG_DNCACHE_TTL,
75         LDAP_BACK_CFG_BIND_TIMEOUT,
76         LDAP_BACK_CFG_ONERR,
77         LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
78         LDAP_BACK_CFG_PSEUDOROOTDN,
79         LDAP_BACK_CFG_PSEUDOROOTPW,
80         LDAP_BACK_CFG_NRETRIES,
81         LDAP_BACK_CFG_CLIENT_PR,
82
83         LDAP_BACK_CFG_LAST
84 };
85
86 static ConfigTable metacfg[] = {
87         { "uri", "uri", 2, 2, 0,
88                 ARG_MAGIC|LDAP_BACK_CFG_URI,
89                 meta_back_cf_gen, "( OLcfgDbAt:0.14 "
90                         "NAME 'olcDbURI' "
91                         "DESC 'URI (list) for remote DSA' "
92                         "SYNTAX OMsDirectoryString "
93                         "SINGLE-VALUE )",
94                 NULL, NULL },
95         { "tls", "what", 2, 0, 0,
96                 ARG_MAGIC|LDAP_BACK_CFG_TLS,
97                 meta_back_cf_gen, "( OLcfgDbAt:3.1 "
98                         "NAME 'olcDbStartTLS' "
99                         "DESC 'StartTLS' "
100                         "SYNTAX OMsDirectoryString "
101                         "SINGLE-VALUE )",
102                 NULL, NULL },
103         { "acl-authcDN", "DN", 2, 2, 0,
104                 ARG_DN|ARG_MAGIC|LDAP_BACK_CFG_ACL_AUTHCDN,
105                 meta_back_cf_gen, "( OLcfgDbAt:3.2 "
106                         "NAME 'olcDbACLAuthcDn' "
107                         "DESC 'Remote ACL administrative identity' "
108                         "OBSOLETE "
109                         "SYNTAX OMsDN "
110                         "SINGLE-VALUE )",
111                 NULL, NULL },
112         /* deprecated, will be removed; aliases "acl-authcDN" */
113         { "binddn", "DN", 2, 2, 0,
114                 ARG_DN|ARG_MAGIC|LDAP_BACK_CFG_ACL_AUTHCDN,
115                 meta_back_cf_gen, NULL, NULL, NULL },
116         { "acl-passwd", "cred", 2, 2, 0,
117                 ARG_MAGIC|LDAP_BACK_CFG_ACL_PASSWD,
118                 meta_back_cf_gen, "( OLcfgDbAt:3.3 "
119                         "NAME 'olcDbACLPasswd' "
120                         "DESC 'Remote ACL administrative identity credentials' "
121                         "OBSOLETE "
122                         "SYNTAX OMsDirectoryString "
123                         "SINGLE-VALUE )",
124                 NULL, NULL },
125         /* deprecated, will be removed; aliases "acl-passwd" */
126         { "bindpw", "cred", 2, 2, 0,
127                 ARG_MAGIC|LDAP_BACK_CFG_ACL_PASSWD,
128                 meta_back_cf_gen, NULL, NULL, NULL },
129         { "idassert-bind", "args", 2, 0, 0,
130                 ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_BIND,
131                 meta_back_cf_gen, "( OLcfgDbAt:3.7 "
132                         "NAME 'olcDbIDAssertBind' "
133                         "DESC 'Remote Identity Assertion administrative identity auth bind configuration' "
134                         "SYNTAX OMsDirectoryString "
135                         "SINGLE-VALUE )",
136                 NULL, NULL },
137         { "idassert-authzFrom", "authzRule", 2, 2, 0,
138                 ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
139                 meta_back_cf_gen, "( OLcfgDbAt:3.9 "
140                         "NAME 'olcDbIDAssertAuthzFrom' "
141                         "DESC 'Remote Identity Assertion authz rules' "
142                         "EQUALITY caseIgnoreMatch "
143                         "SYNTAX OMsDirectoryString "
144                         "X-ORDERED 'VALUES' )",
145                 NULL, NULL },
146         { "rebind-as-user", "true|FALSE", 1, 2, 0,
147                 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_REBIND,
148                 meta_back_cf_gen, "( OLcfgDbAt:3.10 "
149                         "NAME 'olcDbRebindAsUser' "
150                         "DESC 'Rebind as user' "
151                         "SYNTAX OMsBoolean "
152                         "SINGLE-VALUE )",
153                 NULL, NULL },
154         { "chase-referrals", "true|FALSE", 2, 2, 0,
155                 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_CHASE,
156                 meta_back_cf_gen, "( OLcfgDbAt:3.11 "
157                         "NAME 'olcDbChaseReferrals' "
158                         "DESC 'Chase referrals' "
159                         "SYNTAX OMsBoolean "
160                         "SINGLE-VALUE )",
161                 NULL, NULL },
162         { "t-f-support", "true|FALSE|discover", 2, 2, 0,
163                 ARG_MAGIC|LDAP_BACK_CFG_T_F,
164                 meta_back_cf_gen, "( OLcfgDbAt:3.12 "
165                         "NAME 'olcDbTFSupport' "
166                         "DESC 'Absolute filters support' "
167                         "SYNTAX OMsDirectoryString "
168                         "SINGLE-VALUE )",
169                 NULL, NULL },
170         { "timeout", "timeout(list)", 2, 0, 0,
171                 ARG_MAGIC|LDAP_BACK_CFG_TIMEOUT,
172                 meta_back_cf_gen, "( OLcfgDbAt:3.14 "
173                         "NAME 'olcDbTimeout' "
174                         "DESC 'Per-operation timeouts' "
175                         "SYNTAX OMsDirectoryString "
176                         "SINGLE-VALUE )",
177                 NULL, NULL },
178         { "idle-timeout", "timeout", 2, 2, 0,
179                 ARG_MAGIC|LDAP_BACK_CFG_IDLE_TIMEOUT,
180                 meta_back_cf_gen, "( OLcfgDbAt:3.15 "
181                         "NAME 'olcDbIdleTimeout' "
182                         "DESC 'connection idle timeout' "
183                         "SYNTAX OMsDirectoryString "
184                         "SINGLE-VALUE )",
185                 NULL, NULL },
186         { "conn-ttl", "ttl", 2, 2, 0,
187                 ARG_MAGIC|LDAP_BACK_CFG_CONN_TTL,
188                 meta_back_cf_gen, "( OLcfgDbAt:3.16 "
189                         "NAME 'olcDbConnTtl' "
190                         "DESC 'connection ttl' "
191                         "SYNTAX OMsDirectoryString "
192                         "SINGLE-VALUE )",
193                 NULL, NULL },
194         { "network-timeout", "timeout", 2, 2, 0,
195                 ARG_MAGIC|LDAP_BACK_CFG_NETWORK_TIMEOUT,
196                 meta_back_cf_gen, "( OLcfgDbAt:3.17 "
197                         "NAME 'olcDbNetworkTimeout' "
198                         "DESC 'connection network timeout' "
199                         "SYNTAX OMsDirectoryString "
200                         "SINGLE-VALUE )",
201                 NULL, NULL },
202         { "protocol-version", "version", 2, 2, 0,
203                 ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_VERSION,
204                 meta_back_cf_gen, "( OLcfgDbAt:3.18 "
205                         "NAME 'olcDbProtocolVersion' "
206                         "DESC 'protocol version' "
207                         "SYNTAX OMsInteger "
208                         "SINGLE-VALUE )",
209                 NULL, NULL },
210         { "single-conn", "true|FALSE", 2, 2, 0,
211                 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_SINGLECONN,
212                 meta_back_cf_gen, "( OLcfgDbAt:3.19 "
213                         "NAME 'olcDbSingleConn' "
214                         "DESC 'cache a single connection per identity' "
215                         "SYNTAX OMsBoolean "
216                         "SINGLE-VALUE )",
217                 NULL, NULL },
218         { "cancel", "ABANDON|ignore|exop", 2, 2, 0,
219                 ARG_MAGIC|LDAP_BACK_CFG_CANCEL,
220                 meta_back_cf_gen, "( OLcfgDbAt:3.20 "
221                         "NAME 'olcDbCancel' "
222                         "DESC 'abandon/ignore/exop operations when appropriate' "
223                         "SYNTAX OMsDirectoryString "
224                         "SINGLE-VALUE )",
225                 NULL, NULL },
226         { "quarantine", "retrylist", 2, 2, 0,
227                 ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
228                 meta_back_cf_gen, "( OLcfgDbAt:3.21 "
229                         "NAME 'olcDbQuarantine' "
230                         "DESC 'Quarantine database if connection fails and retry according to rule' "
231                         "SYNTAX OMsDirectoryString "
232                         "SINGLE-VALUE )",
233                 NULL, NULL },
234         { "use-temporary-conn", "true|FALSE", 2, 2, 0,
235                 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_USETEMP,
236                 meta_back_cf_gen, "( OLcfgDbAt:3.22 "
237                         "NAME 'olcDbUseTemporaryConn' "
238                         "DESC 'Use temporary connections if the cached one is busy' "
239                         "SYNTAX OMsBoolean "
240                         "SINGLE-VALUE )",
241                 NULL, NULL },
242         { "conn-pool-max", "<n>", 2, 2, 0,
243                 ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_CONNPOOLMAX,
244                 meta_back_cf_gen, "( OLcfgDbAt:3.23 "
245                         "NAME 'olcDbConnectionPoolMax' "
246                         "DESC 'Max size of privileged connections pool' "
247                         "SYNTAX OMsInteger "
248                         "SINGLE-VALUE )",
249                 NULL, NULL },
250 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
251         { "session-tracking-request", "true|FALSE", 2, 2, 0,
252                 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_ST_REQUEST,
253                 meta_back_cf_gen, "( OLcfgDbAt:3.24 "
254                         "NAME 'olcDbSessionTrackingRequest' "
255                         "DESC 'Add session tracking control to proxied requests' "
256                         "SYNTAX OMsBoolean "
257                         "SINGLE-VALUE )",
258                 NULL, NULL },
259 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
260         { "norefs", "true|FALSE", 2, 2, 0,
261                 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOREFS,
262                 meta_back_cf_gen, "( OLcfgDbAt:3.25 "
263                         "NAME 'olcDbNoRefs' "
264                         "DESC 'Do not return search reference responses' "
265                         "SYNTAX OMsBoolean "
266                         "SINGLE-VALUE )",
267                 NULL, NULL },
268         { "noundeffilter", "true|FALSE", 2, 2, 0,
269                 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOUNDEFFILTER,
270                 meta_back_cf_gen, "( OLcfgDbAt:3.26 "
271                         "NAME 'olcDbNoUndefFilter' "
272                         "DESC 'Do not propagate undefined search filters' "
273                         "SYNTAX OMsBoolean "
274                         "SINGLE-VALUE )",
275                 NULL, NULL },
276
277         { "rewrite", "arglist", 2, 4, STRLENOF( "rewrite" ),
278                 ARG_STRING|ARG_MAGIC|LDAP_BACK_CFG_REWRITE,
279                 meta_back_cf_gen, "( OLcfgDbAt:3.100 "
280                         "NAME 'olcDbRewrite' "
281                         "DESC 'DN rewriting rules' "
282                         "SYNTAX OMsDirectoryString )",
283                 NULL, NULL },
284         { "suffixmassage", "virtual> <real", 3, 3, 0,
285                 ARG_MAGIC|LDAP_BACK_CFG_SUFFIXM,
286                 meta_back_cf_gen, "( OLcfgDbAt:3.101 "
287                         "NAME 'olcDbSuffixMassage' "
288                         "DESC 'Suffix rewriting rule' "
289                         "SYNTAX OMsDirectoryString "
290                         "SINGLE-VALUE )",
291                 NULL, NULL },
292         { "map", "attribute|objectClass> [*|<local>] *|<remote", 3, 4, 0,
293                 ARG_MAGIC|LDAP_BACK_CFG_MAP,
294                 meta_back_cf_gen, "( OLcfgDbAt:3.102 "
295                         "NAME 'olcDbMap' "
296                         "DESC 'Map attribute and objectclass names' "
297                         "SYNTAX OMsDirectoryString )",
298                 NULL, NULL },
299
300         { "subtree-exclude", "pattern", 2, 2, 0,
301                 ARG_STRING|ARG_MAGIC|LDAP_BACK_CFG_SUBTREE_EX,
302                 meta_back_cf_gen, "( OLcfgDbAt:3.103 "
303                         "NAME 'olcDbSubtreeExclude' "
304                         "DESC 'DN of subtree to exclude from target' "
305                         "SYNTAX OMsDirectoryString )",
306                 NULL, NULL },
307         { "subtree-include", "pattern", 2, 2, 0,
308                 ARG_STRING|ARG_MAGIC|LDAP_BACK_CFG_SUBTREE_IN,
309                 meta_back_cf_gen, "( OLcfgDbAt:3.104 "
310                         "NAME 'olcDbSubtreeInclude' "
311                         "DESC 'DN of subtree to include in target' "
312                         "SYNTAX OMsDirectoryString )",
313                 NULL, NULL },
314         { "default-target", "[none|<target ID>]", 1, 2, 0,
315                 ARG_MAGIC|LDAP_BACK_CFG_DEFAULT_T,
316                 meta_back_cf_gen, "( OLcfgDbAt:3.105 "
317                         "NAME 'olcDbDefaultTarget' "
318                         "DESC 'Specify the default target' "
319                         "SYNTAX OMsDirectoryString "
320                         "SINGLE-VALUE )",
321                 NULL, NULL },
322         { "dncache-ttl", "ttl", 2, 2, 0,
323                 ARG_MAGIC|LDAP_BACK_CFG_DNCACHE_TTL,
324                 meta_back_cf_gen, "( OLcfgDbAt:3.106 "
325                         "NAME 'olcDbDnCacheTtl' "
326                         "DESC 'dncache ttl' "
327                         "SYNTAX OMsDirectoryString "
328                         "SINGLE-VALUE )",
329                 NULL, NULL },
330         { "bind-timeout", "microseconds", 2, 2, 0,
331                 ARG_MAGIC|LDAP_BACK_CFG_BIND_TIMEOUT,
332                 meta_back_cf_gen, "( OLcfgDbAt:3.107 "
333                         "NAME 'olcDbBindTimeout' "
334                         "DESC 'bind timeout' "
335                         "SYNTAX OMsDirectoryString "
336                         "SINGLE-VALUE )",
337                 NULL, NULL },
338         { "onerr", "CONTINUE|report|stop", 2, 2, 0,
339                 ARG_MAGIC|LDAP_BACK_CFG_ONERR,
340                 meta_back_cf_gen, "( OLcfgDbAt:3.108 "
341                         "NAME 'olcDbOnErr' "
342                         "DESC 'error handling' "
343                         "SYNTAX OMsDirectoryString "
344                         "SINGLE-VALUE )",
345                 NULL, NULL },
346         { "pseudoroot-bind-defer", "TRUE|false", 2, 2, 0,
347                 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
348                 meta_back_cf_gen, "( OLcfgDbAt:3.109 "
349                         "NAME 'olcDbPseudoRootBindDefer' "
350                         "DESC 'error handling' "
351                         "SYNTAX OMsBoolean "
352                         "SINGLE-VALUE )",
353                 NULL, NULL },
354         { "root-bind-defer", "TRUE|false", 2, 2, 0,
355                 ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
356                 meta_back_cf_gen, NULL, NULL, NULL },
357         { "pseudorootdn", "dn", 2, 2, 0,
358                 ARG_MAGIC|ARG_DN|LDAP_BACK_CFG_PSEUDOROOTDN,
359                 meta_back_cf_gen, NULL, NULL, NULL },
360         { "pseudorootpw", "password", 2, 2, 0,
361                 ARG_MAGIC|ARG_STRING|LDAP_BACK_CFG_PSEUDOROOTDN,
362                 meta_back_cf_gen, NULL, NULL, NULL },
363         { "nretries", "NEVER|forever|<number>", 2, 2, 0,
364                 ARG_MAGIC|ARG_STRING|LDAP_BACK_CFG_NRETRIES,
365                 meta_back_cf_gen, "( OLcfgDbAt:3.110 "
366                         "NAME 'olcDbNretries' "
367                         "DESC 'retry handling' "
368                         "SYNTAX OMsDirectoryString "
369                         "SINGLE-VALUE )",
370                 NULL, NULL },
371         { "client-pr", "accept-unsolicited|disable|<size>", 2, 2, 0,
372                 ARG_MAGIC|ARG_STRING|LDAP_BACK_CFG_CLIENT_PR,
373                 meta_back_cf_gen, "( OLcfgDbAt:3.111 "
374                         "NAME 'olcDbClientPr' "
375                         "DESC 'PagedResults handling' "
376                         "SYNTAX OMsDirectoryString "
377                         "SINGLE-VALUE )",
378                 NULL, NULL },
379         
380         { NULL, NULL, 0, 0, 0, ARG_IGNORED,
381                 NULL, NULL, NULL, NULL }
382 };
383
384 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
385 #define ST_ATTR "$ olcDbSessionTrackingRequest "
386 #else
387 #define ST_ATTR ""
388 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
389
390 #define TARGET_ATTRS    \
391                         "$ olcDbCancel " \
392                         "$ olcDbChaseReferrals " \
393                         "$ olcDbClientPr " \
394                         "$ olcDbDefaultTarget " \
395                         "$ olcDbIdleTimeout " \
396                         "$ olcDbNetworkTimeout " \
397                         "$ olcDbNoRefs " \
398                         "$ olcDbNoUndefFilter " \
399                         "$ olcDbNretries " \
400                         "$ olcDbProtocolVersion " \
401                         ST_ATTR \
402                         "$ olcDbTFSupport "
403
404 static ConfigOCs metaocs[] = {
405         { "( OLcfgDbOc:3.2 "
406                 "NAME 'olcMetaConfig' "
407                 "DESC 'Meta backend configuration' "
408                 "SUP olcDatabaseConfig "
409                 "MAY ( olcDbConnTtl "
410                         "$ olcDbDnCacheTtl "
411                         "$ olcDbOnErr "
412                         "$ olcDbPseudoRootBindDefer "
413                         "$ olcDbQuarantine "
414                         "$ olcDbRebindAsUser "
415                         "$ olcDbSingleConn "
416                         "$ olcDbUseTemporaryConn "
417                         "$ olcDbConnectionPoolMax "
418
419                         /* defaults, may be overridden per-target */
420                         TARGET_ATTRS
421                 ") )",
422                         Cft_Database, metacfg},
423         { "( OLcfgDbOc:3.3 "
424                 "NAME 'olcMetaTarget' "
425                 "DESC 'Meta target configuration' "
426                 "MUST olcDbURI "
427                 "MAY ( olcDbACLAuthcDn "
428                         "$ olcDbACLPasswd "
429                         "$ olcDbBindTimeout "
430                         "$ olcDbIDAssertAuthzFrom "
431                         "$ olcDbIDAssertBind "
432                         "$ olcDbMap "
433                         "$ olcDbRewrite "
434                         "$ olcDbSubtreeExclude "
435                         "$ olcDbSubtreeInclude "
436                         "$ olcDbSuffixMassage "
437                         "$ olcDbTimeout "
438                         "$ olcDbStartTLS "
439
440                         /* defaults may be inherited */
441                         TARGET_ATTRS
442                 ") )",
443                         Cft_Misc, metacfg, NULL /* meta_ldadd */},
444         { NULL, 0, NULL }
445 };
446
447 static int
448 meta_back_new_target(
449         metatarget_t    **mtp )
450 {
451         char                    *rargv[ 3 ];
452         metatarget_t            *mt;
453
454         *mtp = NULL;
455
456         mt = ch_calloc( sizeof( metatarget_t ), 1 );
457
458         mt->mt_rwmap.rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
459         if ( mt->mt_rwmap.rwm_rw == NULL ) {
460                 ch_free( mt );
461                 return -1;
462         }
463
464         /*
465          * the filter rewrite as a string must be disabled
466          * by default; it can be re-enabled by adding rules;
467          * this creates an empty rewriteContext
468          */
469         rargv[ 0 ] = "rewriteContext";
470         rargv[ 1 ] = "searchFilter";
471         rargv[ 2 ] = NULL;
472         rewrite_parse( mt->mt_rwmap.rwm_rw, "<suffix massage>", 1, 2, rargv );
473
474         rargv[ 0 ] = "rewriteContext";
475         rargv[ 1 ] = "default";
476         rargv[ 2 ] = NULL;
477         rewrite_parse( mt->mt_rwmap.rwm_rw, "<suffix massage>", 1, 2, rargv );
478
479         ldap_pvt_thread_mutex_init( &mt->mt_uri_mutex );
480
481         mt->mt_idassert_mode = LDAP_BACK_IDASSERT_LEGACY;
482         mt->mt_idassert_authmethod = LDAP_AUTH_NONE;
483         mt->mt_idassert_tls = SB_TLS_DEFAULT;
484
485         /* by default, use proxyAuthz control on each operation */
486         mt->mt_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE;
487
488         *mtp = mt;
489
490         return 0;
491 }
492
493 int
494 meta_subtree_destroy( metasubtree_t *ms )
495 {
496         if ( ms->ms_next ) {
497                 meta_subtree_destroy( ms->ms_next );
498         }
499
500         switch ( ms->ms_type ) {
501         case META_ST_SUBTREE:
502         case META_ST_SUBORDINATE:
503                 ber_memfree( ms->ms_dn.bv_val );
504                 break;
505
506         case META_ST_REGEX:
507                 regfree( &ms->ms_regex );
508                 ch_free( ms->ms_regex_pattern );
509                 break;
510
511         default:
512                 return -1;
513         }
514
515         ch_free( ms );
516
517         return 0;
518 }
519
520 static int
521 meta_subtree_config(
522         metatarget_t *mt,
523         ConfigArgs *c )
524 {
525         meta_st_t       type = META_ST_SUBTREE;
526         char            *pattern;
527         struct berval   ndn = BER_BVNULL;
528         metasubtree_t   *ms = NULL;
529
530         if ( c->type == LDAP_BACK_CFG_SUBTREE_EX ) {
531                 if ( mt->mt_subtree && !mt->mt_subtree_exclude ) {
532                         snprintf( c->cr_msg, sizeof(c->cr_msg),
533                                 "\"subtree-exclude\" incompatible with previous \"subtree-include\" directives" );
534                         return 1;
535                 }
536
537                 mt->mt_subtree_exclude = 1;
538
539         } else {
540                 if ( mt->mt_subtree && mt->mt_subtree_exclude ) {
541                         snprintf( c->cr_msg, sizeof(c->cr_msg),
542                                 "\"subtree-include\" incompatible with previous \"subtree-exclude\" directives" );
543                         return 1;
544                 }
545         }
546
547         pattern = c->argv[1];
548         if ( strncasecmp( pattern, "dn", STRLENOF( "dn" ) ) == 0 ) {
549                 char *style;
550
551                 pattern = &pattern[STRLENOF( "dn")];
552
553                 if ( pattern[0] == '.' ) {
554                         style = &pattern[1];
555
556                         if ( strncasecmp( style, "subtree", STRLENOF( "subtree" ) ) == 0 ) {
557                                 type = META_ST_SUBTREE;
558                                 pattern = &style[STRLENOF( "subtree" )];
559
560                         } else if ( strncasecmp( style, "children", STRLENOF( "children" ) ) == 0 ) {
561                                 type = META_ST_SUBORDINATE;
562                                 pattern = &style[STRLENOF( "children" )];
563
564                         } else if ( strncasecmp( style, "sub", STRLENOF( "sub" ) ) == 0 ) {
565                                 type = META_ST_SUBTREE;
566                                 pattern = &style[STRLENOF( "sub" )];
567
568                         } else if ( strncasecmp( style, "regex", STRLENOF( "regex" ) ) == 0 ) {
569                                 type = META_ST_REGEX;
570                                 pattern = &style[STRLENOF( "regex" )];
571
572                         } else {
573                                 snprintf( c->cr_msg, sizeof(c->cr_msg), "unknown style in \"dn.<style>\"" );
574                                 return 1;
575                         }
576                 }
577
578                 if ( pattern[0] != ':' ) {
579                         snprintf( c->cr_msg, sizeof(c->cr_msg), "missing colon after \"dn.<style>\"" );
580                         return 1;
581                 }
582                 pattern++;
583         }
584
585         switch ( type ) {
586         case META_ST_SUBTREE:
587         case META_ST_SUBORDINATE: {
588                 struct berval dn;
589
590                 ber_str2bv( pattern, 0, 0, &dn );
591                 if ( dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL )
592                         != LDAP_SUCCESS )
593                 {
594                         snprintf( c->cr_msg, sizeof(c->cr_msg), "DN=\"%s\" is invalid", pattern );
595                         return 1;
596                 }
597
598                 if ( !dnIsSuffix( &ndn, &mt->mt_nsuffix ) ) {
599                         snprintf( c->cr_msg, sizeof(c->cr_msg),
600                                 "DN=\"%s\" is not a subtree of target \"%s\"",
601                                 pattern, mt->mt_nsuffix.bv_val );
602                         ber_memfree( ndn.bv_val );
603                         return( 1 );
604                 }
605                 } break;
606
607         default:
608                 /* silence warnings */
609                 break;
610         }
611
612         ms = ch_calloc( sizeof( metasubtree_t ), 1 );
613         ms->ms_type = type;
614
615         switch ( ms->ms_type ) {
616         case META_ST_SUBTREE:
617         case META_ST_SUBORDINATE:
618                 ms->ms_dn = ndn;
619                 break;
620
621         case META_ST_REGEX: {
622                 int rc;
623
624                 rc = regcomp( &ms->ms_regex, pattern, REG_EXTENDED|REG_ICASE );
625                 if ( rc != 0 ) {
626                         char regerr[ SLAP_TEXT_BUFLEN ];
627
628                         regerror( rc, &ms->ms_regex, regerr, sizeof(regerr) );
629
630                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
631                                 "regular expression \"%s\" bad because of %s",
632                                 pattern, regerr );
633                         ch_free( ms );
634                         return 1;
635                 }
636                 ms->ms_regex_pattern = ch_strdup( pattern );
637                 } break;
638         }
639
640         if ( mt->mt_subtree == NULL ) {
641                  mt->mt_subtree = ms;
642
643         } else {
644                 metasubtree_t **msp;
645
646                 for ( msp = &mt->mt_subtree; *msp; ) {
647                         switch ( ms->ms_type ) {
648                         case META_ST_SUBTREE:
649                                 switch ( (*msp)->ms_type ) {
650                                 case META_ST_SUBTREE:
651                                         if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
652                                                 metasubtree_t *tmp = *msp;
653                                                 Debug( LDAP_DEBUG_CONFIG,
654                                                         "%s: previous rule \"dn.subtree:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
655                                                         c->log, pattern, (*msp)->ms_dn.bv_val );
656                                                 *msp = (*msp)->ms_next;
657                                                 tmp->ms_next = NULL;
658                                                 meta_subtree_destroy( tmp );
659                                                 continue;
660
661                                         } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) ) {
662                                                 Debug( LDAP_DEBUG_CONFIG,
663                                                         "%s: previous rule \"dn.subtree:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
664                                                         c->log, (*msp)->ms_dn.bv_val, pattern );
665                                                 meta_subtree_destroy( ms );
666                                                 ms = NULL;
667                                                 return( 0 );
668                                         }
669                                         break;
670
671                                 case META_ST_SUBORDINATE:
672                                         if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
673                                                 metasubtree_t *tmp = *msp;
674                                                 Debug( LDAP_DEBUG_CONFIG,
675                                                         "%s: previous rule \"dn.children:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
676                                                         c->log, pattern, (*msp)->ms_dn.bv_val );
677                                                 *msp = (*msp)->ms_next;
678                                                 tmp->ms_next = NULL;
679                                                 meta_subtree_destroy( tmp );
680                                                 continue;
681
682                                         } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) && ms->ms_dn.bv_len > (*msp)->ms_dn.bv_len ) {
683                                                 Debug( LDAP_DEBUG_CONFIG,
684                                                         "%s: previous rule \"dn.children:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
685                                                         c->log, (*msp)->ms_dn.bv_val, pattern );
686                                                 meta_subtree_destroy( ms );
687                                                 ms = NULL;
688                                                 return( 0 );
689                                         }
690                                         break;
691
692                                 case META_ST_REGEX:
693                                         if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
694                                                 Debug( LDAP_DEBUG_CONFIG,
695                                                         "%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n",
696                                                         c->log, (*msp)->ms_regex_pattern, ms->ms_dn.bv_val );
697                                         }
698                                         break;
699                                 }
700                                 break;
701
702                         case META_ST_SUBORDINATE:
703                                 switch ( (*msp)->ms_type ) {
704                                 case META_ST_SUBTREE:
705                                         if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
706                                                 metasubtree_t *tmp = *msp;
707                                                 Debug( LDAP_DEBUG_CONFIG,
708                                                         "%s: previous rule \"dn.children:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
709                                                         c->log, pattern, (*msp)->ms_dn.bv_val );
710                                                 *msp = (*msp)->ms_next;
711                                                 tmp->ms_next = NULL;
712                                                 meta_subtree_destroy( tmp );
713                                                 continue;
714
715                                         } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) && ms->ms_dn.bv_len > (*msp)->ms_dn.bv_len ) {
716                                                 Debug( LDAP_DEBUG_CONFIG,
717                                                         "%s: previous rule \"dn.children:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
718                                                         c->log, (*msp)->ms_dn.bv_val, pattern );
719                                                 meta_subtree_destroy( ms );
720                                                 ms = NULL;
721                                                 return( 0 );
722                                         }
723                                         break;
724
725                                 case META_ST_SUBORDINATE:
726                                         if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
727                                                 metasubtree_t *tmp = *msp;
728                                                 Debug( LDAP_DEBUG_CONFIG,
729                                                         "%s: previous rule \"dn.children:%s\" is contained in rule \"dn.children:%s\" (replaced)\n",
730                                                         c->log, pattern, (*msp)->ms_dn.bv_val );
731                                                 *msp = (*msp)->ms_next;
732                                                 tmp->ms_next = NULL;
733                                                 meta_subtree_destroy( tmp );
734                                                 continue;
735
736                                         } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) ) {
737                                                 Debug( LDAP_DEBUG_CONFIG,
738                                                         "%s: previous rule \"dn.children:%s\" contains rule \"dn.children:%s\" (ignored)\n",
739                                                         c->log, (*msp)->ms_dn.bv_val, pattern );
740                                                 meta_subtree_destroy( ms );
741                                                 ms = NULL;
742                                                 return( 0 );
743                                         }
744                                         break;
745
746                                 case META_ST_REGEX:
747                                         if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
748                                                 Debug( LDAP_DEBUG_CONFIG,
749                                                         "%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n",
750                                                         c->log, (*msp)->ms_regex_pattern, ms->ms_dn.bv_val );
751                                         }
752                                         break;
753                                 }
754                                 break;
755
756                         case META_ST_REGEX:
757                                 switch ( (*msp)->ms_type ) {
758                                 case META_ST_SUBTREE:
759                                 case META_ST_SUBORDINATE:
760                                         if ( regexec( &ms->ms_regex, (*msp)->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
761                                                 Debug( LDAP_DEBUG_CONFIG,
762                                                         "%s: previous rule \"dn.subtree:%s\" may be contained in rule \"dn.regex:%s\"\n",
763                                                         c->log, (*msp)->ms_dn.bv_val, ms->ms_regex_pattern );
764                                         }
765                                         break;
766
767                                 case META_ST_REGEX:
768                                         /* no check possible */
769                                         break;
770                                 }
771                                 break;
772                         }
773
774                         msp = &(*msp)->ms_next;
775                 }
776
777                 *msp = ms;
778         }
779
780         return 0;
781 }
782
783 static slap_verbmasks idassert_mode[] = {
784         { BER_BVC("self"),              LDAP_BACK_IDASSERT_SELF },
785         { BER_BVC("anonymous"),         LDAP_BACK_IDASSERT_ANONYMOUS },
786         { BER_BVC("none"),              LDAP_BACK_IDASSERT_NOASSERT },
787         { BER_BVC("legacy"),            LDAP_BACK_IDASSERT_LEGACY },
788         { BER_BVNULL,                   0 }
789 };
790
791 static slap_verbmasks tls_mode[] = {
792         { BER_BVC( "propagate" ),       LDAP_BACK_F_TLS_PROPAGATE_MASK },
793         { BER_BVC( "try-propagate" ),   LDAP_BACK_F_PROPAGATE_TLS },
794         { BER_BVC( "start" ),           LDAP_BACK_F_TLS_USE_MASK },
795         { BER_BVC( "try-start" ),       LDAP_BACK_F_USE_TLS },
796         { BER_BVC( "ldaps" ),           LDAP_BACK_F_TLS_LDAPS },
797         { BER_BVC( "none" ),            LDAP_BACK_F_NONE },
798         { BER_BVNULL,                   0 }
799 };
800
801 static slap_verbmasks t_f_mode[] = {
802         { BER_BVC( "yes" ),             LDAP_BACK_F_T_F },
803         { BER_BVC( "discover" ),        LDAP_BACK_F_T_F_DISCOVER },
804         { BER_BVC( "no" ),              LDAP_BACK_F_NONE },
805         { BER_BVNULL,                   0 }
806 };
807
808 static slap_verbmasks cancel_mode[] = {
809         { BER_BVC( "ignore" ),          LDAP_BACK_F_CANCEL_IGNORE },
810         { BER_BVC( "exop" ),            LDAP_BACK_F_CANCEL_EXOP },
811         { BER_BVC( "exop-discover" ),   LDAP_BACK_F_CANCEL_EXOP_DISCOVER },
812         { BER_BVC( "abandon" ),         LDAP_BACK_F_CANCEL_ABANDON },
813         { BER_BVNULL,                   0 }
814 };
815
816 static slap_verbmasks onerr_mode[] = {
817         { BER_BVC( "stop" ),            META_BACK_F_ONERR_STOP },
818         { BER_BVC( "report" ),  META_BACK_F_ONERR_REPORT },
819         { BER_BVC( "continue" ),                LDAP_BACK_F_NONE },
820         { BER_BVNULL,                   0 }
821 };
822
823 /* see enum in slap.h */
824 static slap_cf_aux_table timeout_table[] = {
825         { BER_BVC("bind="),     SLAP_OP_BIND * sizeof( time_t ),        'u', 0, NULL },
826         /* unbind makes no sense */
827         { BER_BVC("add="),      SLAP_OP_ADD * sizeof( time_t ),         'u', 0, NULL },
828         { BER_BVC("delete="),   SLAP_OP_DELETE * sizeof( time_t ),      'u', 0, NULL },
829         { BER_BVC("modrdn="),   SLAP_OP_MODRDN * sizeof( time_t ),      'u', 0, NULL },
830         { BER_BVC("modify="),   SLAP_OP_MODIFY * sizeof( time_t ),      'u', 0, NULL },
831         { BER_BVC("compare="),  SLAP_OP_COMPARE * sizeof( time_t ),     'u', 0, NULL },
832         { BER_BVC("search="),   SLAP_OP_SEARCH * sizeof( time_t ),      'u', 0, NULL },
833         /* abandon makes little sense */
834 #if 0   /* not implemented yet */
835         { BER_BVC("extended="), SLAP_OP_EXTENDED * sizeof( time_t ),    'u', 0, NULL },
836 #endif
837         { BER_BVNULL, 0, 0, 0, NULL }
838 };
839
840 static int
841 meta_back_cf_gen( ConfigArgs *c )
842 {
843         metainfo_t      *mi = ( metainfo_t * )c->be->be_private;
844         int i, rc = 0;
845
846         assert( mi != NULL );
847
848         if ( c->op == SLAP_CONFIG_EMIT ) {
849                 return 1;
850         } else if ( c->op == LDAP_MOD_DELETE ) {
851                 return 1;
852         }
853
854         switch( c->type ) {
855         case LDAP_BACK_CFG_URI: {
856                 LDAPURLDesc     *ludp;
857                 struct berval   dn;
858                 int             j;
859
860                 metatarget_t    *mt;
861
862                 char            **uris = NULL;
863
864                 if ( c->be->be_nsuffix == NULL ) {
865                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
866                                 "the suffix must be defined before any target" );
867                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
868                         return 1;
869                 }
870
871                 i = mi->mi_ntargets++;
872
873                 mi->mi_targets = ( metatarget_t ** )ch_realloc( mi->mi_targets,
874                         sizeof( metatarget_t * ) * mi->mi_ntargets );
875                 if ( mi->mi_targets == NULL ) {
876                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
877                                 "out of memory while storing server name"
878                                 " in \"%s <protocol>://<server>[:port]/<naming context>\"",
879                                 c->argv[0] );
880                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
881                         return 1;
882                 }
883
884                 if ( meta_back_new_target( &mi->mi_targets[ i ] ) != 0 ) {
885                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
886                                 "unable to init server"
887                                 " in \"%s <protocol>://<server>[:port]/<naming context>\"",
888                                 c->argv[0] );
889                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
890                         return 1;
891                 }
892
893                 mt = mi->mi_targets[ i ];
894
895                 mt->mt_rebind_f = mi->mi_rebind_f;
896                 mt->mt_urllist_f = mi->mi_urllist_f;
897                 mt->mt_urllist_p = mt;
898
899                 mt->mt_nretries = mi->mi_nretries;
900                 mt->mt_quarantine = mi->mi_quarantine;
901                 if ( META_BACK_QUARANTINE( mi ) ) {
902                         ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
903                 }
904                 mt->mt_flags = mi->mi_flags;
905                 mt->mt_version = mi->mi_version;
906 #ifdef SLAPD_META_CLIENT_PR
907                 mt->mt_ps = mi->mi_ps;
908 #endif /* SLAPD_META_CLIENT_PR */
909                 mt->mt_network_timeout = mi->mi_network_timeout;
910                 mt->mt_bind_timeout = mi->mi_bind_timeout;
911                 for ( j = 0; j < SLAP_OP_LAST; j++ ) {
912                         mt->mt_timeout[ j ] = mi->mi_timeout[ j ];
913                 }
914
915                 for ( j = 1; j < c->argc; j++ ) {
916                         char    **tmpuris = ldap_str2charray( c->argv[ j ], "\t" );
917
918                         if ( tmpuris == NULL ) {
919                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
920                                         "unable to parse URIs #%d"
921                                         " in \"%s <protocol>://<server>[:port]/<naming context>\"",
922                                         j-1, c->argv[0] );
923                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
924                                 return 1;
925                         }
926
927                         if ( j == 0 ) {
928                                 uris = tmpuris;
929
930                         } else {
931                                 ldap_charray_merge( &uris, tmpuris );
932                                 ldap_charray_free( tmpuris );
933                         }
934                 }
935
936                 for ( j = 0; uris[ j ] != NULL; j++ ) {
937                         char *tmpuri = NULL;
938
939                         /*
940                          * uri MUST be legal!
941                          */
942                         if ( ldap_url_parselist_ext( &ludp, uris[ j ], "\t",
943                                         LDAP_PVT_URL_PARSE_NONE ) != LDAP_SUCCESS
944                                 || ludp->lud_next != NULL )
945                         {
946                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
947                                         "unable to parse URI #%d"
948                                         " in \"%s <protocol>://<server>[:port]/<naming context>\"",
949                                         j-1, c->argv[0] );
950                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
951                                 ldap_charray_free( uris );
952                                 return 1;
953                         }
954
955                         if ( j == 0 ) {
956
957                                 /*
958                                  * uri MUST have the <dn> part!
959                                  */
960                                 if ( ludp->lud_dn == NULL ) {
961                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
962                                                 "missing <naming context> "
963                                                 " in \"%s <protocol>://<server>[:port]/<naming context>\"",
964                                                 c->argv[0] );
965                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
966                                         ldap_free_urllist( ludp );
967                                         ldap_charray_free( uris );
968                                         return 1;
969                                 }
970
971                                 /*
972                                  * copies and stores uri and suffix
973                                  */
974                                 ber_str2bv( ludp->lud_dn, 0, 0, &dn );
975                                 rc = dnPrettyNormal( NULL, &dn, &mt->mt_psuffix,
976                                         &mt->mt_nsuffix, NULL );
977                                 if ( rc != LDAP_SUCCESS ) {
978                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
979                                                 "target DN is invalid \"%s\"",
980                                                 c->argv[1] );
981                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
982                                         ldap_free_urllist( ludp );
983                                         ldap_charray_free( uris );
984                                         return( 1 );
985                                 }
986
987                                 ludp->lud_dn[ 0 ] = '\0';
988
989                                 switch ( ludp->lud_scope ) {
990                                 case LDAP_SCOPE_DEFAULT:
991                                         mt->mt_scope = LDAP_SCOPE_SUBTREE;
992                                         break;
993
994                                 case LDAP_SCOPE_SUBTREE:
995                                 case LDAP_SCOPE_SUBORDINATE:
996                                         mt->mt_scope = ludp->lud_scope;
997                                         break;
998
999                                 default:
1000                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1001                                                 "invalid scope for target \"%s\"",
1002                                                 c->argv[1] );
1003                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1004                                         ldap_free_urllist( ludp );
1005                                         ldap_charray_free( uris );
1006                                         return( 1 );
1007                                 }
1008
1009                         } else {
1010                                 /* check all, to apply the scope check on the first one */
1011                                 if ( ludp->lud_dn != NULL && ludp->lud_dn[ 0 ] != '\0' ) {
1012                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1013                                                 "multiple URIs must have no DN part" );
1014                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1015                                         ldap_free_urllist( ludp );
1016                                         ldap_charray_free( uris );
1017                                         return( 1 );
1018
1019                                 }
1020                         }
1021
1022                         tmpuri = ldap_url_list2urls( ludp );
1023                         ldap_free_urllist( ludp );
1024                         if ( tmpuri == NULL ) {
1025                                 snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" );
1026                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1027                                 ldap_charray_free( uris );
1028                                 return( 1 );
1029                         }
1030                         ldap_memfree( uris[ j ] );
1031                         uris[ j ] = tmpuri;
1032                 }
1033
1034                 mt->mt_uri = ldap_charray2str( uris, " " );
1035                 ldap_charray_free( uris );
1036                 if ( mt->mt_uri == NULL) {
1037                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" );
1038                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1039                         return( 1 );
1040                 }
1041
1042                 /*
1043                  * uri MUST be a branch of suffix!
1044                  */
1045                 for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
1046                         if ( dnIsSuffix( &mt->mt_nsuffix, &c->be->be_nsuffix[ j ] ) ) {
1047                                 break;
1048                         }
1049                 }
1050
1051                 if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
1052                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1053                                 "<naming context> of URI must be within the naming context of this database." );
1054                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1055                         return 1;
1056                 }
1057         } break;
1058         case LDAP_BACK_CFG_SUBTREE_EX:
1059         case LDAP_BACK_CFG_SUBTREE_IN:
1060         /* subtree-exclude */
1061                 i = mi->mi_ntargets - 1;
1062
1063                 if ( i < 0 ) {
1064                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1065                                 "need \"uri\" directive first" );
1066                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1067                         return 1;
1068                 }
1069
1070                 if ( meta_subtree_config( mi->mi_targets[ i ], c )) {
1071                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1072                         return 1;
1073                 }
1074                 break;
1075
1076         case LDAP_BACK_CFG_DEFAULT_T:
1077         /* default target directive */
1078                 i = mi->mi_ntargets - 1;
1079
1080                 if ( c->argc == 1 ) {
1081                         if ( i < 0 ) {
1082                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1083                                         "\"%s\" alone must be inside a \"uri\" directive",
1084                                         c->argv[0] );
1085                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1086                                 return 1;
1087                         }
1088                         mi->mi_defaulttarget = i;
1089
1090                 } else {
1091                         if ( strcasecmp( c->argv[ 1 ], "none" ) == 0 ) {
1092                                 if ( i >= 0 ) {
1093                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1094                                                 "\"%s none\" should go before uri definitions",
1095                                                 c->argv[0] );
1096                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1097                                 }
1098                                 mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
1099
1100                         } else {
1101
1102                                 if ( lutil_atoi( &mi->mi_defaulttarget, c->argv[ 1 ] ) != 0
1103                                         || mi->mi_defaulttarget < 0
1104                                         || mi->mi_defaulttarget >= i - 1 )
1105                                 {
1106                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1107                                                 "illegal target number %d",
1108                                                 mi->mi_defaulttarget );
1109                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1110                                         return 1;
1111                                 }
1112                         }
1113                 }
1114                 break;
1115
1116         case LDAP_BACK_CFG_DNCACHE_TTL:
1117         /* ttl of dn cache */
1118                 if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) {
1119                         mi->mi_cache.ttl = META_DNCACHE_FOREVER;
1120
1121                 } else if ( strcasecmp( c->argv[ 1 ], "disabled" ) == 0 ) {
1122                         mi->mi_cache.ttl = META_DNCACHE_DISABLED;
1123
1124                 } else {
1125                         unsigned long   t;
1126
1127                         if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1128                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1129                                         "unable to parse dncache ttl \"%s\"",
1130                                         c->argv[ 1 ] );
1131                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1132                                 return 1;
1133                         }
1134                         mi->mi_cache.ttl = (time_t)t;
1135                 }
1136                 break;
1137
1138         case LDAP_BACK_CFG_NETWORK_TIMEOUT: {
1139         /* network timeout when connecting to ldap servers */
1140                 unsigned long   t;
1141                 time_t          *tp = mi->mi_ntargets ?
1142                                 &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_network_timeout
1143                                 : &mi->mi_network_timeout;
1144
1145                 if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
1146                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1147                                 "unable to parse network timeout \"%s\"",
1148                                 c->argv[ 1 ] );
1149                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1150                         return 1;
1151                 }
1152
1153                 *tp = (time_t)t;
1154                 } break;
1155
1156         case LDAP_BACK_CFG_IDLE_TIMEOUT: {
1157         /* idle timeout when connecting to ldap servers */
1158                 unsigned long   t;
1159
1160                 if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
1161                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1162                                 "unable to parse idle timeout \"%s\"",
1163                                 c->argv[ 1 ] );
1164                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1165                         return 1;
1166
1167                 }
1168                 mi->mi_idle_timeout = (time_t)t;
1169                 } break;
1170
1171         case LDAP_BACK_CFG_CONN_TTL: {
1172         /* conn ttl */
1173                 unsigned long   t;
1174
1175                 if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
1176                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1177                                 "unable to parse conn ttl \"%s\"",
1178                                 c->argv[ 1 ] );
1179                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1180                         return 1;
1181
1182                 }
1183                 mi->mi_conn_ttl = (time_t)t;
1184                 } break;
1185
1186         case LDAP_BACK_CFG_BIND_TIMEOUT: {
1187         /* bind timeout when connecting to ldap servers */
1188                 unsigned long   t;
1189                 struct timeval  *tp = mi->mi_ntargets ?
1190                                 &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_bind_timeout
1191                                 : &mi->mi_bind_timeout;
1192
1193                 if ( lutil_atoul( &t, c->argv[ 1 ] ) != 0 ) {
1194                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1195                                 "unable to parse bind timeout \"%s\"",
1196                                 c->argv[ 1 ] );
1197                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1198                         return 1;
1199
1200                 }
1201
1202                 tp->tv_sec = t/1000000;
1203                 tp->tv_usec = t%1000000;
1204                 } break;
1205
1206         case LDAP_BACK_CFG_ACL_AUTHCDN:
1207         /* name to use for meta_back_group */
1208
1209                 i = mi->mi_ntargets - 1;
1210                 if ( i < 0 ) {
1211                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1212                                 "need \"uri\" directive first" );
1213                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1214                         return 1;
1215                 }
1216
1217                 if ( strcasecmp( c->argv[ 0 ], "binddn" ) == 0 ) {
1218                         Debug( LDAP_DEBUG_ANY, "%s: "
1219                                 "\"binddn\" statement is deprecated; "
1220                                 "use \"acl-authcDN\" instead\n",
1221                                 c->log, 0, 0 );
1222                         /* FIXME: some day we'll need to throw an error */
1223                 }
1224
1225                 ber_memfree_x( c->value_dn.bv_val, NULL );
1226                 mi->mi_targets[ i ]->mt_binddn = c->value_ndn;
1227                 BER_BVZERO( &c->value_dn );
1228                 BER_BVZERO( &c->value_ndn );
1229                 break;
1230
1231         case LDAP_BACK_CFG_ACL_PASSWD:
1232         /* password to use for meta_back_group */
1233                 i = mi->mi_ntargets - 1;
1234
1235                 if ( i < 0 ) {
1236                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1237                                 "need \"uri\" directive first" );
1238                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1239                         return 1;
1240                 }
1241
1242                 if ( strcasecmp( c->argv[ 0 ], "bindpw" ) == 0 ) {
1243                         Debug( LDAP_DEBUG_ANY, "%s "
1244                                 "\"bindpw\" statement is deprecated; "
1245                                 "use \"acl-passwd\" instead\n",
1246                                 c->log, 0, 0 );
1247                         /* FIXME: some day we'll need to throw an error */
1248                 }
1249
1250                 ber_str2bv( c->argv[ 1 ], 0L, 1, &mi->mi_targets[ i ]->mt_bindpw );
1251                 break;
1252
1253         case LDAP_BACK_CFG_REBIND: {
1254         /* save bind creds for referral rebinds? */
1255                 unsigned        *flagsp = mi->mi_ntargets ?
1256                                 &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
1257                                 : &mi->mi_flags;
1258
1259                 if ( c->argc == 1 || c->value_int ) {
1260                         *flagsp |= LDAP_BACK_F_SAVECRED;
1261                 } else {
1262                         *flagsp &= ~LDAP_BACK_F_SAVECRED;
1263                 }
1264                 } break;
1265
1266         case LDAP_BACK_CFG_CHASE: {
1267                 unsigned        *flagsp = mi->mi_ntargets ?
1268                                 &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
1269                                 : &mi->mi_flags;
1270
1271                 if ( c->argc == 1 || c->value_int ) {
1272                         *flagsp |= LDAP_BACK_F_CHASE_REFERRALS;
1273                 } else {
1274                         *flagsp &= ~LDAP_BACK_F_CHASE_REFERRALS;
1275                 }
1276                 } break;
1277
1278         case LDAP_BACK_CFG_TLS: {
1279                 unsigned        *flagsp = mi->mi_ntargets ?
1280                                 &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
1281                                 : &mi->mi_flags;
1282
1283                 i = verb_to_mask( c->argv[1], tls_mode );
1284                 if ( BER_BVISNULL( &tls_mode[i].word ) ) {
1285                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1286                                 "%s unknown argument \"%s\"",
1287                                 c->argv[0], c->argv[1] );
1288                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1289                         return 1;
1290                 }
1291
1292                 if ( c->argc > 2 ) {
1293                         metatarget_t    *mt = NULL;
1294
1295                         if ( mi->mi_ntargets - 1 < 0 ) {
1296                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1297                                         "need \"uri\" directive first" );
1298                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1299                                 return 1;
1300                         }
1301
1302                         mt = mi->mi_targets[ mi->mi_ntargets - 1 ];
1303
1304                         for ( i = 2; i < c->argc; i++ ) {
1305                                 if ( bindconf_tls_parse( c->argv[i], &mt->mt_tls ))
1306                                         return 1;
1307                         }
1308                         bindconf_tls_defaults( &mt->mt_tls );
1309                 }
1310                 } break;
1311
1312         case LDAP_BACK_CFG_T_F: {
1313                 unsigned        *flagsp = mi->mi_ntargets ?
1314                                 &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
1315                                 : &mi->mi_flags;
1316                 slap_mask_t mask;
1317
1318                 i = verb_to_mask( c->argv[1], t_f_mode );
1319                 if ( BER_BVISNULL( &t_f_mode[i].word ) ) {
1320                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1321                                 "%s unknown argument \"%s\"",
1322                                 c->argv[0], c->argv[1] );
1323                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1324                         return 1;
1325                 }
1326                 mask = t_f_mode[i].mask;
1327
1328                 *flagsp &= ~LDAP_BACK_F_T_F_MASK2;
1329                 *flagsp |= mask;
1330                 } break;
1331
1332         case LDAP_BACK_CFG_ONERR: {
1333         /* onerr? */
1334                 slap_mask_t mask;
1335
1336                 i = verb_to_mask( c->argv[1], onerr_mode );
1337                 if ( BER_BVISNULL( &onerr_mode[i].word ) ) {
1338                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1339                                 "%s unknown argument \"%s\"",
1340                                 c->argv[0], c->argv[1] );
1341                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1342                         return 1;
1343                 }
1344                 mask = onerr_mode[i].mask;
1345
1346                 mi->mi_flags &= ~META_BACK_F_ONERR_MASK;
1347                 mi->mi_flags |= mask;
1348                 } break;
1349
1350         case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
1351         /* bind-defer? */
1352                 if ( c->argc == 1 || c->value_int ) {
1353                         mi->mi_flags |= META_BACK_F_DEFER_ROOTDN_BIND;
1354                 } else {
1355                         mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
1356                 }
1357                 break;
1358
1359         case LDAP_BACK_CFG_SINGLECONN:
1360         /* single-conn? */
1361                 if ( mi->mi_ntargets > 0 ) {
1362                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1363                                 "\"%s\" must appear before target definitions",
1364                                 c->argv[0] );
1365                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1366                         return( 1 );
1367                 }
1368                 if ( c->value_int ) {
1369                         mi->mi_flags |= LDAP_BACK_F_SINGLECONN;
1370                 } else {
1371                         mi->mi_flags &= ~LDAP_BACK_F_SINGLECONN;
1372                 }
1373                 break;
1374
1375         case LDAP_BACK_CFG_USETEMP:
1376         /* use-temporaries? */
1377                 if ( mi->mi_ntargets > 0 ) {
1378                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1379                                 "\"%s\" must appear before target definitions",
1380                                 c->argv[0] );
1381                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1382                         return( 1 );
1383                 }
1384                 if ( c->value_int ) {
1385                         mi->mi_flags |= LDAP_BACK_F_USE_TEMPORARIES;
1386                 } else {
1387                         mi->mi_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
1388                 }
1389                 break;
1390
1391         case LDAP_BACK_CFG_CONNPOOLMAX:
1392         /* privileged connections pool max size ? */
1393                 if ( mi->mi_ntargets > 0 ) {
1394                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1395                                 "\"%s\" must appear before target definitions",
1396                                 c->argv[0] );
1397                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1398                         return( 1 );
1399                 }
1400
1401                 if ( c->value_int < LDAP_BACK_CONN_PRIV_MIN
1402                         || c->value_int > LDAP_BACK_CONN_PRIV_MAX )
1403                 {
1404                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1405                                 "invalid max size " "of privileged "
1406                                 "connections pool \"%s\" "
1407                                 "in \"conn-pool-max <n> "
1408                                 "(must be between %d and %d)\"",
1409                                 c->argv[ 1 ],
1410                                 LDAP_BACK_CONN_PRIV_MIN,
1411                                 LDAP_BACK_CONN_PRIV_MAX );
1412                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1413                         return 1;
1414                 }
1415                 mi->mi_conn_priv_max = c->value_int;
1416                 break;
1417
1418         case LDAP_BACK_CFG_CANCEL: {
1419                 unsigned        *flagsp = mi->mi_ntargets ?
1420                                 &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
1421                                 : &mi->mi_flags;
1422                 slap_mask_t mask;
1423
1424                 i = verb_to_mask( c->argv[1], cancel_mode );
1425                 if ( BER_BVISNULL( &cancel_mode[i].word ) ) {
1426                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1427                                 "%s unknown argument \"%s\"",
1428                                 c->argv[0], c->argv[1] );
1429                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1430                         return 1;
1431                 }
1432                 mask = t_f_mode[i].mask;
1433
1434                 *flagsp &= ~LDAP_BACK_F_CANCEL_MASK2;
1435                 *flagsp |= mask;
1436                 } break;;
1437
1438         case LDAP_BACK_CFG_TIMEOUT: {
1439                 time_t  *tv = mi->mi_ntargets ?
1440                                 mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_timeout
1441                                 : mi->mi_timeout;
1442
1443                 for ( i = 1; i < c->argc; i++ ) {
1444                         if ( isdigit( (unsigned char) c->argv[ i ][ 0 ] ) ) {
1445                                 int             j;
1446                                 unsigned        u;
1447
1448                                 if ( lutil_atoux( &u, c->argv[ i ], 0 ) != 0 ) {
1449                                         snprintf( c->cr_msg, sizeof( c->cr_msg),
1450                                                 "unable to parse timeout \"%s\"",
1451                                                 c->argv[ i ] );
1452                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1453                                         return 1;
1454                                 }
1455
1456                                 for ( j = 0; j < SLAP_OP_LAST; j++ ) {
1457                                         tv[ j ] = u;
1458                                 }
1459
1460                                 continue;
1461                         }
1462
1463                         if ( slap_cf_aux_table_parse( c->argv[ i ], tv, timeout_table, "slapd-meta timeout" ) ) {
1464                                 snprintf( c->cr_msg, sizeof( c->cr_msg),
1465                                         "unable to parse timeout \"%s\"",
1466                                         c->argv[ i ] );
1467                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1468                                 return 1;
1469                         }
1470                 }
1471                 } break;
1472
1473         case LDAP_BACK_CFG_PSEUDOROOTDN:
1474         /* name to use as pseudo-root dn */
1475                 i = mi->mi_ntargets - 1;
1476
1477                 if ( i < 0 ) {
1478                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1479                                 "need \"uri\" directive first" );
1480                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1481                         return 1;
1482                 }
1483
1484                 /*
1485                  * exact replacement:
1486                  *
1487
1488 idassert-bind   bindmethod=simple
1489                 binddn=<pseudorootdn>
1490                 credentials=<pseudorootpw>
1491                 mode=none
1492                 flags=non-prescriptive
1493 idassert-authzFrom      "dn:<rootdn>"
1494
1495                  * so that only when authc'd as <rootdn> the proxying occurs
1496                  * rebinding as the <pseudorootdn> without proxyAuthz.
1497                  */
1498
1499                 Debug( LDAP_DEBUG_ANY,
1500                         "%s: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; "
1501                         "use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n",
1502                         c->log, 0, 0 );
1503
1504                 {
1505                         char    binddn[ SLAP_TEXT_BUFLEN ];
1506                         char    *cargv[] = {
1507                                 "idassert-bind",
1508                                 "bindmethod=simple",
1509                                 NULL,
1510                                 "mode=none",
1511                                 "flags=non-prescriptive",
1512                                 NULL
1513                         };
1514                         char **oargv;
1515                         int oargc;
1516                         int     cargc = 5;
1517                         int     rc;
1518
1519
1520                         if ( BER_BVISNULL( &c->be->be_rootndn ) ) {
1521                                 Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootpw\": \"rootdn\" must be defined first.\n",
1522                                         c->log, 0, 0 );
1523                                 return 1;
1524                         }
1525
1526                         if ( sizeof( binddn ) <= (unsigned) snprintf( binddn,
1527                                         sizeof( binddn ), "binddn=%s", c->argv[ 1 ] ))
1528                         {
1529                                 Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootdn\" too long.\n",
1530                                         c->log, 0, 0 );
1531                                 return 1;
1532                         }
1533                         cargv[ 2 ] = binddn;
1534
1535                         oargv = c->argv;
1536                         oargc = c->argc;
1537                         c->argv = cargv;
1538                         c->argc = cargc;
1539                         rc = mi->mi_ldap_extra->idassert_parse( c, &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert );
1540                         c->argv = oargv;
1541                         c->argc = oargc;
1542                         if ( rc == 0 ) {
1543                                 struct berval   bv;
1544
1545                                 if ( mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz != NULL ) {
1546                                         Debug( LDAP_DEBUG_ANY, "%s: \"idassert-authzFrom\" already defined (discarded).\n",
1547                                                 c->log, 0, 0 );
1548                                         ber_bvarray_free( mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz );
1549                                         mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz = NULL;
1550                                 }
1551
1552                                 assert( !BER_BVISNULL( &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authcDN ) );
1553
1554                                 bv.bv_len = STRLENOF( "dn:" ) + c->be->be_rootndn.bv_len;
1555                                 bv.bv_val = ber_memalloc( bv.bv_len + 1 );
1556                                 AC_MEMCPY( bv.bv_val, "dn:", STRLENOF( "dn:" ) );
1557                                 AC_MEMCPY( &bv.bv_val[ STRLENOF( "dn:" ) ], c->be->be_rootndn.bv_val, c->be->be_rootndn.bv_len + 1 );
1558
1559                                 ber_bvarray_add( &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert_authz, &bv );
1560                         }
1561
1562                         return rc;
1563                 }
1564                 break;
1565
1566         case LDAP_BACK_CFG_PSEUDOROOTPW:
1567         /* password to use as pseudo-root */
1568                 i = mi->mi_ntargets - 1;
1569
1570                 if ( i < 0 ) {
1571                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1572                                 "need \"uri\" directive first" );
1573                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1574                         return 1;
1575                 }
1576
1577                 Debug( LDAP_DEBUG_ANY,
1578                         "%s: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; "
1579                         "use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n",
1580                         c->log, 0, 0 );
1581
1582                 if ( BER_BVISNULL( &mi->mi_targets[ i ]->mt_idassert_authcDN ) ) {
1583                         Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootpw\": \"pseudorootdn\" must be defined first.\n",
1584                                 c->log, 0, 0 );
1585                         return 1;
1586                 }
1587
1588                 if ( !BER_BVISNULL( &mi->mi_targets[ i ]->mt_idassert_passwd ) ) {
1589                         memset( mi->mi_targets[ i ]->mt_idassert_passwd.bv_val, 0,
1590                                 mi->mi_targets[ i ]->mt_idassert_passwd.bv_len );
1591                         ber_memfree( mi->mi_targets[ i ]->mt_idassert_passwd.bv_val );
1592                 }
1593                 ber_str2bv( c->argv[ 1 ], 0, 1, &mi->mi_targets[ i ]->mt_idassert_passwd );
1594
1595         case LDAP_BACK_CFG_IDASSERT_BIND:
1596         /* idassert-bind */
1597                 if ( mi->mi_ntargets == 0 ) {
1598                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1599                                 "need \"uri\" directive first" );
1600                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1601                         return 1;
1602                 }
1603
1604                 rc = mi->mi_ldap_extra->idassert_parse( c, &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert );
1605                 break;
1606
1607         case LDAP_BACK_CFG_IDASSERT_AUTHZFROM:
1608         /* idassert-authzFrom */
1609                 if ( mi->mi_ntargets == 0 ) {
1610                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1611                                 "need \"uri\" directive first" );
1612                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1613                         return 1;
1614                 }
1615
1616                 rc = mi->mi_ldap_extra->idassert_authzfrom_parse( c, &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_idassert );
1617                 break;
1618
1619         case LDAP_BACK_CFG_QUARANTINE: {
1620         /* quarantine */
1621                 slap_retry_info_t       *ri = mi->mi_ntargets ?
1622                                 &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_quarantine
1623                                 : &mi->mi_quarantine;
1624
1625                 if ( ( mi->mi_ntargets == 0 && META_BACK_QUARANTINE( mi ) )
1626                         || ( mi->mi_ntargets > 0 && META_BACK_TGT_QUARANTINE( mi->mi_targets[ mi->mi_ntargets - 1 ] ) ) )
1627                 {
1628                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1629                                 "quarantine already defined" );
1630                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1631                         return 1;
1632                 }
1633
1634                 if ( ri != &mi->mi_quarantine ) {
1635                         ri->ri_interval = NULL;
1636                         ri->ri_num = NULL;
1637                 }
1638
1639                 if ( mi->mi_ntargets > 0 && !META_BACK_QUARANTINE( mi ) ) {
1640                         ldap_pvt_thread_mutex_init( &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_quarantine_mutex );
1641                 }
1642
1643                 if ( mi->mi_ldap_extra->retry_info_parse( c->argv[ 1 ], ri, c->cr_msg, sizeof( c->cr_msg ) ) ) {
1644                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1645                         return 1;
1646                 }
1647
1648                 if ( mi->mi_ntargets == 0 ) {
1649                         mi->mi_flags |= LDAP_BACK_F_QUARANTINE;
1650
1651                 } else {
1652                         mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags |= LDAP_BACK_F_QUARANTINE;
1653                 }
1654                 } break;
1655
1656 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
1657         case LDAP_BACK_CFG_ST_REQUEST: {
1658         /* session tracking request */
1659                 unsigned        *flagsp = mi->mi_ntargets ?
1660                                 &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
1661                                 : &mi->mi_flags;
1662
1663                 if ( c->value_int ) {
1664                         *flagsp |= LDAP_BACK_F_ST_REQUEST;
1665                 } else {
1666                         *flagsp &= ~LDAP_BACK_F_ST_REQUEST;
1667                 }
1668                 } break;
1669 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
1670
1671         case LDAP_BACK_CFG_SUFFIXM: {
1672         /* dn massaging */
1673                 BackendDB       *tmp_bd;
1674                 struct berval   dn, nvnc, pvnc, nrnc, prnc;
1675                 int j;
1676
1677                 i = mi->mi_ntargets - 1;
1678                 if ( i < 0 ) {
1679                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1680                                 "need \"uri\" directive first" );
1681                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1682                         return 1;
1683                 }
1684
1685                 /*
1686                  * syntax:
1687                  *
1688                  *      suffixmassage <suffix> <massaged suffix>
1689                  *
1690                  * the <suffix> field must be defined as a valid suffix
1691                  * (or suffixAlias?) for the current database;
1692                  * the <massaged suffix> shouldn't have already been
1693                  * defined as a valid suffix or suffixAlias for the
1694                  * current server
1695                  */
1696
1697                 ber_str2bv( c->argv[ 1 ], 0, 0, &dn );
1698                 if ( dnPrettyNormal( NULL, &dn, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
1699                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1700                                 "suffix \"%s\" is invalid",
1701                                 c->argv[1] );
1702                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1703                         return 1;
1704                 }
1705
1706                 for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
1707                         if ( dnIsSuffix( &nvnc, &c->be->be_nsuffix[ 0 ] ) ) {
1708                                 break;
1709                         }
1710                 }
1711
1712                 if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
1713                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1714                                 "suffix \"%s\" must be within the database naming context",
1715                                 c->argv[1] );
1716                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1717                         free( pvnc.bv_val );
1718                         free( nvnc.bv_val );
1719                         return 1;
1720                 }
1721
1722                 ber_str2bv( c->argv[ 2 ], 0, 0, &dn );
1723                 if ( dnPrettyNormal( NULL, &dn, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
1724                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1725                                 "massaged suffix \"%s\" is invalid",
1726                                 c->argv[2] );
1727                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1728                         free( pvnc.bv_val );
1729                         free( nvnc.bv_val );
1730                         return 1;
1731                 }
1732
1733                 tmp_bd = select_backend( &nrnc, 0 );
1734                 if ( tmp_bd != NULL && tmp_bd->be_private == c->be->be_private ) {
1735                         Debug( LDAP_DEBUG_ANY,
1736         "%s: warning: <massaged suffix> \"%s\" resolves to this database, in "
1737         "\"suffixMassage <suffix> <massaged suffix>\"\n",
1738                                 c->log, prnc.bv_val, 0 );
1739                 }
1740
1741                 /*
1742                  * The suffix massaging is emulated by means of the
1743                  * rewrite capabilities
1744                  */
1745                 rc = suffix_massage_config( mi->mi_targets[ i ]->mt_rwmap.rwm_rw,
1746                                 &pvnc, &nvnc, &prnc, &nrnc );
1747
1748                 free( pvnc.bv_val );
1749                 free( nvnc.bv_val );
1750                 free( prnc.bv_val );
1751                 free( nrnc.bv_val );
1752
1753                 return rc;
1754                 }
1755
1756         case LDAP_BACK_CFG_REWRITE:
1757         /* rewrite stuff ... */
1758                 i = mi->mi_ntargets - 1;
1759
1760                 if ( i < 0 ) {
1761                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1762                                 "need \"uri\" directive first" );
1763                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1764                         return 1;
1765                 }
1766
1767                 return rewrite_parse( mi->mi_targets[ i ]->mt_rwmap.rwm_rw,
1768                                 c->fname, c->lineno, c->argc, c->argv );
1769
1770         case LDAP_BACK_CFG_MAP:
1771         /* objectclass/attribute mapping */
1772                 i = mi->mi_ntargets - 1;
1773
1774                 if ( i < 0 ) {
1775                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1776                                 "need \"uri\" directive first" );
1777                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1778                         return 1;
1779                 }
1780
1781                 return ldap_back_map_config( c, &mi->mi_targets[ i ]->mt_rwmap.rwm_oc,
1782                                 &mi->mi_targets[ i ]->mt_rwmap.rwm_at );
1783
1784         case LDAP_BACK_CFG_NRETRIES: {
1785                 int             nretries = META_RETRY_UNDEFINED;
1786
1787                 i = mi->mi_ntargets - 1;
1788
1789                 if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) {
1790                         nretries = META_RETRY_FOREVER;
1791
1792                 } else if ( strcasecmp( c->argv[ 1 ], "never" ) == 0 ) {
1793                         nretries = META_RETRY_NEVER;
1794
1795                 } else {
1796                         if ( lutil_atoi( &nretries, c->argv[ 1 ] ) != 0 ) {
1797                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1798                                         "unable to parse nretries {never|forever|<retries>}: \"%s\"",
1799                                         c->argv[ 1 ] );
1800                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1801                                 return 1;
1802                         }
1803                 }
1804
1805                 if ( i < 0 ) {
1806                         mi->mi_nretries = nretries;
1807
1808                 } else {
1809                         mi->mi_targets[ i ]->mt_nretries = nretries;
1810                 }
1811                 } break;
1812
1813         case LDAP_BACK_CFG_VERSION: {
1814                 int     *version = mi->mi_ntargets ?
1815                                 &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_version
1816                                 : &mi->mi_version;
1817
1818                 if ( *version != 0 && ( *version < LDAP_VERSION_MIN || *version > LDAP_VERSION_MAX ) ) {
1819                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1820                                 "unsupported protocol version \"%s\"",
1821                                 c->argv[ 1 ] );
1822                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1823                         return 1;
1824                 }
1825                 } break;
1826
1827         case LDAP_BACK_CFG_NOREFS: {
1828         /* do not return search references */
1829                 unsigned        *flagsp = mi->mi_ntargets ?
1830                                 &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
1831                                 : &mi->mi_flags;
1832
1833                 if ( c->value_int ) {
1834                         *flagsp |= LDAP_BACK_F_NOREFS;
1835                 } else {
1836                         *flagsp &= ~LDAP_BACK_F_NOREFS;
1837                 }
1838                 } break;
1839
1840         case LDAP_BACK_CFG_NOUNDEFFILTER: {
1841         /* do not propagate undefined search filters */
1842                 unsigned        *flagsp = mi->mi_ntargets ?
1843                                 &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_flags
1844                                 : &mi->mi_flags;
1845
1846                 if ( c->value_int ) {
1847                         *flagsp |= LDAP_BACK_F_NOUNDEFFILTER;
1848                 } else {
1849                         *flagsp &= ~LDAP_BACK_F_NOUNDEFFILTER;
1850                 }
1851                 } break;
1852
1853 #ifdef SLAPD_META_CLIENT_PR
1854         case LDAP_BACK_CFG_CLIENT_PR: {
1855                 int *ps = mi->mi_ntargets ?
1856                                 &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_ps
1857                                 : &mi->mi_ps;
1858
1859                 if ( strcasecmp( c->argv[ 1 ], "accept-unsolicited" ) == 0 ) {
1860                         *ps = META_CLIENT_PR_ACCEPT_UNSOLICITED;
1861
1862                 } else if ( strcasecmp( c->argv[ 1 ], "disable" ) == 0 ) {
1863                         *ps = META_CLIENT_PR_DISABLE;
1864
1865                 } else if ( lutil_atoi( ps, c->argv[ 1 ] ) || *ps < -1 ) {
1866                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1867                                 "unable to parse client-pr {accept-unsolicited|disable|<size>}: \"%s\"",
1868                                 c->argv[ 1 ] );
1869                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1870                         return( 1 );
1871                 }
1872                 } break;
1873 #endif /* SLAPD_META_CLIENT_PR */
1874
1875         /* anything else */
1876         default:
1877                 return SLAP_CONF_UNKNOWN;
1878         }
1879
1880         return rc;
1881 }
1882
1883 int
1884 meta_back_init_cf( BackendInfo *bi )
1885 {
1886         int                     rc;
1887         AttributeDescription    *ad = NULL;
1888         const char              *text;
1889
1890         /* Make sure we don't exceed the bits reserved for userland */
1891         config_check_userland( LDAP_BACK_CFG_LAST );
1892
1893         bi->bi_cf_ocs = metaocs;
1894
1895         rc = config_register_schema( metacfg, metaocs );
1896         if ( rc ) {
1897                 return rc;
1898         }
1899
1900         /* setup olcDbAclPasswd and olcDbIDAssertPasswd
1901          * to be base64-encoded when written in LDIF form;
1902          * basically, we don't care if it fails */
1903         rc = slap_str2ad( "olcDbACLPasswd", &ad, &text );
1904         if ( rc ) {
1905                 Debug( LDAP_DEBUG_ANY, "config_back_initialize: "
1906                         "warning, unable to get \"olcDbACLPasswd\" "
1907                         "attribute description: %d: %s\n",
1908                         rc, text, 0 );
1909         } else {
1910                 (void)ldif_must_b64_encode_register( ad->ad_cname.bv_val,
1911                         ad->ad_type->sat_oid );
1912         }
1913
1914         ad = NULL;
1915         rc = slap_str2ad( "olcDbIDAssertPasswd", &ad, &text );
1916         if ( rc ) {
1917                 Debug( LDAP_DEBUG_ANY, "config_back_initialize: "
1918                         "warning, unable to get \"olcDbIDAssertPasswd\" "
1919                         "attribute description: %d: %s\n",
1920                         rc, text, 0 );
1921         } else {
1922                 (void)ldif_must_b64_encode_register( ad->ad_cname.bv_val,
1923                         ad->ad_type->sat_oid );
1924         }
1925
1926         return 0;
1927 }
1928
1929 static int
1930 ldap_back_map_config(
1931                 ConfigArgs *c,
1932                 struct ldapmap  *oc_map,
1933                 struct ldapmap  *at_map )
1934 {
1935         struct ldapmap          *map;
1936         struct ldapmapping      *mapping;
1937         char                    *src, *dst;
1938         int                     is_oc = 0;
1939
1940         if ( strcasecmp( c->argv[ 1 ], "objectclass" ) == 0 ) {
1941                 map = oc_map;
1942                 is_oc = 1;
1943
1944         } else if ( strcasecmp( c->argv[ 1 ], "attribute" ) == 0 ) {
1945                 map = at_map;
1946
1947         } else {
1948                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1949                         "%s unknown argument \"%s\"",
1950                         c->argv[0], c->argv[1] );
1951                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1952                 return 1;
1953         }
1954
1955         if ( !is_oc && map->map == NULL ) {
1956                 /* only init if required */
1957                 ldap_back_map_init( map, &mapping );
1958         }
1959
1960         if ( strcmp( c->argv[ 2 ], "*" ) == 0 ) {
1961                 if ( c->argc < 4 || strcmp( c->argv[ 3 ], "*" ) == 0 ) {
1962                         map->drop_missing = ( c->argc < 4 );
1963                         goto success_return;
1964                 }
1965                 src = dst = c->argv[ 3 ];
1966
1967         } else if ( c->argc < 4 ) {
1968                 src = "";
1969                 dst = c->argv[ 2 ];
1970
1971         } else {
1972                 src = c->argv[ 2 ];
1973                 dst = ( strcmp( c->argv[ 3 ], "*" ) == 0 ? src : c->argv[ 3 ] );
1974         }
1975
1976         if ( ( map == at_map )
1977                 && ( strcasecmp( src, "objectclass" ) == 0
1978                         || strcasecmp( dst, "objectclass" ) == 0 ) )
1979         {
1980                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1981                         "objectclass attribute cannot be mapped" );
1982                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1983                 return 1;
1984         }
1985
1986         mapping = (struct ldapmapping *)ch_calloc( 2,
1987                 sizeof(struct ldapmapping) );
1988         if ( mapping == NULL ) {
1989                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1990                         "out of memory" );
1991                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1992                 return 1;
1993         }
1994         ber_str2bv( src, 0, 1, &mapping[ 0 ].src );
1995         ber_str2bv( dst, 0, 1, &mapping[ 0 ].dst );
1996         mapping[ 1 ].src = mapping[ 0 ].dst;
1997         mapping[ 1 ].dst = mapping[ 0 ].src;
1998
1999         /*
2000          * schema check
2001          */
2002         if ( is_oc ) {
2003                 if ( src[ 0 ] != '\0' ) {
2004                         if ( oc_bvfind( &mapping[ 0 ].src ) == NULL ) {
2005                                 Debug( LDAP_DEBUG_ANY,
2006         "warning, source objectClass '%s' should be defined in schema\n",
2007                                         c->log, src, 0 );
2008
2009                                 /*
2010                                  * FIXME: this should become an err
2011                                  */
2012                                 goto error_return;
2013                         }
2014                 }
2015
2016                 if ( oc_bvfind( &mapping[ 0 ].dst ) == NULL ) {
2017                         Debug( LDAP_DEBUG_ANY,
2018         "warning, destination objectClass '%s' is not defined in schema\n",
2019                                 c->log, dst, 0 );
2020                 }
2021         } else {
2022                 int                     rc;
2023                 const char              *text = NULL;
2024                 AttributeDescription    *ad = NULL;
2025
2026                 if ( src[ 0 ] != '\0' ) {
2027                         rc = slap_bv2ad( &mapping[ 0 ].src, &ad, &text );
2028                         if ( rc != LDAP_SUCCESS ) {
2029                                 Debug( LDAP_DEBUG_ANY,
2030         "warning, source attributeType '%s' should be defined in schema\n",
2031                                         c->log, src, 0 );
2032
2033                                 /*
2034                                  * FIXME: this should become an err
2035                                  */
2036                                 /*
2037                                  * we create a fake "proxied" ad
2038                                  * and add it here.
2039                                  */
2040
2041                                 rc = slap_bv2undef_ad( &mapping[ 0 ].src,
2042                                                 &ad, &text, SLAP_AD_PROXIED );
2043                                 if ( rc != LDAP_SUCCESS ) {
2044                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
2045                                                 "source attributeType \"%s\": %d (%s)",
2046                                                 src, rc, text ? text : "" );
2047                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2048                                         goto error_return;
2049                                 }
2050                         }
2051
2052                         ad = NULL;
2053                 }
2054
2055                 rc = slap_bv2ad( &mapping[ 0 ].dst, &ad, &text );
2056                 if ( rc != LDAP_SUCCESS ) {
2057                         Debug( LDAP_DEBUG_ANY,
2058         "warning, destination attributeType '%s' is not defined in schema\n",
2059                                 c->log, dst, 0 );
2060
2061                         /*
2062                          * we create a fake "proxied" ad
2063                          * and add it here.
2064                          */
2065
2066                         rc = slap_bv2undef_ad( &mapping[ 0 ].dst,
2067                                         &ad, &text, SLAP_AD_PROXIED );
2068                         if ( rc != LDAP_SUCCESS ) {
2069                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
2070                                         "destination attributeType \"%s\": %d (%s)\n",
2071                                         dst, rc, text ? text : "" );
2072                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2073                                 return 1;
2074                         }
2075                 }
2076         }
2077
2078         if ( (src[ 0 ] != '\0' && avl_find( map->map, (caddr_t)&mapping[ 0 ], mapping_cmp ) != NULL)
2079                         || avl_find( map->remap, (caddr_t)&mapping[ 1 ], mapping_cmp ) != NULL)
2080         {
2081                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
2082                         "duplicate mapping found." );
2083                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
2084                 goto error_return;
2085         }
2086
2087         if ( src[ 0 ] != '\0' ) {
2088                 avl_insert( &map->map, (caddr_t)&mapping[ 0 ],
2089                                         mapping_cmp, mapping_dup );
2090         }
2091         avl_insert( &map->remap, (caddr_t)&mapping[ 1 ],
2092                                 mapping_cmp, mapping_dup );
2093
2094 success_return:;
2095         return 0;
2096
2097 error_return:;
2098         if ( mapping ) {
2099                 ch_free( mapping[ 0 ].src.bv_val );
2100                 ch_free( mapping[ 0 ].dst.bv_val );
2101                 ch_free( mapping );
2102         }
2103
2104         return 1;
2105 }
2106
2107
2108 #ifdef ENABLE_REWRITE
2109 static char *
2110 suffix_massage_regexize( const char *s )
2111 {
2112         char *res, *ptr;
2113         const char *p, *r;
2114         int i;
2115
2116         if ( s[ 0 ] == '\0' ) {
2117                 return ch_strdup( "^(.+)$" );
2118         }
2119
2120         for ( i = 0, p = s;
2121                         ( r = strchr( p, ',' ) ) != NULL;
2122                         p = r + 1, i++ )
2123                 ;
2124
2125         res = ch_calloc( sizeof( char ),
2126                         strlen( s )
2127                         + STRLENOF( "((.+),)?" )
2128                         + STRLENOF( "[ ]?" ) * i
2129                         + STRLENOF( "$" ) + 1 );
2130
2131         ptr = lutil_strcopy( res, "((.+),)?" );
2132         for ( i = 0, p = s;
2133                         ( r = strchr( p, ',' ) ) != NULL;
2134                         p = r + 1 , i++ ) {
2135                 ptr = lutil_strncopy( ptr, p, r - p + 1 );
2136                 ptr = lutil_strcopy( ptr, "[ ]?" );
2137
2138                 if ( r[ 1 ] == ' ' ) {
2139                         r++;
2140                 }
2141         }
2142         ptr = lutil_strcopy( ptr, p );
2143         ptr[ 0 ] = '$';
2144         ptr++;
2145         ptr[ 0 ] = '\0';
2146
2147         return res;
2148 }
2149
2150 static char *
2151 suffix_massage_patternize( const char *s, const char *p )
2152 {
2153         ber_len_t       len;
2154         char            *res, *ptr;
2155
2156         len = strlen( p );
2157
2158         if ( s[ 0 ] == '\0' ) {
2159                 len++;
2160         }
2161
2162         res = ch_calloc( sizeof( char ), len + STRLENOF( "%1" ) + 1 );
2163         if ( res == NULL ) {
2164                 return NULL;
2165         }
2166
2167         ptr = lutil_strcopy( res, ( p[ 0 ] == '\0' ? "%2" : "%1" ) );
2168         if ( s[ 0 ] == '\0' ) {
2169                 ptr[ 0 ] = ',';
2170                 ptr++;
2171         }
2172         lutil_strcopy( ptr, p );
2173
2174         return res;
2175 }
2176
2177 int
2178 suffix_massage_config(
2179                 struct rewrite_info *info,
2180                 struct berval *pvnc,
2181                 struct berval *nvnc,
2182                 struct berval *prnc,
2183                 struct berval *nrnc
2184 )
2185 {
2186         char *rargv[ 5 ];
2187         int line = 0;
2188
2189         rargv[ 0 ] = "rewriteEngine";
2190         rargv[ 1 ] = "on";
2191         rargv[ 2 ] = NULL;
2192         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
2193
2194         rargv[ 0 ] = "rewriteContext";
2195         rargv[ 1 ] = "default";
2196         rargv[ 2 ] = NULL;
2197         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
2198
2199         rargv[ 0 ] = "rewriteRule";
2200         rargv[ 1 ] = suffix_massage_regexize( pvnc->bv_val );
2201         rargv[ 2 ] = suffix_massage_patternize( pvnc->bv_val, prnc->bv_val );
2202         rargv[ 3 ] = ":";
2203         rargv[ 4 ] = NULL;
2204         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
2205         ch_free( rargv[ 1 ] );
2206         ch_free( rargv[ 2 ] );
2207
2208         if ( BER_BVISEMPTY( pvnc ) ) {
2209                 rargv[ 0 ] = "rewriteRule";
2210                 rargv[ 1 ] = "^$";
2211                 rargv[ 2 ] = prnc->bv_val;
2212                 rargv[ 3 ] = ":";
2213                 rargv[ 4 ] = NULL;
2214                 rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
2215         }
2216
2217         rargv[ 0 ] = "rewriteContext";
2218         rargv[ 1 ] = "searchEntryDN";
2219         rargv[ 2 ] = NULL;
2220         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
2221
2222         rargv[ 0 ] = "rewriteRule";
2223         rargv[ 1 ] = suffix_massage_regexize( prnc->bv_val );
2224         rargv[ 2 ] = suffix_massage_patternize( prnc->bv_val, pvnc->bv_val );
2225         rargv[ 3 ] = ":";
2226         rargv[ 4 ] = NULL;
2227         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
2228         ch_free( rargv[ 1 ] );
2229         ch_free( rargv[ 2 ] );
2230
2231         if ( BER_BVISEMPTY( prnc ) ) {
2232                 rargv[ 0 ] = "rewriteRule";
2233                 rargv[ 1 ] = "^$";
2234                 rargv[ 2 ] = pvnc->bv_val;
2235                 rargv[ 3 ] = ":";
2236                 rargv[ 4 ] = NULL;
2237                 rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
2238         }
2239
2240         /* backward compatibility */
2241         rargv[ 0 ] = "rewriteContext";
2242         rargv[ 1 ] = "searchResult";
2243         rargv[ 2 ] = "alias";
2244         rargv[ 3 ] = "searchEntryDN";
2245         rargv[ 4 ] = NULL;
2246         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
2247
2248         rargv[ 0 ] = "rewriteContext";
2249         rargv[ 1 ] = "matchedDN";
2250         rargv[ 2 ] = "alias";
2251         rargv[ 3 ] = "searchEntryDN";
2252         rargv[ 4 ] = NULL;
2253         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
2254
2255         rargv[ 0 ] = "rewriteContext";
2256         rargv[ 1 ] = "searchAttrDN";
2257         rargv[ 2 ] = "alias";
2258         rargv[ 3 ] = "searchEntryDN";
2259         rargv[ 4 ] = NULL;
2260         rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
2261
2262         /* NOTE: this corresponds to #undef'ining RWM_REFERRAL_REWRITE;
2263          * see servers/slapd/overlays/rwm.h for details */
2264         rargv[ 0 ] = "rewriteContext";
2265         rargv[ 1 ] = "referralAttrDN";
2266         rargv[ 2 ] = NULL;
2267         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
2268
2269         rargv[ 0 ] = "rewriteContext";
2270         rargv[ 1 ] = "referralDN";
2271         rargv[ 2 ] = NULL;
2272         rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
2273
2274         return 0;
2275 }
2276 #endif /* ENABLE_REWRITE */
2277