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