]> git.sur5r.net Git - openldap/blob - servers/slapd/controls.c
d0da95c2bb04a8ff1914038c7af0ddd048c91186
[openldap] / servers / slapd / controls.c
1 /* $OpenLDAP$ */
2 /* 
3  * Copyright 1999-2002 The OpenLDAP Foundation.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are permitted only
7  * as authorized by the OpenLDAP Public License.  A copy of this
8  * license is available at http://www.OpenLDAP.org/license.html or
9  * in file LICENSE in the top-level directory of the distribution.
10  */
11 #include "portable.h"
12
13 #include <stdio.h>
14
15 #include <ac/string.h>
16 #include <ac/socket.h>
17
18 #include "slap.h"
19
20 #include "../../libraries/liblber/lber-int.h"
21
22 #define SLAP_CTRL_FRONTEND                      0x80000000U
23 #define SLAP_CTRL_FRONTEND_SEARCH       0x01000000U     /* for NOOP */
24
25 #define SLAP_CTRL_OPFLAGS                       0x0000FFFFU
26 #define SLAP_CTRL_ABANDON                       0x00000001U
27 #define SLAP_CTRL_ADD                           0x00002002U
28 #define SLAP_CTRL_BIND                          0x00000004U
29 #define SLAP_CTRL_COMPARE                       0x00001008U
30 #define SLAP_CTRL_DELETE                        0x00002010U
31 #define SLAP_CTRL_MODIFY                        0x00002020U
32 #define SLAP_CTRL_RENAME                        0x00002040U
33 #define SLAP_CTRL_SEARCH                        0x00001080U
34 #define SLAP_CTRL_UNBIND                        0x00000100U
35
36 #define SLAP_CTRL_INTROGATE     (SLAP_CTRL_COMPARE|SLAP_CTRL_SEARCH)
37 #define SLAP_CTRL_UPDATE \
38         (SLAP_CTRL_ADD|SLAP_CTRL_DELETE|SLAP_CTRL_MODIFY|SLAP_CTRL_RENAME)
39 #define SLAP_CTRL_ACCESS        (SLAP_CTRL_INTROGATE|SLAP_CTRL_UPDATE)
40
41 typedef int (SLAP_CTRL_PARSE_FN) LDAP_P((
42         Connection *conn,
43         Operation *op,
44         LDAPControl *ctrl,
45         const char **text ));
46
47 static SLAP_CTRL_PARSE_FN parseManageDSAit;
48 static SLAP_CTRL_PARSE_FN parseSubentries;
49 static SLAP_CTRL_PARSE_FN parseNoOp;
50 static SLAP_CTRL_PARSE_FN parsePagedResults;
51 static SLAP_CTRL_PARSE_FN parseValuesReturnFilter;
52
53 #ifdef LDAP_CLIENT_UPDATE
54 static SLAP_CTRL_PARSE_FN parseClientUpdate;
55 #endif /* LDAP_CLIENT_UPDATE */
56
57 #undef sc_mask /* avoid conflict with Irix 6.5 <sys/signal.h> */
58
59 static struct slap_control {
60         char *sc_oid;
61         slap_mask_t sc_mask;
62         char **sc_extendedops;
63         SLAP_CTRL_PARSE_FN *sc_parse;
64
65 } supportedControls[] = {
66         { LDAP_CONTROL_MANAGEDSAIT,
67                 SLAP_CTRL_ACCESS, NULL,
68                 parseManageDSAit },
69 #ifdef LDAP_CONTROL_SUBENTRIES
70         { LDAP_CONTROL_SUBENTRIES,
71                 SLAP_CTRL_SEARCH, NULL,
72                 parseSubentries },
73 #endif
74 #ifdef LDAP_CONTROL_NOOP
75         { LDAP_CONTROL_NOOP,
76                 SLAP_CTRL_ACCESS, NULL,
77                 parseNoOp },
78 #endif
79 #ifdef LDAP_CONTROL_PAGEDRESULTS_REQUEST
80         { LDAP_CONTROL_PAGEDRESULTS_REQUEST,
81                 SLAP_CTRL_SEARCH, NULL,
82                 parsePagedResults },
83 #endif
84 #ifdef LDAP_CONTROL_VALUESRETURNFILTER
85         { LDAP_CONTROL_VALUESRETURNFILTER,
86                 SLAP_CTRL_SEARCH, NULL,
87                 parseValuesReturnFilter },
88 #endif
89 #ifdef LDAP_CLIENT_UPDATE
90         { LDAP_CONTROL_CLIENT_UPDATE,
91                 SLAP_CTRL_SEARCH, NULL,
92                 parseClientUpdate },
93 #endif
94         { NULL }
95 };
96
97 char *
98 get_supported_ctrl(int index)
99 {
100         return supportedControls[index].sc_oid;
101 }
102
103 static struct slap_control *
104 find_ctrl( const char *oid )
105 {
106         int i;
107         for( i=0; supportedControls[i].sc_oid; i++ ) {
108                 if( strcmp( oid, supportedControls[i].sc_oid ) == 0 ) {
109                         return &supportedControls[i];
110                 }
111         }
112         return NULL;
113 }
114
115 int get_ctrls(
116         Connection *conn,
117         Operation *op,
118         int sendres )
119 {
120         int nctrls = 0;
121         ber_tag_t tag;
122         ber_len_t len;
123         char *opaque;
124         BerElement *ber = op->o_ber;
125         struct slap_control *sc;
126         int rc = LDAP_SUCCESS;
127         const char *errmsg = NULL;
128
129 #ifdef LDAP_CLIENT_UPDATE
130         op->o_clientupdatetype = -1;
131 #endif
132
133         len = ber_pvt_ber_remaining(ber);
134
135         if( len == 0) {
136                 /* no controls */
137                 rc = LDAP_SUCCESS;
138                 return rc;
139         }
140
141         if(( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) {
142                 if( tag == LBER_ERROR ) {
143                         rc = SLAPD_DISCONNECT;
144                         errmsg = "unexpected data in PDU";
145                 }
146
147                 goto return_results;
148         }
149
150 #ifdef NEW_LOGGING
151         LDAP_LOG( OPERATION, ENTRY, "get_ctrls: conn %lu\n", conn->c_connid, 0, 0 );
152 #else
153         Debug( LDAP_DEBUG_TRACE, "=> get_ctrls\n", 0, 0, 0 );
154 #endif
155         if( op->o_protocol < LDAP_VERSION3 ) {
156                 rc = SLAPD_DISCONNECT;
157                 errmsg = "controls require LDAPv3";
158                 goto return_results;
159         }
160
161         /* one for first control, one for termination */
162         op->o_ctrls = ch_malloc( 2 * sizeof(LDAPControl *) );
163
164 #if 0
165         if( op->ctrls == NULL ) {
166                 rc = LDAP_NO_MEMORY;
167                 errmsg = "no memory";
168                 goto return_results;
169         }
170 #endif
171
172         op->o_ctrls[nctrls] = NULL;
173
174         /* step through each element */
175         for( tag = ber_first_element( ber, &len, &opaque );
176                 tag != LBER_ERROR;
177                 tag = ber_next_element( ber, &len, opaque ) )
178         {
179                 LDAPControl *c;
180                 LDAPControl **tctrls;
181
182                 c = ch_calloc( 1, sizeof(LDAPControl) );
183
184 #if 0
185                 if( c == NULL ) {
186                         ldap_controls_free(op->o_ctrls);
187                         op->o_ctrls = NULL;
188
189                         rc = LDAP_NO_MEMORY;
190                         errmsg = "no memory";
191                         goto return_results;
192                 }
193 #endif
194
195                 /* allocate pointer space for current controls (nctrls)
196                  * + this control + extra NULL
197                  */
198                 tctrls = ch_realloc( op->o_ctrls,
199                         (nctrls+2) * sizeof(LDAPControl *));
200
201 #if 0
202                 if( tctrls == NULL ) {
203                         ch_free( c );
204                         ldap_controls_free(op->o_ctrls);
205                         op->o_ctrls = NULL;
206
207                         rc = LDAP_NO_MEMORY;
208                         errmsg = "no memory";
209                         goto return_results;
210                 }
211 #endif
212                 op->o_ctrls = tctrls;
213
214                 op->o_ctrls[nctrls++] = c;
215                 op->o_ctrls[nctrls] = NULL;
216
217                 tag = ber_scanf( ber, "{a" /*}*/, &c->ldctl_oid );
218
219                 if( tag == LBER_ERROR ) {
220 #ifdef NEW_LOGGING
221                         LDAP_LOG( OPERATION, INFO, "get_ctrls: conn %lu get OID failed.\n",
222                                 conn->c_connid, 0, 0 );
223 #else
224                         Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get oid failed.\n",
225                                 0, 0, 0 );
226 #endif
227                         ldap_controls_free( op->o_ctrls );
228                         op->o_ctrls = NULL;
229                         rc = SLAPD_DISCONNECT;
230                         errmsg = "decoding controls error";
231                         goto return_results;
232                 }
233
234                 tag = ber_peek_tag( ber, &len );
235
236                 if( tag == LBER_BOOLEAN ) {
237                         ber_int_t crit;
238                         tag = ber_scanf( ber, "b", &crit );
239
240                         if( tag == LBER_ERROR ) {
241 #ifdef NEW_LOGGING
242                                 LDAP_LOG( OPERATION, INFO, 
243                                         "get_ctrls: conn %lu get crit failed.\n", 
244                                         conn->c_connid, 0, 0 );
245 #else
246                                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get crit failed.\n",
247                                         0, 0, 0 );
248 #endif
249                                 ldap_controls_free( op->o_ctrls );
250                                 op->o_ctrls = NULL;
251                                 rc = SLAPD_DISCONNECT;
252                                 errmsg = "decoding controls error";
253                                 goto return_results;
254                         }
255
256                         c->ldctl_iscritical = (crit != 0);
257                         tag = ber_peek_tag( ber, &len );
258                 }
259
260                 if( tag == LBER_OCTETSTRING ) {
261                         tag = ber_scanf( ber, "o", &c->ldctl_value );
262
263                         if( tag == LBER_ERROR ) {
264 #ifdef NEW_LOGGING
265                                 LDAP_LOG( OPERATION, INFO, "get_ctrls: conn %lu: "
266                                         "%s (%scritical): get value failed.\n",
267                                         conn->c_connid, c->ldctl_oid ? c->ldctl_oid : "(NULL)",
268                                         c->ldctl_iscritical ? "" : "non" );
269 #else
270                                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: conn %lu: "
271                                         "%s (%scritical): get value failed.\n",
272                                         conn->c_connid,
273                                         c->ldctl_oid ? c->ldctl_oid : "(NULL)",
274                                         c->ldctl_iscritical ? "" : "non" );
275 #endif
276                                 ldap_controls_free( op->o_ctrls );
277                                 op->o_ctrls = NULL;
278                                 rc = SLAPD_DISCONNECT;
279                                 errmsg = "decoding controls error";
280                                 goto return_results;
281                         }
282                 }
283
284 #ifdef NEW_LOGGING
285                 LDAP_LOG( OPERATION, INFO, 
286                         "get_ctrls: conn %lu oid=\"%s\" (%scritical)\n",
287                         conn->c_connid, c->ldctl_oid ? c->ldctl_oid : "(NULL)",
288                         c->ldctl_iscritical ? "" : "non" );
289 #else
290                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: oid=\"%s\" (%scritical)\n",
291                         c->ldctl_oid ? c->ldctl_oid : "(NULL)",
292                         c->ldctl_iscritical ? "" : "non",
293                         0 );
294 #endif
295
296                 sc = find_ctrl( c->ldctl_oid );
297                 if( sc != NULL ) {
298                         /* recognized control */
299                         slap_mask_t tagmask;
300                         switch( op->o_tag ) {
301                         case LDAP_REQ_ADD:
302                                 tagmask = SLAP_CTRL_ADD;
303                                 break;
304                         case LDAP_REQ_BIND:
305                                 tagmask = SLAP_CTRL_BIND;
306                                 break;
307                         case LDAP_REQ_COMPARE:
308                                 tagmask = SLAP_CTRL_COMPARE;
309                                 break;
310                         case LDAP_REQ_DELETE:
311                                 tagmask = SLAP_CTRL_DELETE;
312                                 break;
313                         case LDAP_REQ_MODIFY:
314                                 tagmask = SLAP_CTRL_MODIFY;
315                                 break;
316                         case LDAP_REQ_RENAME:
317                                 tagmask = SLAP_CTRL_RENAME;
318                                 break;
319                         case LDAP_REQ_SEARCH:
320                                 tagmask = SLAP_CTRL_SEARCH;
321                                 break;
322                         case LDAP_REQ_UNBIND:
323                                 tagmask = SLAP_CTRL_UNBIND;
324                                 break;
325                         case LDAP_REQ_EXTENDED:
326                                 /* FIXME: check list of extended operations */
327                                 tagmask = ~0U;
328                                 break;
329                         default:
330                                 rc = LDAP_OTHER;
331                                 errmsg = "controls internal error";
332                                 goto return_results;
333                         }
334
335                         if (( sc->sc_mask & tagmask ) == tagmask ) {
336                                 /* available extension */
337
338                                 if( !sc->sc_parse ) {
339                                         rc = LDAP_OTHER;
340                                         errmsg = "not yet implemented";
341                                         goto return_results;
342                                 }
343
344                                 rc = sc->sc_parse( conn, op, c, &errmsg );
345
346                                 if( rc != LDAP_SUCCESS ) goto return_results;
347
348                                 if ( sc->sc_mask & SLAP_CTRL_FRONTEND ) {
349                                         /* kludge to disable backend_control() check */
350                                         c->ldctl_iscritical = 0;
351
352                                 } else if ( tagmask == SLAP_CTRL_SEARCH &&
353                                         sc->sc_mask & SLAP_CTRL_FRONTEND_SEARCH )
354                                 {
355                                         /* kludge to disable backend_control() check */
356                                         c->ldctl_iscritical = 0;
357                                 }
358
359                         } else if( c->ldctl_iscritical ) {
360                                 /* unavailable CRITICAL control */
361                                 rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
362                                 errmsg = "critical extension is unavailable";
363                                 goto return_results;
364                         }
365
366                 } else if( c->ldctl_iscritical ) {
367                         /* unrecognized CRITICAL control */
368                         rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
369                         errmsg = "critical extension is not recognized";
370                         goto return_results;
371                 }
372         }
373
374 return_results:
375 #ifdef NEW_LOGGING
376         LDAP_LOG( OPERATION, RESULTS, 
377                 "get_ctrls: n=%d rc=%d err=%s\n", nctrls, rc, errmsg ? errmsg : "" );
378 #else
379         Debug( LDAP_DEBUG_TRACE, "<= get_ctrls: n=%d rc=%d err=%s\n",
380                 nctrls, rc, errmsg ? errmsg : "");
381 #endif
382
383         if( sendres && rc != LDAP_SUCCESS ) {
384                 if( rc == SLAPD_DISCONNECT ) {
385                         send_ldap_disconnect( conn, op, LDAP_PROTOCOL_ERROR, errmsg );
386                 } else {
387                         send_ldap_result( conn, op, rc,
388                                 NULL, errmsg, NULL, NULL );
389                 }
390         }
391
392         return rc;
393 }
394
395 static int parseManageDSAit (
396         Connection *conn,
397         Operation *op,
398         LDAPControl *ctrl,
399         const char **text )
400 {
401         if ( op->o_managedsait != SLAP_NO_CONTROL ) {
402                 *text = "manageDSAit control specified multiple times";
403                 return LDAP_PROTOCOL_ERROR;
404         }
405
406         if ( ctrl->ldctl_value.bv_len ) {
407                 *text = "manageDSAit control value not empty";
408                 return LDAP_PROTOCOL_ERROR;
409         }
410
411         op->o_managedsait = ctrl->ldctl_iscritical
412                 ? SLAP_CRITICAL_CONTROL
413                 : SLAP_NONCRITICAL_CONTROL;
414
415         return LDAP_SUCCESS;
416 }
417
418 #ifdef LDAP_CONTROL_SUBENTRIES
419 static int parseSubentries (
420         Connection *conn,
421         Operation *op,
422         LDAPControl *ctrl,
423         const char **text )
424 {
425         if ( op->o_subentries != SLAP_NO_CONTROL ) {
426                 *text = "subentries control specified multiple times";
427                 return LDAP_PROTOCOL_ERROR;
428         }
429
430         /* FIXME: should use BER library */
431         if( ( ctrl->ldctl_value.bv_len != 3 )
432                 && ( ctrl->ldctl_value.bv_val[0] != 0x01 )
433                 && ( ctrl->ldctl_value.bv_val[1] != 0x01 ))
434         {
435                 *text = "subentries control value encoding is bogus";
436                 return LDAP_PROTOCOL_ERROR;
437         }
438
439         op->o_subentries = ctrl->ldctl_iscritical
440                 ? SLAP_CRITICAL_CONTROL
441                 : SLAP_NONCRITICAL_CONTROL;
442
443         op->o_subentries_visibility = (ctrl->ldctl_value.bv_val[2] != 0x00);
444
445         return LDAP_SUCCESS;
446 }
447 #endif
448
449 #ifdef LDAP_CONTROL_NOOP
450 static int parseNoOp (
451         Connection *conn,
452         Operation *op,
453         LDAPControl *ctrl,
454         const char **text )
455 {
456         if ( op->o_noop != SLAP_NO_CONTROL ) {
457                 *text = "noop control specified multiple times";
458                 return LDAP_PROTOCOL_ERROR;
459         }
460
461         if ( ctrl->ldctl_value.bv_len ) {
462                 *text = "noop control value not empty";
463                 return LDAP_PROTOCOL_ERROR;
464         }
465
466         op->o_noop = ctrl->ldctl_iscritical
467                 ? SLAP_CRITICAL_CONTROL
468                 : SLAP_NONCRITICAL_CONTROL;
469
470         return LDAP_SUCCESS;
471 }
472 #endif
473
474 #ifdef LDAP_CONTROL_PAGEDRESULTS_REQUEST
475 static int parsePagedResults (
476         Connection *conn,
477         Operation *op,
478         LDAPControl *ctrl,
479         const char **text )
480 {
481         ber_tag_t tag;
482         ber_int_t size;
483         BerElement *ber;
484         struct berval cookie = { 0, NULL };
485
486         if ( op->o_pagedresults != SLAP_NO_CONTROL ) {
487                 *text = "paged results control specified multiple times";
488                 return LDAP_PROTOCOL_ERROR;
489         }
490
491         if ( ctrl->ldctl_value.bv_len == 0 ) {
492                 *text = "paged results control value is empty";
493                 return LDAP_PROTOCOL_ERROR;
494         }
495
496         /* Parse the control value
497          *      realSearchControlValue ::= SEQUENCE {
498          *              size    INTEGER (0..maxInt),
499          *                              -- requested page size from client
500          *                              -- result set size estimate from server
501          *              cookie  OCTET STRING
502          */
503         ber = ber_init( &ctrl->ldctl_value );
504         if( ber == NULL ) {
505                 *text = "internal error";
506                 return LDAP_OTHER;
507         }
508
509         tag = ber_scanf( ber, "{im}", &size, &cookie );
510         (void) ber_free( ber, 1 );
511
512         if( tag == LBER_ERROR ) {
513                 *text = "paged results control could not be decoded";
514                 return LDAP_PROTOCOL_ERROR;
515         }
516
517         if( size <= 0 ) {
518                 *text = "paged results control size invalid";
519                 return LDAP_PROTOCOL_ERROR;
520         }
521
522         if( cookie.bv_len ) {
523                 PagedResultsCookie reqcookie;
524                 if( cookie.bv_len != sizeof( reqcookie ) ) {
525                         /* bad cookie */
526                         *text = "paged results cookie is invalid";
527                         return LDAP_PROTOCOL_ERROR;
528                 }
529
530                 AC_MEMCPY( &reqcookie, cookie.bv_val, sizeof( reqcookie ));
531
532                 if( reqcookie > op->o_pagedresults_state.ps_cookie ) {
533                         /* bad cookie */
534                         *text = "paged results cookie is invalid";
535                         return LDAP_PROTOCOL_ERROR;
536
537                 } else if( reqcookie < op->o_pagedresults_state.ps_cookie ) {
538                         *text = "paged results cookie is invalid or old";
539                         return LDAP_UNWILLING_TO_PERFORM;
540                 }
541         }
542
543         op->o_pagedresults_state.ps_cookie = op->o_opid;
544         op->o_pagedresults_size = size;
545
546         op->o_pagedresults = ctrl->ldctl_iscritical
547                 ? SLAP_CRITICAL_CONTROL
548                 : SLAP_NONCRITICAL_CONTROL;
549
550         return LDAP_SUCCESS;
551 }
552 #endif
553
554 #ifdef LDAP_CONTROL_VALUESRETURNFILTER
555 int parseValuesReturnFilter (
556         Connection *conn,
557         Operation *op,
558         LDAPControl *ctrl,
559         const char **text )
560 {
561         int             rc;
562         BerElement      *ber;
563         struct berval   fstr = { 0, NULL };
564         const char *err_msg = "";
565
566         if ( op->o_valuesreturnfilter != SLAP_NO_CONTROL ) {
567                 *text = "valuesreturnfilter control specified multiple times";
568                 return LDAP_PROTOCOL_ERROR;
569         }
570
571         ber = ber_init( &(ctrl->ldctl_value) );
572         if (ber == NULL) {
573                 *text = "internal error";
574                 return LDAP_OTHER;
575         }
576         
577         rc = get_vrFilter( conn, ber, &(op->vrFilter), &err_msg);
578
579         if( rc != LDAP_SUCCESS ) {
580                 text = &err_msg;
581                 if( rc == SLAPD_DISCONNECT ) {
582                         send_ldap_disconnect( conn, op,
583                                 LDAP_PROTOCOL_ERROR, *text );
584                 } else {
585                         send_ldap_result( conn, op, rc,
586                                 NULL, *text, NULL, NULL );
587                 }
588                 if( fstr.bv_val != NULL) free( fstr.bv_val );
589                 if( op->vrFilter != NULL) vrFilter_free( op->vrFilter ); 
590
591         } else {
592                 vrFilter2bv( op->vrFilter, &fstr );
593         }
594
595 #ifdef NEW_LOGGING
596         LDAP_LOG( OPERATION, ARGS, 
597                 "parseValuesReturnFilter: conn %d       vrFilter: %s\n", 
598                 conn->c_connid, fstr.bv_len ? fstr.bv_val : "empty" , 0 );
599 #else
600         Debug( LDAP_DEBUG_ARGS, "       vrFilter: %s\n",
601                 fstr.bv_len ? fstr.bv_val : "empty", 0, 0 );
602 #endif
603
604         op->o_valuesreturnfilter = ctrl->ldctl_iscritical
605                 ? SLAP_CRITICAL_CONTROL
606                 : SLAP_NONCRITICAL_CONTROL;
607
608         return LDAP_SUCCESS;
609 }
610 #endif
611
612 #ifdef LDAP_CLIENT_UPDATE
613 static int parseClientUpdate (
614         Connection *conn,
615         Operation *op,
616         LDAPControl *ctrl,
617         const char **text )
618 {
619         ber_tag_t tag;
620         BerElement *ber;
621         ber_int_t type;
622         ber_int_t interval;
623         ber_len_t len;
624         struct berval scheme = { 0, NULL };
625         struct berval cookie = { 0, NULL };
626
627         if ( op->o_noop != SLAP_NO_CONTROL ) {
628                 *text = "LCUP client update control specified multiple times";
629                 return LDAP_PROTOCOL_ERROR;
630         }
631
632         if ( ctrl->ldctl_value.bv_len == 0 ) {
633                 *text = "LCUP client update control value is empty";
634                 return LDAP_PROTOCOL_ERROR;
635         }
636
637         /* Parse the control value
638          *      ClientUpdateControlValue ::= SEQUENCE {
639          *              updateType      ENUMERATED {
640          *                                      synchronizeOnly {0},
641          *                                      synchronizeAndPersist {1},
642          *                                      persistOnly {2} },
643          *              sendCookieInterval INTEGER OPTIONAL,
644          *              cookie          LCUPCookie OPTIONAL
645          *      }
646          */
647
648         ber = ber_init( &ctrl->ldctl_value );
649         if( ber == NULL ) {
650                 *text = "internal error";
651                 return LDAP_OTHER;
652         }
653
654         if ( (tag = ber_scanf( ber, "{i" /*}*/, &type )) == LBER_ERROR ) {
655                 *text = "LCUP client update control : decoding error";
656                 return LDAP_PROTOCOL_ERROR;
657         }
658
659         if ( type != SYNCHRONIZE_ONLY &&
660              type != SYNCHRONIZE_AND_PERSIST &&
661              type != PERSIST_ONLY ) {
662                 *text = "LCUP client update control : unknown update type";
663                 return LDAP_PROTOCOL_ERROR;
664         }
665
666         if ( (tag = ber_peek_tag( ber, &len )) == LBER_DEFAULT ) {
667                 *text = "LCUP client update control : decoding error";
668                 return LDAP_PROTOCOL_ERROR;
669         }
670
671         if ( tag == LDAP_TAG_INTERVAL ) {
672                 if ( (tag = ber_scanf( ber, "i", &interval )) == LBER_ERROR ) {
673                         *text = "LCUP client update control : decoding error";
674                         return LDAP_PROTOCOL_ERROR;
675                 }
676                 
677                 if ( interval <= 0 ) {
678                         /* server chooses interval */
679                         interval = LDAP_LCUP_DEFAULT_SEND_COOKIE_INTERVAL;
680                 }
681         }
682         else {
683                 /* server chooses interval */
684                 interval = LDAP_LCUP_DEFAULT_SEND_COOKIE_INTERVAL;
685         }
686
687         if ( (tag = ber_peek_tag( ber, &len )) == LBER_DEFAULT ) {
688                 *text = "LCUP client update control : decoding error";
689                 return LDAP_PROTOCOL_ERROR;
690         }
691
692         if ( tag == LDAP_TAG_COOKIE ) {
693                 if ( (tag = ber_scanf( ber, /*{*/ "{mm}}",
694                                         &scheme, &cookie )) == LBER_ERROR ) {
695                         *text = "LCUP client update control : decoding error";
696                         return LDAP_PROTOCOL_ERROR;
697                 }
698         }
699
700         /* TODO : Cookie Scheme Validation */
701 #if 0
702         if ( lcup_cookie_validate(scheme, cookie) != LDAP_SUCCESS ) {
703                 *text = "Invalid LCUP cookie";
704                 return LCUP_INVALID_COOKIE;
705         }
706
707         if ( lcup_cookie_scheme_validate(scheme) != LDAP_SUCCESS ) {
708                 *text = "Unsupported LCUP cookie scheme";
709                 return LCUP_UNSUPPORTED_SCHEME;
710         }
711 #endif
712
713         op->o_clientupdatestate = ber_dupbv(NULL, &cookie);
714
715         (void) ber_free( ber, 1 );
716
717         op->o_clientupdatetype = type;
718         op->o_clientupdateinterval = interval;
719
720         op->o_clientupdate = ctrl->ldctl_iscritical
721                 ? SLAP_CRITICAL_CONTROL
722                 : SLAP_NONCRITICAL_CONTROL;
723
724         return LDAP_SUCCESS;
725 }
726 #endif