]> git.sur5r.net Git - openldap/blob - servers/slapd/controls.c
Additional manageDIT changes.
[openldap] / servers / slapd / controls.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2005 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15
16 #include "portable.h"
17
18 #include <stdio.h>
19
20 #include <ac/string.h>
21 #include <ac/socket.h>
22
23 #include "slap.h"
24
25 #include "../../libraries/liblber/lber-int.h"
26
27 static SLAP_CTRL_PARSE_FN parseAssert;
28 static SLAP_CTRL_PARSE_FN parsePreRead;
29 static SLAP_CTRL_PARSE_FN parsePostRead;
30 static SLAP_CTRL_PARSE_FN parseProxyAuthz;
31 static SLAP_CTRL_PARSE_FN parseManageDIT;
32 static SLAP_CTRL_PARSE_FN parseManageDSAit;
33 static SLAP_CTRL_PARSE_FN parseModifyIncrement;
34 static SLAP_CTRL_PARSE_FN parseNoOp;
35 static SLAP_CTRL_PARSE_FN parsePagedResults;
36 #ifdef LDAP_DEVEL
37 static SLAP_CTRL_PARSE_FN parseSortedResults;
38 #endif
39 static SLAP_CTRL_PARSE_FN parseValuesReturnFilter;
40 static SLAP_CTRL_PARSE_FN parsePermissiveModify;
41 static SLAP_CTRL_PARSE_FN parseDomainScope;
42 static SLAP_CTRL_PARSE_FN parseTreeDelete;
43 static SLAP_CTRL_PARSE_FN parseSearchOptions;
44 static SLAP_CTRL_PARSE_FN parseSubentries;
45
46 #undef sc_mask /* avoid conflict with Irix 6.5 <sys/signal.h> */
47
48 const struct berval slap_pre_read_bv = BER_BVC(LDAP_CONTROL_PRE_READ);
49 const struct berval slap_post_read_bv = BER_BVC(LDAP_CONTROL_POST_READ);
50
51 struct slap_control_ids slap_cids;
52
53 struct slap_control {
54         /* Control OID */
55         char *sc_oid;
56
57         /* The controlID for this control */
58         int sc_cid;
59
60         /* Operations supported by control */
61         slap_mask_t sc_mask;
62
63         /* Extended operations supported by control */
64         char **sc_extendedops;
65
66         /* Control parsing callback */
67         SLAP_CTRL_PARSE_FN *sc_parse;
68
69         LDAP_SLIST_ENTRY(slap_control) sc_next;
70 };
71
72 static LDAP_SLIST_HEAD(ControlsList, slap_control) controls_list
73         = LDAP_SLIST_HEAD_INITIALIZER(&controls_list);
74
75 /*
76  * all known request control OIDs should be added to this list
77  */
78 /*
79  * NOTE: initialize num_known_controls to 1 so that cid = 0 always
80  * addresses an undefined control; this allows to safely test for 
81  * well known controls even if they are not registered, e.g. if 
82  * they get moved to modules.  An example is sc_LDAPsync, which 
83  * is implemented in the syncprov overlay and thus, if configured 
84  * as dynamic module, may not be registered.  One side effect is that 
85  * slap_known_controls[0] == NULL, so it should always be used 
86  * starting from 1.
87  * FIXME: should we define the "undefined control" oid?
88  */
89 char *slap_known_controls[SLAP_MAX_CIDS+1];
90 static int num_known_controls = 1;
91
92 static char *proxy_authz_extops[] = {
93         LDAP_EXOP_MODIFY_PASSWD,
94         LDAP_EXOP_X_WHO_AM_I,
95         NULL
96 };
97
98 static struct slap_control control_defs[] = {
99         {  LDAP_CONTROL_ASSERT,
100                 (int)offsetof(struct slap_control_ids, sc_assert),
101                 SLAP_CTRL_HIDE|SLAP_CTRL_ACCESS, NULL,
102                 parseAssert, LDAP_SLIST_ENTRY_INITIALIZER(next) },
103         { LDAP_CONTROL_PRE_READ,
104                 (int)offsetof(struct slap_control_ids, sc_preRead),
105                 SLAP_CTRL_HIDE|SLAP_CTRL_DELETE|SLAP_CTRL_MODIFY|SLAP_CTRL_RENAME, NULL,
106                 parsePreRead, LDAP_SLIST_ENTRY_INITIALIZER(next) },
107         { LDAP_CONTROL_POST_READ,
108                 (int)offsetof(struct slap_control_ids, sc_postRead),
109                 SLAP_CTRL_HIDE|SLAP_CTRL_ADD|SLAP_CTRL_MODIFY|SLAP_CTRL_RENAME, NULL,
110                 parsePostRead, LDAP_SLIST_ENTRY_INITIALIZER(next) },
111         { LDAP_CONTROL_VALUESRETURNFILTER,
112                 (int)offsetof(struct slap_control_ids, sc_valuesReturnFilter),
113                 SLAP_CTRL_GLOBAL|SLAP_CTRL_SEARCH, NULL,
114                 parseValuesReturnFilter, LDAP_SLIST_ENTRY_INITIALIZER(next) },
115         { LDAP_CONTROL_PAGEDRESULTS,
116                 (int)offsetof(struct slap_control_ids, sc_pagedResults),
117                 SLAP_CTRL_SEARCH, NULL,
118                 parsePagedResults, LDAP_SLIST_ENTRY_INITIALIZER(next) },
119 #ifdef LDAP_DEVEL
120         { LDAP_CONTROL_SORTREQUEST,
121                 (int)offsetof(struct slap_control_ids, sc_sortedResults),
122                 SLAP_CTRL_GLOBAL|SLAP_CTRL_SEARCH|SLAP_CTRL_HIDE, NULL,
123                 parseSortedResults, LDAP_SLIST_ENTRY_INITIALIZER(next) },
124 #endif
125 #ifdef LDAP_CONTROL_X_DOMAIN_SCOPE
126         { LDAP_CONTROL_X_DOMAIN_SCOPE,
127                 (int)offsetof(struct slap_control_ids, sc_domainScope),
128                 SLAP_CTRL_GLOBAL|SLAP_CTRL_SEARCH, NULL,
129                 parseDomainScope, LDAP_SLIST_ENTRY_INITIALIZER(next) },
130 #endif
131 #ifdef LDAP_CONTROL_X_PERMISSIVE_MODIFY
132         { LDAP_CONTROL_X_PERMISSIVE_MODIFY,
133                 (int)offsetof(struct slap_control_ids, sc_permissiveModify),
134                 SLAP_CTRL_MODIFY, NULL,
135                 parsePermissiveModify, LDAP_SLIST_ENTRY_INITIALIZER(next) },
136 #endif
137 #ifdef SLAP_CONTROL_X_TREE_DELETE
138         { LDAP_CONTROL_X_TREE_DELETE,
139                 (int)offsetof(struct slap_control_ids, sc_treeDelete),
140                 SLAP_CTRL_HIDE|SLAP_CTRL_DELETE, NULL,
141                 parseTreeDelete, LDAP_SLIST_ENTRY_INITIALIZER(next) },
142 #endif
143 #ifdef LDAP_CONTROL_X_SEARCH_OPTIONS
144         { LDAP_CONTROL_X_SEARCH_OPTIONS,
145                 (int)offsetof(struct slap_control_ids, sc_searchOptions),
146                 SLAP_CTRL_GLOBAL|SLAP_CTRL_SEARCH, NULL,
147                 parseSearchOptions, LDAP_SLIST_ENTRY_INITIALIZER(next) },
148 #endif
149 #ifdef LDAP_CONTROL_SUBENTRIES
150         { LDAP_CONTROL_SUBENTRIES,
151                 (int)offsetof(struct slap_control_ids, sc_subentries),
152                 SLAP_CTRL_SEARCH, NULL,
153                 parseSubentries, LDAP_SLIST_ENTRY_INITIALIZER(next) },
154 #endif
155         { LDAP_CONTROL_NOOP,
156                 (int)offsetof(struct slap_control_ids, sc_noOp),
157                 SLAP_CTRL_HIDE|SLAP_CTRL_ACCESS, NULL,
158                 parseNoOp, LDAP_SLIST_ENTRY_INITIALIZER(next) },
159 #ifdef LDAP_CONTROL_MODIFY_INCREMENT
160         { LDAP_CONTROL_MODIFY_INCREMENT,
161                 (int)offsetof(struct slap_control_ids, sc_modifyIncrement),
162                 SLAP_CTRL_HIDE|SLAP_CTRL_MODIFY, NULL,
163                 parseModifyIncrement, LDAP_SLIST_ENTRY_INITIALIZER(next) },
164 #endif
165         { LDAP_CONTROL_MANAGEDIT,
166                 (int)offsetof(struct slap_control_ids, sc_manageDIT),
167                 SLAP_CTRL_GLOBAL|SLAP_CTRL_UPDATE, NULL,
168                 parseManageDIT, LDAP_SLIST_ENTRY_INITIALIZER(next) },
169         { LDAP_CONTROL_MANAGEDSAIT,
170                 (int)offsetof(struct slap_control_ids, sc_manageDSAit),
171                 SLAP_CTRL_ACCESS, NULL,
172                 parseManageDSAit, LDAP_SLIST_ENTRY_INITIALIZER(next) },
173         { LDAP_CONTROL_PROXY_AUTHZ,
174                 (int)offsetof(struct slap_control_ids, sc_proxyAuthz),
175                 SLAP_CTRL_GLOBAL|SLAP_CTRL_ACCESS, proxy_authz_extops,
176                 parseProxyAuthz, LDAP_SLIST_ENTRY_INITIALIZER(next) },
177         { NULL, 0, 0, NULL, 0, LDAP_SLIST_ENTRY_INITIALIZER(next) }
178 };
179
180 /*
181  * Register a supported control.
182  *
183  * This can be called by an OpenLDAP plugin or, indirectly, by a
184  * SLAPI plugin calling slapi_register_supported_control().
185  */
186 int
187 register_supported_control(const char *controloid,
188         slap_mask_t controlmask,
189         char **controlexops,
190         SLAP_CTRL_PARSE_FN *controlparsefn,
191         int *controlcid)
192 {
193         struct slap_control *sc;
194         int i;
195
196         if ( num_known_controls >= SLAP_MAX_CIDS ) {
197                 Debug( LDAP_DEBUG_ANY, "Too many controls registered."
198                         " Recompile slapd with SLAP_MAX_CIDS defined > %d\n",
199                 SLAP_MAX_CIDS, 0, 0 );
200                 return LDAP_OTHER;
201         }
202
203         if ( controloid == NULL ) return LDAP_PARAM_ERROR;
204
205         /* sanity check - should never happen */
206         for ( i = 0; slap_known_controls[ i ]; i++ ) {
207                 if ( strcmp( controloid, slap_known_controls[ i ] ) == 0 ) {
208                         Debug( LDAP_DEBUG_ANY,
209                                 "Control %s already registered.\n",
210                                 controloid, 0, 0 );
211                         return LDAP_PARAM_ERROR;
212                 }
213         }
214
215         sc = (struct slap_control *)SLAP_MALLOC( sizeof( *sc ) );
216         if ( sc == NULL ) return LDAP_NO_MEMORY;
217
218         sc->sc_oid = ch_strdup( controloid );
219         sc->sc_mask = controlmask;
220         if ( controlexops != NULL ) {
221                 sc->sc_extendedops = ldap_charray_dup( controlexops );
222                 if ( sc->sc_extendedops == NULL ) {
223                         ch_free( sc );
224                         return LDAP_NO_MEMORY;
225                 }
226         } else {
227                 sc->sc_extendedops = NULL;
228         }
229         sc->sc_parse = controlparsefn;
230
231         if ( controlcid ) *controlcid = num_known_controls;
232         sc->sc_cid = num_known_controls;
233
234         /* Update slap_known_controls, too. */
235         slap_known_controls[num_known_controls-1] = sc->sc_oid;
236         slap_known_controls[num_known_controls++] = NULL;
237
238         LDAP_SLIST_NEXT( sc, sc_next ) = NULL;
239         LDAP_SLIST_INSERT_HEAD( &controls_list, sc, sc_next );
240         return LDAP_SUCCESS;
241 }
242
243 /*
244  * One-time initialization of internal controls.
245  */
246 int
247 slap_controls_init( void )
248 {
249         int i, rc;
250         struct slap_control *sc;
251
252         rc = LDAP_SUCCESS;
253
254         for ( i = 0; control_defs[i].sc_oid != NULL; i++ ) {
255                 int *cid = (int *)(((char *)&slap_cids) + control_defs[i].sc_cid );
256                 rc = register_supported_control( control_defs[i].sc_oid,
257                         control_defs[i].sc_mask, control_defs[i].sc_extendedops,
258                         control_defs[i].sc_parse, cid );
259                 if ( rc != LDAP_SUCCESS ) break;
260         }
261
262         return rc;
263 }
264
265 /*
266  * Free memory associated with list of supported controls.
267  */
268 void
269 controls_destroy( void )
270 {
271         struct slap_control *sc;
272
273         while ( !LDAP_SLIST_EMPTY(&controls_list) ) {
274                 sc = LDAP_SLIST_FIRST(&controls_list);
275                 LDAP_SLIST_REMOVE_HEAD(&controls_list, sc_next);
276
277                 ch_free( sc->sc_oid );
278                 if ( sc->sc_extendedops != NULL ) {
279                         ldap_charray_free( sc->sc_extendedops );
280                 }
281                 ch_free( sc );
282         }
283 }
284
285 /*
286  * Format the supportedControl attribute of the root DSE,
287  * detailing which controls are supported by the directory
288  * server.
289  */
290 int
291 controls_root_dse_info( Entry *e )
292 {
293         AttributeDescription *ad_supportedControl
294                 = slap_schema.si_ad_supportedControl;
295         struct berval vals[2];
296         struct slap_control *sc;
297
298         vals[1].bv_val = NULL;
299         vals[1].bv_len = 0;
300
301         LDAP_SLIST_FOREACH( sc, &controls_list, sc_next ) {
302                 if( sc->sc_mask & SLAP_CTRL_HIDE ) continue;
303
304                 vals[0].bv_val = sc->sc_oid;
305                 vals[0].bv_len = strlen( sc->sc_oid );
306
307                 if ( attr_merge( e, ad_supportedControl, vals, NULL ) ) {
308                         return -1;
309                 }
310         }
311
312         return 0;
313 }
314
315 /*
316  * Return a list of OIDs and operation masks for supported
317  * controls. Used by SLAPI.
318  */
319 int
320 get_supported_controls(char ***ctrloidsp,
321         slap_mask_t **ctrlmasks)
322 {
323         int i, n;
324         char **oids;
325         slap_mask_t *masks;
326         int rc;
327         struct slap_control *sc;
328
329         n = 0;
330
331         LDAP_SLIST_FOREACH( sc, &controls_list, sc_next ) {
332                 n++;
333         }
334
335         if ( n == 0 ) {
336                 *ctrloidsp = NULL;
337                 *ctrlmasks = NULL;
338                 return LDAP_SUCCESS;
339         }
340
341         oids = (char **)SLAP_MALLOC( (n + 1) * sizeof(char *) );
342         if ( oids == NULL ) {
343                 return LDAP_NO_MEMORY;
344         }
345         masks = (slap_mask_t *)SLAP_MALLOC( (n + 1) * sizeof(slap_mask_t) );
346         if  ( masks == NULL ) {
347                 ch_free( oids );
348                 return LDAP_NO_MEMORY;
349         }
350
351         n = 0;
352
353         LDAP_SLIST_FOREACH( sc, &controls_list, sc_next ) {
354                 oids[n] = ch_strdup( sc->sc_oid );
355                 masks[n] = sc->sc_mask;
356                 n++;
357         }
358         oids[n] = NULL;
359         masks[n] = 0;
360
361         *ctrloidsp = oids;
362         *ctrlmasks = masks;
363
364         return LDAP_SUCCESS;
365 }
366
367 /*
368  * Find a control given its OID.
369  */
370 static struct slap_control *
371 find_ctrl( const char *oid )
372 {
373         struct slap_control *sc;
374
375         LDAP_SLIST_FOREACH( sc, &controls_list, sc_next ) {
376                 if ( strcmp( oid, sc->sc_oid ) == 0 ) {
377                         return sc;
378                 }
379         }
380
381         return NULL;
382 }
383
384 int
385 slap_find_control_id(
386         const char *oid,
387         int *cid )
388 {
389         struct slap_control *ctrl = find_ctrl( oid );
390         if ( ctrl ) {
391                 if ( cid ) *cid = ctrl->sc_cid;
392                 return LDAP_SUCCESS;
393         }
394         return LDAP_CONTROL_NOT_FOUND;
395 }
396
397 int
398 slap_global_control( Operation *op, const char *oid, int *cid )
399 {
400         struct slap_control *ctrl = find_ctrl( oid );
401
402         if ( ctrl == NULL ) {
403                 /* should not be reachable */
404                 Debug( LDAP_DEBUG_ANY,
405                         "slap_global_control: unrecognized control: %s\n",      
406                         oid, 0, 0 );
407                 return LDAP_CONTROL_NOT_FOUND;
408         }
409
410         if ( cid ) *cid = ctrl->sc_cid;
411
412         if ( ( ctrl->sc_mask & SLAP_CTRL_GLOBAL ) ||
413                 ( ( op->o_tag & LDAP_REQ_SEARCH ) &&
414                 ( ctrl->sc_mask & SLAP_CTRL_GLOBAL_SEARCH ) ) )
415         {
416                 return LDAP_COMPARE_TRUE;
417         }
418
419         Debug( LDAP_DEBUG_TRACE,
420                 "slap_global_control: unavailable control: %s\n",      
421                 oid, 0, 0 );
422
423         return LDAP_COMPARE_FALSE;
424 }
425
426 void slap_free_ctrls(
427         Operation *op,
428         LDAPControl **ctrls )
429 {
430         int i;
431
432         for (i=0; ctrls[i]; i++) {
433                 op->o_tmpfree(ctrls[i], op->o_tmpmemctx );
434         }
435         op->o_tmpfree( ctrls, op->o_tmpmemctx );
436 }
437
438 int get_ctrls(
439         Operation *op,
440         SlapReply *rs,
441         int sendres )
442 {
443         int nctrls = 0;
444         ber_tag_t tag;
445         ber_len_t len;
446         char *opaque;
447         BerElement *ber = op->o_ber;
448         struct slap_control *sc;
449         struct berval bv;
450
451         len = ber_pvt_ber_remaining(ber);
452
453         if( len == 0) {
454                 /* no controls */
455                 rs->sr_err = LDAP_SUCCESS;
456                 return rs->sr_err;
457         }
458
459         if(( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) {
460                 if( tag == LBER_ERROR ) {
461                         rs->sr_err = SLAPD_DISCONNECT;
462                         rs->sr_text = "unexpected data in PDU";
463                 }
464
465                 goto return_results;
466         }
467
468         Debug( LDAP_DEBUG_TRACE,
469                 "=> get_ctrls\n", 0, 0, 0 );
470
471         if( op->o_protocol < LDAP_VERSION3 ) {
472                 rs->sr_err = SLAPD_DISCONNECT;
473                 rs->sr_text = "controls require LDAPv3";
474                 goto return_results;
475         }
476
477         /* one for first control, one for termination */
478         op->o_ctrls = op->o_tmpalloc( 2 * sizeof(LDAPControl *), op->o_tmpmemctx );
479
480 #if 0
481         if( op->ctrls == NULL ) {
482                 rs->sr_err = LDAP_NO_MEMORY;
483                 rs->sr_text = "no memory";
484                 goto return_results;
485         }
486 #endif
487
488         op->o_ctrls[nctrls] = NULL;
489
490         /* step through each element */
491         for( tag = ber_first_element( ber, &len, &opaque );
492                 tag != LBER_ERROR;
493                 tag = ber_next_element( ber, &len, opaque ) )
494         {
495                 LDAPControl *c;
496                 LDAPControl **tctrls;
497
498                 c = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
499                 memset(c, 0, sizeof(LDAPControl));
500
501                 /* allocate pointer space for current controls (nctrls)
502                  * + this control + extra NULL
503                  */
504                 tctrls = op->o_tmprealloc( op->o_ctrls,
505                         (nctrls+2) * sizeof(LDAPControl *), op->o_tmpmemctx );
506
507 #if 0
508                 if( tctrls == NULL ) {
509                         ch_free( c );
510                         ldap_controls_free(op->o_ctrls);
511                         op->o_ctrls = NULL;
512
513                         rs->sr_err = LDAP_NO_MEMORY;
514                         rs->sr_text = "no memory";
515                         goto return_results;
516                 }
517 #endif
518                 op->o_ctrls = tctrls;
519
520                 op->o_ctrls[nctrls++] = c;
521                 op->o_ctrls[nctrls] = NULL;
522
523                 tag = ber_scanf( ber, "{m" /*}*/, &bv );
524                 c->ldctl_oid = bv.bv_val;
525
526                 if( tag == LBER_ERROR ) {
527                         Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get oid failed.\n",
528                                 0, 0, 0 );
529
530                         slap_free_ctrls( op, op->o_ctrls );
531                         op->o_ctrls = NULL;
532                         rs->sr_err = SLAPD_DISCONNECT;
533                         rs->sr_text = "decoding controls error";
534                         goto return_results;
535
536                 } else if( c->ldctl_oid == NULL ) {
537                         Debug( LDAP_DEBUG_TRACE,
538                                 "get_ctrls: conn %lu got emtpy OID.\n",
539                                 op->o_connid, 0, 0 );
540
541                         slap_free_ctrls( op, op->o_ctrls );
542                         op->o_ctrls = NULL;
543                         rs->sr_err = LDAP_PROTOCOL_ERROR;
544                         rs->sr_text = "OID field is empty";
545                         goto return_results;
546                 }
547
548                 tag = ber_peek_tag( ber, &len );
549
550                 if( tag == LBER_BOOLEAN ) {
551                         ber_int_t crit;
552                         tag = ber_scanf( ber, "b", &crit );
553
554                         if( tag == LBER_ERROR ) {
555                                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get crit failed.\n",
556                                         0, 0, 0 );
557                                 slap_free_ctrls( op, op->o_ctrls );
558                                 op->o_ctrls = NULL;
559                                 rs->sr_err = SLAPD_DISCONNECT;
560                                 rs->sr_text = "decoding controls error";
561                                 goto return_results;
562                         }
563
564                         c->ldctl_iscritical = (crit != 0);
565                         tag = ber_peek_tag( ber, &len );
566                 }
567
568                 if( tag == LBER_OCTETSTRING ) {
569                         tag = ber_scanf( ber, "m", &c->ldctl_value );
570
571                         if( tag == LBER_ERROR ) {
572                                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: conn %lu: "
573                                         "%s (%scritical): get value failed.\n",
574                                         op->o_connid, c->ldctl_oid,
575                                         c->ldctl_iscritical ? "" : "non" );
576                                 slap_free_ctrls( op, op->o_ctrls );
577                                 op->o_ctrls = NULL;
578                                 rs->sr_err = SLAPD_DISCONNECT;
579                                 rs->sr_text = "decoding controls error";
580                                 goto return_results;
581                         }
582                 }
583
584                 Debug( LDAP_DEBUG_TRACE,
585                         "=> get_ctrls: oid=\"%s\" (%scritical)\n",
586                         c->ldctl_oid, c->ldctl_iscritical ? "" : "non", 0 );
587
588                 sc = find_ctrl( c->ldctl_oid );
589                 if( sc != NULL ) {
590                         /* recognized control */
591                         slap_mask_t tagmask;
592                         switch( op->o_tag ) {
593                         case LDAP_REQ_ADD:
594                                 tagmask = SLAP_CTRL_ADD;
595                                 break;
596                         case LDAP_REQ_BIND:
597                                 tagmask = SLAP_CTRL_BIND;
598                                 break;
599                         case LDAP_REQ_COMPARE:
600                                 tagmask = SLAP_CTRL_COMPARE;
601                                 break;
602                         case LDAP_REQ_DELETE:
603                                 tagmask = SLAP_CTRL_DELETE;
604                                 break;
605                         case LDAP_REQ_MODIFY:
606                                 tagmask = SLAP_CTRL_MODIFY;
607                                 break;
608                         case LDAP_REQ_RENAME:
609                                 tagmask = SLAP_CTRL_RENAME;
610                                 break;
611                         case LDAP_REQ_SEARCH:
612                                 tagmask = SLAP_CTRL_SEARCH;
613                                 break;
614                         case LDAP_REQ_UNBIND:
615                                 tagmask = SLAP_CTRL_UNBIND;
616                                 break;
617                         case LDAP_REQ_ABANDON:
618                                 tagmask = SLAP_CTRL_ABANDON;
619                                 break;
620                         case LDAP_REQ_EXTENDED:
621                                 tagmask=~0L;
622                                 assert( op->ore_reqoid.bv_val != NULL );
623                                 if( sc->sc_extendedops != NULL ) {
624                                         int i;
625                                         for( i=0; sc->sc_extendedops[i] != NULL; i++ ) {
626                                                 if( strcmp( op->ore_reqoid.bv_val,
627                                                         sc->sc_extendedops[i] ) == 0 )
628                                                 {
629                                                         tagmask=0L;
630                                                         break;
631                                                 }
632                                         }
633                                 }
634                                 break;
635                         default:
636                                 rs->sr_err = LDAP_OTHER;
637                                 rs->sr_text = "controls internal error";
638                                 goto return_results;
639                         }
640
641                         if (( sc->sc_mask & tagmask ) == tagmask ) {
642                                 /* available extension */
643                                 int     rc;
644
645                                 if( !sc->sc_parse ) {
646                                         rs->sr_err = LDAP_OTHER;
647                                         rs->sr_text = "not yet implemented";
648                                         goto return_results;
649                                 }
650
651                                 rc = sc->sc_parse( op, rs, c );
652                                 if ( rc ) {
653                                         assert( rc != LDAP_UNAVAILABLE_CRITICAL_EXTENSION );
654                                         rs->sr_err = rc;
655                                         goto return_results;
656                                 }
657
658                         } else if( c->ldctl_iscritical ) {
659                                 /* unavailable CRITICAL control */
660                                 rs->sr_err = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
661                                 rs->sr_text = "critical extension is unavailable";
662                                 goto return_results;
663                         }
664
665                 } else if( c->ldctl_iscritical ) {
666                         /* unrecognized CRITICAL control */
667                         rs->sr_err = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
668                         rs->sr_text = "critical extension is not recognized";
669                         goto return_results;
670                 }
671 next_ctrl:;
672         }
673
674 return_results:
675         Debug( LDAP_DEBUG_TRACE,
676                 "<= get_ctrls: n=%d rc=%d err=\"%s\"\n",
677                 nctrls, rs->sr_err, rs->sr_text ? rs->sr_text : "");
678
679         if( sendres && rs->sr_err != LDAP_SUCCESS ) {
680                 if( rs->sr_err == SLAPD_DISCONNECT ) {
681                         rs->sr_err = LDAP_PROTOCOL_ERROR;
682                         send_ldap_disconnect( op, rs );
683                         rs->sr_err = SLAPD_DISCONNECT;
684                 } else {
685                         send_ldap_result( op, rs );
686                 }
687         }
688
689         return rs->sr_err;
690 }
691
692 static int parseModifyIncrement (
693         Operation *op,
694         SlapReply *rs,
695         LDAPControl *ctrl )
696 {
697 #if 0
698         if ( op->o_modifyIncrement != SLAP_CONTROL_NONE ) {
699                 rs->sr_text = "modifyIncrement control specified multiple times";
700                 return LDAP_PROTOCOL_ERROR;
701         }
702 #endif
703
704         if ( ctrl->ldctl_value.bv_len ) {
705                 rs->sr_text = "modifyIncrement control value not empty";
706                 return LDAP_PROTOCOL_ERROR;
707         }
708
709 #if 0
710         op->o_modifyIncrement = ctrl->ldctl_iscritical
711                 ? SLAP_CONTROL_CRITICAL
712                 : SLAP_CONTROL_NONCRITICAL;
713 #endif
714
715         return LDAP_SUCCESS;
716 }
717
718 static int parseManageDIT (
719         Operation *op,
720         SlapReply *rs,
721         LDAPControl *ctrl )
722 {
723         if ( op->o_managedit != SLAP_CONTROL_NONE ) {
724                 rs->sr_text = "manageDIT control specified multiple times";
725                 return LDAP_PROTOCOL_ERROR;
726         }
727
728         if ( ctrl->ldctl_value.bv_len ) {
729                 rs->sr_text = "manageDIT control value not empty";
730                 return LDAP_PROTOCOL_ERROR;
731         }
732
733         op->o_managedit = ctrl->ldctl_iscritical
734                 ? SLAP_CONTROL_CRITICAL
735                 : SLAP_CONTROL_NONCRITICAL;
736
737         return LDAP_SUCCESS;
738 }
739
740 static int parseManageDSAit (
741         Operation *op,
742         SlapReply *rs,
743         LDAPControl *ctrl )
744 {
745         if ( op->o_managedsait != SLAP_CONTROL_NONE ) {
746                 rs->sr_text = "manageDSAit control specified multiple times";
747                 return LDAP_PROTOCOL_ERROR;
748         }
749
750         if ( ctrl->ldctl_value.bv_len ) {
751                 rs->sr_text = "manageDSAit control value not empty";
752                 return LDAP_PROTOCOL_ERROR;
753         }
754
755         op->o_managedsait = ctrl->ldctl_iscritical
756                 ? SLAP_CONTROL_CRITICAL
757                 : SLAP_CONTROL_NONCRITICAL;
758
759         return LDAP_SUCCESS;
760 }
761
762 static int parseProxyAuthz (
763         Operation *op,
764         SlapReply *rs,
765         LDAPControl *ctrl )
766 {
767         int             rc;
768         struct berval   dn = BER_BVNULL;
769
770         if ( op->o_proxy_authz != SLAP_CONTROL_NONE ) {
771                 rs->sr_text = "proxy authorization control specified multiple times";
772                 return LDAP_PROTOCOL_ERROR;
773         }
774
775         op->o_proxy_authz = ctrl->ldctl_iscritical
776                 ? SLAP_CONTROL_CRITICAL
777                 : SLAP_CONTROL_NONCRITICAL;
778
779         Debug( LDAP_DEBUG_ARGS,
780                 "parseProxyAuthz: conn %lu authzid=\"%s\"\n", 
781                 op->o_connid,
782                 ctrl->ldctl_value.bv_len ?  ctrl->ldctl_value.bv_val : "anonymous",
783                 0 );
784
785         if ( ctrl->ldctl_value.bv_len == 0 ) {
786                 Debug( LDAP_DEBUG_TRACE,
787                         "parseProxyAuthz: conn=%lu anonymous\n", 
788                         op->o_connid, 0, 0 );
789
790                 /* anonymous */
791                 op->o_ndn.bv_val[ 0 ] = '\0';
792                 op->o_ndn.bv_len = 0;
793
794                 op->o_dn.bv_val[ 0 ] = '\0';
795                 op->o_dn.bv_len = 0;
796
797                 return LDAP_SUCCESS;
798         }
799
800         rc = slap_sasl_getdn( op->o_conn, op, &ctrl->ldctl_value,
801                         NULL, &dn, SLAP_GETDN_AUTHZID );
802
803         /* FIXME: empty DN in proxyAuthz control should be legal... */
804         if( rc != LDAP_SUCCESS /* || !dn.bv_len */ ) {
805                 if ( dn.bv_val ) {
806                         ch_free( dn.bv_val );
807                 }
808                 rs->sr_text = "authzId mapping failed";
809                 return LDAP_PROXY_AUTHZ_FAILURE;
810         }
811
812         Debug( LDAP_DEBUG_TRACE,
813                 "parseProxyAuthz: conn=%lu \"%s\"\n", 
814                 op->o_connid,
815                 dn.bv_len ? dn.bv_val : "(NULL)", 0 );
816
817         rc = slap_sasl_authorized( op, &op->o_ndn, &dn );
818
819         if ( rc ) {
820                 ch_free( dn.bv_val );
821                 rs->sr_text = "not authorized to assume identity";
822                 return LDAP_PROXY_AUTHZ_FAILURE;
823         }
824
825         ch_free( op->o_ndn.bv_val );
826         ch_free( op->o_dn.bv_val );
827
828         /*
829          * NOTE: since slap_sasl_getdn() returns a normalized dn,
830          * from now on op->o_dn is normalized
831          */
832         op->o_ndn = dn;
833         ber_dupbv( &op->o_dn, &dn );
834
835
836         Statslog( LDAP_DEBUG_STATS, "%s PROXYAUTHZ dn=\"%s\"\n",
837             op->o_log_prefix, dn.bv_val, 0, 0, 0 );
838
839         return LDAP_SUCCESS;
840 }
841
842 static int parseNoOp (
843         Operation *op,
844         SlapReply *rs,
845         LDAPControl *ctrl )
846 {
847         if ( op->o_noop != SLAP_CONTROL_NONE ) {
848                 rs->sr_text = "noop control specified multiple times";
849                 return LDAP_PROTOCOL_ERROR;
850         }
851
852         if ( ctrl->ldctl_value.bv_len ) {
853                 rs->sr_text = "noop control value not empty";
854                 return LDAP_PROTOCOL_ERROR;
855         }
856
857         op->o_noop = ctrl->ldctl_iscritical
858                 ? SLAP_CONTROL_CRITICAL
859                 : SLAP_CONTROL_NONCRITICAL;
860
861         return LDAP_SUCCESS;
862 }
863
864 static int parsePagedResults (
865         Operation *op,
866         SlapReply *rs,
867         LDAPControl *ctrl )
868 {
869         int             rc = LDAP_SUCCESS;
870         ber_tag_t       tag;
871         ber_int_t       size;
872         BerElement      *ber;
873         struct berval   cookie = BER_BVNULL;
874         PagedResultsState       *ps;
875
876         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
877                 rs->sr_text = "paged results control specified multiple times";
878                 return LDAP_PROTOCOL_ERROR;
879         }
880
881         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
882                 rs->sr_text = "paged results control value is empty (or absent)";
883                 return LDAP_PROTOCOL_ERROR;
884         }
885
886         /* Parse the control value
887          *      realSearchControlValue ::= SEQUENCE {
888          *              size    INTEGER (0..maxInt),
889          *                              -- requested page size from client
890          *                              -- result set size estimate from server
891          *              cookie  OCTET STRING
892          * }
893          */
894         ber = ber_init( &ctrl->ldctl_value );
895         if ( ber == NULL ) {
896                 rs->sr_text = "internal error";
897                 return LDAP_OTHER;
898         }
899
900         tag = ber_scanf( ber, "{im}", &size, &cookie );
901
902         if ( tag == LBER_ERROR ) {
903                 rs->sr_text = "paged results control could not be decoded";
904                 rc = LDAP_PROTOCOL_ERROR;
905                 goto done;
906         }
907
908         if ( size < 0 ) {
909                 rs->sr_text = "paged results control size invalid";
910                 rc = LDAP_PROTOCOL_ERROR;
911                 goto done;
912         }
913
914 #if 0
915         /* defer cookie decoding/checks to backend... */
916         if ( cookie.bv_len ) {
917                 PagedResultsCookie reqcookie;
918                 if( cookie.bv_len != sizeof( reqcookie ) ) {
919                         /* bad cookie */
920                         rs->sr_text = "paged results cookie is invalid";
921                         rc = LDAP_PROTOCOL_ERROR;
922                         goto done;
923                 }
924
925                 AC_MEMCPY( &reqcookie, cookie.bv_val, sizeof( reqcookie ));
926
927                 if ( reqcookie > op->o_pagedresults_state.ps_cookie ) {
928                         /* bad cookie */
929                         rs->sr_text = "paged results cookie is invalid";
930                         rc = LDAP_PROTOCOL_ERROR;
931                         goto done;
932
933                 } else if ( reqcookie < op->o_pagedresults_state.ps_cookie ) {
934                         rs->sr_text = "paged results cookie is invalid or old";
935                         rc = LDAP_UNWILLING_TO_PERFORM;
936                         goto done;
937                 }
938
939         } else {
940                 /* Initial request.  Initialize state. */
941 #if 0
942                 if ( op->o_conn->c_pagedresults_state.ps_cookie != 0 ) {
943                         /* There's another pagedResults control on the
944                          * same connection; reject new pagedResults controls 
945                          * (allowed by RFC2696) */
946                         rs->sr_text = "paged results cookie unavailable; try later";
947                         rc = LDAP_UNWILLING_TO_PERFORM;
948                         goto done;
949                 }
950 #endif
951                 op->o_pagedresults_state.ps_cookie = 0;
952                 op->o_pagedresults_state.ps_count = 0;
953         }
954 #endif
955
956         ps = op->o_tmpalloc( sizeof(PagedResultsState), op->o_tmpmemctx );
957         *ps = op->o_conn->c_pagedresults_state;
958         ps->ps_size = size;
959         op->o_pagedresults_state = ps;
960
961         /* NOTE: according to RFC 2696 3.:
962
963     If the page size is greater than or equal to the sizeLimit value, the
964     server should ignore the control as the request can be satisfied in a
965     single page.
966          
967          * NOTE: this assumes that the op->ors_slimit be set
968          * before the controls are parsed.     
969          */
970                 
971         if ( op->ors_slimit > 0 && size >= op->ors_slimit ) {
972                 op->o_pagedresults = SLAP_CONTROL_IGNORED;
973
974         } else if ( ctrl->ldctl_iscritical ) {
975                 op->o_pagedresults = SLAP_CONTROL_CRITICAL;
976
977         } else {
978                 op->o_pagedresults = SLAP_CONTROL_NONCRITICAL;
979         }
980
981 done:;
982         (void)ber_free( ber, 1 );
983         return rc;
984 }
985
986 #ifdef LDAP_DEVEL
987 static int parseSortedResults (
988         Operation *op,
989         SlapReply *rs,
990         LDAPControl *ctrl )
991 {
992         int             rc = LDAP_SUCCESS;
993
994         if ( op->o_sortedresults != SLAP_CONTROL_NONE ) {
995                 rs->sr_text = "sorted results control specified multiple times";
996                 return LDAP_PROTOCOL_ERROR;
997         }
998
999         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1000                 rs->sr_text = "sorted results control value is empty (or absent)";
1001                 return LDAP_PROTOCOL_ERROR;
1002         }
1003
1004         /* blow off parsing the value */
1005
1006         op->o_sortedresults = ctrl->ldctl_iscritical
1007                 ? SLAP_CONTROL_CRITICAL
1008                 : SLAP_CONTROL_NONCRITICAL;
1009
1010         return rc;
1011 }
1012 #endif
1013
1014 static int parseAssert (
1015         Operation *op,
1016         SlapReply *rs,
1017         LDAPControl *ctrl )
1018 {
1019         BerElement      *ber;
1020         struct berval   fstr = BER_BVNULL;
1021         const char *err_msg = "";
1022
1023         if ( op->o_assert != SLAP_CONTROL_NONE ) {
1024                 rs->sr_text = "assert control specified multiple times";
1025                 return LDAP_PROTOCOL_ERROR;
1026         }
1027
1028         if ( ctrl->ldctl_value.bv_len == 0 ) {
1029                 rs->sr_text = "assert control value is empty (or absent)";
1030                 return LDAP_PROTOCOL_ERROR;
1031         }
1032
1033         ber = ber_init( &(ctrl->ldctl_value) );
1034         if (ber == NULL) {
1035                 rs->sr_text = "assert control: internal error";
1036                 return LDAP_OTHER;
1037         }
1038         
1039         rs->sr_err = get_filter( op, ber, (Filter **)&(op->o_assertion), &rs->sr_text);
1040
1041         if( rs->sr_err != LDAP_SUCCESS ) {
1042                 if( rs->sr_err == SLAPD_DISCONNECT ) {
1043                         rs->sr_err = LDAP_PROTOCOL_ERROR;
1044                         send_ldap_disconnect( op, rs );
1045                         rs->sr_err = SLAPD_DISCONNECT;
1046                 } else {
1047                         send_ldap_result( op, rs );
1048                 }
1049                 if( op->o_assertion != NULL ) {
1050                         filter_free_x( op, op->o_assertion );
1051                 }
1052                 return rs->sr_err;
1053         }
1054
1055 #ifdef LDAP_DEBUG
1056         filter2bv_x( op, op->o_assertion, &fstr );
1057
1058         Debug( LDAP_DEBUG_ARGS, "parseAssert: conn %ld assert: %s\n",
1059                 op->o_connid, fstr.bv_len ? fstr.bv_val : "empty" , 0 );
1060         op->o_tmpfree( fstr.bv_val, op->o_tmpmemctx );
1061 #endif
1062
1063         op->o_assert = ctrl->ldctl_iscritical
1064                 ? SLAP_CONTROL_CRITICAL
1065                 : SLAP_CONTROL_NONCRITICAL;
1066
1067         rs->sr_err = LDAP_SUCCESS;
1068         return LDAP_SUCCESS;
1069 }
1070
1071 static int parsePreRead (
1072         Operation *op,
1073         SlapReply *rs,
1074         LDAPControl *ctrl )
1075 {
1076         ber_len_t siz, off, i;
1077         AttributeName *an = NULL;
1078         BerElement      *ber;
1079
1080         if ( op->o_preread != SLAP_CONTROL_NONE ) {
1081                 rs->sr_text = "preread control specified multiple times";
1082                 return LDAP_PROTOCOL_ERROR;
1083         }
1084
1085         if ( ctrl->ldctl_value.bv_len == 0 ) {
1086                 rs->sr_text = "preread control value is empty (or absent)";
1087                 return LDAP_PROTOCOL_ERROR;
1088         }
1089
1090         ber = ber_init( &(ctrl->ldctl_value) );
1091         if (ber == NULL) {
1092                 rs->sr_text = "preread control: internal error";
1093                 return LDAP_OTHER;
1094         }
1095
1096         siz = sizeof( AttributeName );
1097         off = offsetof( AttributeName, an_name );
1098         if ( ber_scanf( ber, "{M}", &an, &siz, off ) == LBER_ERROR ) {
1099                 rs->sr_text = "preread control: decoding error";
1100                 return LDAP_PROTOCOL_ERROR;
1101         }
1102
1103         for( i=0; i<siz; i++ ) {
1104                 int             rc = LDAP_SUCCESS;
1105                 const char      *dummy = NULL;
1106
1107                 an[i].an_desc = NULL;
1108                 an[i].an_oc = NULL;
1109                 an[i].an_oc_exclude = 0;
1110                 rc = slap_bv2ad( &an[i].an_name, &an[i].an_desc, &dummy );
1111                 if ( rc != LDAP_SUCCESS && ctrl->ldctl_iscritical ) {
1112                         rs->sr_text = dummy
1113                                 ? dummy
1114                                 : "postread control: unknown attributeType";
1115                         return rc;
1116                 }
1117         }
1118
1119         op->o_preread = ctrl->ldctl_iscritical
1120                 ? SLAP_CONTROL_CRITICAL
1121                 : SLAP_CONTROL_NONCRITICAL;
1122
1123         op->o_preread_attrs = an;
1124
1125         rs->sr_err = LDAP_SUCCESS;
1126         return LDAP_SUCCESS;
1127 }
1128
1129 static int parsePostRead (
1130         Operation *op,
1131         SlapReply *rs,
1132         LDAPControl *ctrl )
1133 {
1134         ber_len_t siz, off, i;
1135         AttributeName *an = NULL;
1136         BerElement      *ber;
1137
1138         if ( op->o_postread != SLAP_CONTROL_NONE ) {
1139                 rs->sr_text = "postread control specified multiple times";
1140                 return LDAP_PROTOCOL_ERROR;
1141         }
1142
1143         if ( ctrl->ldctl_value.bv_len == 0 ) {
1144                 rs->sr_text = "postread control value is empty (or absent)";
1145                 return LDAP_PROTOCOL_ERROR;
1146         }
1147
1148         ber = ber_init( &(ctrl->ldctl_value) );
1149         if (ber == NULL) {
1150                 rs->sr_text = "postread control: internal error";
1151                 return LDAP_OTHER;
1152         }
1153
1154         siz = sizeof( AttributeName );
1155         off = offsetof( AttributeName, an_name );
1156         if ( ber_scanf( ber, "{M}", &an, &siz, off ) == LBER_ERROR ) {
1157                 rs->sr_text = "postread control: decoding error";
1158                 return LDAP_PROTOCOL_ERROR;
1159         }
1160
1161         for( i=0; i<siz; i++ ) {
1162                 int             rc = LDAP_SUCCESS;
1163                 const char      *dummy = NULL;
1164
1165                 an[i].an_desc = NULL;
1166                 an[i].an_oc = NULL;
1167                 an[i].an_oc_exclude = 0;
1168                 rc = slap_bv2ad( &an[i].an_name, &an[i].an_desc, &dummy );
1169                 if ( rc != LDAP_SUCCESS && ctrl->ldctl_iscritical ) {
1170                         rs->sr_text = dummy
1171                                 ? dummy
1172                                 : "postread control: unknown attributeType";
1173                         return rc;
1174                 }
1175         }
1176
1177         op->o_postread = ctrl->ldctl_iscritical
1178                 ? SLAP_CONTROL_CRITICAL
1179                 : SLAP_CONTROL_NONCRITICAL;
1180
1181         op->o_postread_attrs = an;
1182
1183         rs->sr_err = LDAP_SUCCESS;
1184         return LDAP_SUCCESS;
1185 }
1186
1187 int parseValuesReturnFilter (
1188         Operation *op,
1189         SlapReply *rs,
1190         LDAPControl *ctrl )
1191 {
1192         BerElement      *ber;
1193         struct berval   fstr = BER_BVNULL;
1194         const char *err_msg = "";
1195
1196         if ( op->o_valuesreturnfilter != SLAP_CONTROL_NONE ) {
1197                 rs->sr_text = "valuesReturnFilter control specified multiple times";
1198                 return LDAP_PROTOCOL_ERROR;
1199         }
1200
1201         if ( ctrl->ldctl_value.bv_len == 0 ) {
1202                 rs->sr_text = "valuesReturnFilter control value is empty (or absent)";
1203                 return LDAP_PROTOCOL_ERROR;
1204         }
1205
1206         ber = ber_init( &(ctrl->ldctl_value) );
1207         if (ber == NULL) {
1208                 rs->sr_text = "internal error";
1209                 return LDAP_OTHER;
1210         }
1211         
1212         rs->sr_err = get_vrFilter( op, ber, (ValuesReturnFilter **)&(op->o_vrFilter), &rs->sr_text);
1213
1214         if( rs->sr_err != LDAP_SUCCESS ) {
1215                 if( rs->sr_err == SLAPD_DISCONNECT ) {
1216                         rs->sr_err = LDAP_PROTOCOL_ERROR;
1217                         send_ldap_disconnect( op, rs );
1218                         rs->sr_err = SLAPD_DISCONNECT;
1219                 } else {
1220                         send_ldap_result( op, rs );
1221                 }
1222                 if( op->o_vrFilter != NULL) vrFilter_free( op, op->o_vrFilter ); 
1223         }
1224 #ifdef LDAP_DEBUG
1225         else {
1226                 vrFilter2bv( op, op->o_vrFilter, &fstr );
1227         }
1228
1229         Debug( LDAP_DEBUG_ARGS, "       vrFilter: %s\n",
1230                 fstr.bv_len ? fstr.bv_val : "empty", 0, 0 );
1231         op->o_tmpfree( fstr.bv_val, op->o_tmpmemctx );
1232 #endif
1233
1234         op->o_valuesreturnfilter = ctrl->ldctl_iscritical
1235                 ? SLAP_CONTROL_CRITICAL
1236                 : SLAP_CONTROL_NONCRITICAL;
1237
1238         rs->sr_err = LDAP_SUCCESS;
1239         return LDAP_SUCCESS;
1240 }
1241
1242 #ifdef LDAP_CONTROL_SUBENTRIES
1243 static int parseSubentries (
1244         Operation *op,
1245         SlapReply *rs,
1246         LDAPControl *ctrl )
1247 {
1248         if ( op->o_subentries != SLAP_CONTROL_NONE ) {
1249                 rs->sr_text = "subentries control specified multiple times";
1250                 return LDAP_PROTOCOL_ERROR;
1251         }
1252
1253         /* FIXME: should use BER library */
1254         if( ( ctrl->ldctl_value.bv_len != 3 )
1255                 || ( ctrl->ldctl_value.bv_val[0] != 0x01 )
1256                 || ( ctrl->ldctl_value.bv_val[1] != 0x01 ))
1257         {
1258                 rs->sr_text = "subentries control value encoding is bogus";
1259                 return LDAP_PROTOCOL_ERROR;
1260         }
1261
1262         op->o_subentries = ctrl->ldctl_iscritical
1263                 ? SLAP_CONTROL_CRITICAL
1264                 : SLAP_CONTROL_NONCRITICAL;
1265
1266         if (ctrl->ldctl_value.bv_val[2]) {
1267                 set_subentries_visibility( op );
1268         }
1269
1270         return LDAP_SUCCESS;
1271 }
1272 #endif
1273
1274 #ifdef LDAP_CONTROL_X_PERMISSIVE_MODIFY
1275 static int parsePermissiveModify (
1276         Operation *op,
1277         SlapReply *rs,
1278         LDAPControl *ctrl )
1279 {
1280         if ( op->o_permissive_modify != SLAP_CONTROL_NONE ) {
1281                 rs->sr_text = "permissiveModify control specified multiple times";
1282                 return LDAP_PROTOCOL_ERROR;
1283         }
1284
1285         if ( ctrl->ldctl_value.bv_len ) {
1286                 rs->sr_text = "permissiveModify control value not empty";
1287                 return LDAP_PROTOCOL_ERROR;
1288         }
1289
1290         op->o_permissive_modify = ctrl->ldctl_iscritical
1291                 ? SLAP_CONTROL_CRITICAL
1292                 : SLAP_CONTROL_NONCRITICAL;
1293
1294         return LDAP_SUCCESS;
1295 }
1296 #endif
1297
1298 #ifdef LDAP_CONTROL_X_DOMAIN_SCOPE
1299 static int parseDomainScope (
1300         Operation *op,
1301         SlapReply *rs,
1302         LDAPControl *ctrl )
1303 {
1304         if ( op->o_domain_scope != SLAP_CONTROL_NONE ) {
1305                 rs->sr_text = "domainScope control specified multiple times";
1306                 return LDAP_PROTOCOL_ERROR;
1307         }
1308
1309         if ( ctrl->ldctl_value.bv_len ) {
1310                 rs->sr_text = "domainScope control value not empty";
1311                 return LDAP_PROTOCOL_ERROR;
1312         }
1313
1314         op->o_domain_scope = ctrl->ldctl_iscritical
1315                 ? SLAP_CONTROL_CRITICAL
1316                 : SLAP_CONTROL_NONCRITICAL;
1317
1318         return LDAP_SUCCESS;
1319 }
1320 #endif
1321
1322 #ifdef LDAP_CONTROL_X_TREE_DELETE
1323 static int parseTreeDelete (
1324         Operation *op,
1325         SlapReply *rs,
1326         LDAPControl *ctrl )
1327 {
1328         if ( op->o_tree_delete != SLAP_CONTROL_NONE ) {
1329                 rs->sr_text = "treeDelete control specified multiple times";
1330                 return LDAP_PROTOCOL_ERROR;
1331         }
1332
1333         if ( ctrl->ldctl_value.bv_len ) {
1334                 rs->sr_text = "treeDelete control value not empty";
1335                 return LDAP_PROTOCOL_ERROR;
1336         }
1337
1338         op->o_tree_delete = ctrl->ldctl_iscritical
1339                 ? SLAP_CONTROL_CRITICAL
1340                 : SLAP_CONTROL_NONCRITICAL;
1341
1342         return LDAP_SUCCESS;
1343 }
1344 #endif
1345
1346 #ifdef LDAP_CONTROL_X_SEARCH_OPTIONS
1347 static int parseSearchOptions (
1348         Operation *op,
1349         SlapReply *rs,
1350         LDAPControl *ctrl )
1351 {
1352         BerElement *ber;
1353         ber_int_t search_flags;
1354         ber_tag_t tag;
1355
1356         if ( ctrl->ldctl_value.bv_len == 0 ) {
1357                 rs->sr_text = "searchOptions control value not empty";
1358                 return LDAP_PROTOCOL_ERROR;
1359         }
1360
1361         ber = ber_init( &ctrl->ldctl_value );
1362         if( ber == NULL ) {
1363                 rs->sr_text = "internal error";
1364                 return LDAP_OTHER;
1365         }
1366
1367         if ( (tag = ber_scanf( ber, "{i}", &search_flags )) == LBER_ERROR ) {
1368                 rs->sr_text = "searchOptions control decoding error";
1369                 return LDAP_PROTOCOL_ERROR;
1370         }
1371
1372         (void) ber_free( ber, 1 );
1373
1374         if ( search_flags & LDAP_SEARCH_FLAG_DOMAIN_SCOPE ) {
1375                 if ( op->o_domain_scope != SLAP_CONTROL_NONE ) {
1376                         rs->sr_text = "searchOptions control specified multiple times "
1377                                 "or with domainScope control";
1378                         return LDAP_PROTOCOL_ERROR;
1379                 }
1380
1381                 op->o_domain_scope = ctrl->ldctl_iscritical
1382                         ? SLAP_CONTROL_CRITICAL
1383                         : SLAP_CONTROL_NONCRITICAL;
1384         }
1385
1386         if ( search_flags & ~(LDAP_SEARCH_FLAG_DOMAIN_SCOPE) ) {
1387                 /* Other search flags not recognised so far,
1388                  * including:
1389                  *              LDAP_SEARCH_FLAG_PHANTOM_ROOM
1390                  */
1391                 rs->sr_text = "searchOptions contained unrecongized flag";
1392                 return LDAP_UNWILLING_TO_PERFORM;
1393         }
1394
1395         return LDAP_SUCCESS;
1396 }
1397 #endif
1398