]> git.sur5r.net Git - openldap/blob - servers/slapd/controls.c
81ec83508958b9e2405c30495b03f4bb3c07f86e
[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
80         { LDAP_CONTROL_PAGEDRESULTS,
81                 SLAP_CTRL_SEARCH, NULL,
82                 parsePagedResults },
83 #endif /* LDAP_CONTROL_PAGEDRESULTS */
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 /* LDAP_CLIENT_UPDATE */
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         len = ber_pvt_ber_remaining(ber);
130
131         if( len == 0) {
132                 /* no controls */
133                 rc = LDAP_SUCCESS;
134                 return rc;
135         }
136
137         if(( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) {
138                 if( tag == LBER_ERROR ) {
139                         rc = SLAPD_DISCONNECT;
140                         errmsg = "unexpected data in PDU";
141                 }
142
143                 goto return_results;
144         }
145
146 #ifdef NEW_LOGGING
147         LDAP_LOG( OPERATION, ENTRY, "get_ctrls: conn %lu\n", conn->c_connid, 0, 0 );
148 #else
149         Debug( LDAP_DEBUG_TRACE, "=> get_ctrls\n", 0, 0, 0 );
150 #endif
151         if( op->o_protocol < LDAP_VERSION3 ) {
152                 rc = SLAPD_DISCONNECT;
153                 errmsg = "controls require LDAPv3";
154                 goto return_results;
155         }
156
157         /* one for first control, one for termination */
158         op->o_ctrls = ch_malloc( 2 * sizeof(LDAPControl *) );
159
160 #if 0
161         if( op->ctrls == NULL ) {
162                 rc = LDAP_NO_MEMORY;
163                 errmsg = "no memory";
164                 goto return_results;
165         }
166 #endif
167
168         op->o_ctrls[nctrls] = NULL;
169
170         /* step through each element */
171         for( tag = ber_first_element( ber, &len, &opaque );
172                 tag != LBER_ERROR;
173                 tag = ber_next_element( ber, &len, opaque ) )
174         {
175                 LDAPControl *c;
176                 LDAPControl **tctrls;
177
178                 c = ch_calloc( 1, sizeof(LDAPControl) );
179
180 #if 0
181                 if( c == NULL ) {
182                         ldap_controls_free(op->o_ctrls);
183                         op->o_ctrls = NULL;
184
185                         rc = LDAP_NO_MEMORY;
186                         errmsg = "no memory";
187                         goto return_results;
188                 }
189 #endif
190
191                 /* allocate pointer space for current controls (nctrls)
192                  * + this control + extra NULL
193                  */
194                 tctrls = ch_realloc( op->o_ctrls,
195                         (nctrls+2) * sizeof(LDAPControl *));
196
197 #if 0
198                 if( tctrls == NULL ) {
199                         ch_free( c );
200                         ldap_controls_free(op->o_ctrls);
201                         op->o_ctrls = NULL;
202
203                         rc = LDAP_NO_MEMORY;
204                         errmsg = "no memory";
205                         goto return_results;
206                 }
207 #endif
208                 op->o_ctrls = tctrls;
209
210                 op->o_ctrls[nctrls++] = c;
211                 op->o_ctrls[nctrls] = NULL;
212
213                 tag = ber_scanf( ber, "{a" /*}*/, &c->ldctl_oid );
214
215                 if( tag == LBER_ERROR ) {
216 #ifdef NEW_LOGGING
217                         LDAP_LOG( OPERATION, INFO, "get_ctrls: conn %lu get OID failed.\n",
218                                 conn->c_connid, 0, 0 );
219 #else
220                         Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get oid failed.\n",
221                                 0, 0, 0 );
222 #endif
223                         ldap_controls_free( op->o_ctrls );
224                         op->o_ctrls = NULL;
225                         rc = SLAPD_DISCONNECT;
226                         errmsg = "decoding controls error";
227                         goto return_results;
228                 }
229
230                 tag = ber_peek_tag( ber, &len );
231
232                 if( tag == LBER_BOOLEAN ) {
233                         ber_int_t crit;
234                         tag = ber_scanf( ber, "b", &crit );
235
236                         if( tag == LBER_ERROR ) {
237 #ifdef NEW_LOGGING
238                                 LDAP_LOG( OPERATION, INFO, 
239                                         "get_ctrls: conn %lu get crit failed.\n", 
240                                         conn->c_connid, 0, 0 );
241 #else
242                                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get crit failed.\n",
243                                         0, 0, 0 );
244 #endif
245                                 ldap_controls_free( op->o_ctrls );
246                                 op->o_ctrls = NULL;
247                                 rc = SLAPD_DISCONNECT;
248                                 errmsg = "decoding controls error";
249                                 goto return_results;
250                         }
251
252                         c->ldctl_iscritical = (crit != 0);
253                         tag = ber_peek_tag( ber, &len );
254                 }
255
256                 if( tag == LBER_OCTETSTRING ) {
257                         tag = ber_scanf( ber, "o", &c->ldctl_value );
258
259                         if( tag == LBER_ERROR ) {
260 #ifdef NEW_LOGGING
261                                 LDAP_LOG( OPERATION, INFO, "get_ctrls: conn %lu: "
262                                         "%s (%scritical): get value failed.\n",
263                                         conn->c_connid, c->ldctl_oid ? c->ldctl_oid : "(NULL)",
264                                         c->ldctl_iscritical ? "" : "non" );
265 #else
266                                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: conn %lu: "
267                                         "%s (%scritical): get value failed.\n",
268                                         conn->c_connid,
269                                         c->ldctl_oid ? c->ldctl_oid : "(NULL)",
270                                         c->ldctl_iscritical ? "" : "non" );
271 #endif
272                                 ldap_controls_free( op->o_ctrls );
273                                 op->o_ctrls = NULL;
274                                 rc = SLAPD_DISCONNECT;
275                                 errmsg = "decoding controls error";
276                                 goto return_results;
277                         }
278                 }
279
280 #ifdef NEW_LOGGING
281                 LDAP_LOG( OPERATION, INFO, 
282                         "get_ctrls: conn %lu oid=\"%s\" (%scritical)\n",
283                         conn->c_connid, c->ldctl_oid ? c->ldctl_oid : "(NULL)",
284                         c->ldctl_iscritical ? "" : "non" );
285 #else
286                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: oid=\"%s\" (%scritical)\n",
287                         c->ldctl_oid ? c->ldctl_oid : "(NULL)",
288                         c->ldctl_iscritical ? "" : "non",
289                         0 );
290 #endif
291
292                 sc = find_ctrl( c->ldctl_oid );
293                 if( sc != NULL ) {
294                         /* recognized control */
295                         slap_mask_t tagmask;
296                         switch( op->o_tag ) {
297                         case LDAP_REQ_ADD:
298                                 tagmask = SLAP_CTRL_ADD;
299                                 break;
300                         case LDAP_REQ_BIND:
301                                 tagmask = SLAP_CTRL_BIND;
302                                 break;
303                         case LDAP_REQ_COMPARE:
304                                 tagmask = SLAP_CTRL_COMPARE;
305                                 break;
306                         case LDAP_REQ_DELETE:
307                                 tagmask = SLAP_CTRL_DELETE;
308                                 break;
309                         case LDAP_REQ_MODIFY:
310                                 tagmask = SLAP_CTRL_MODIFY;
311                                 break;
312                         case LDAP_REQ_RENAME:
313                                 tagmask = SLAP_CTRL_RENAME;
314                                 break;
315                         case LDAP_REQ_SEARCH:
316                                 tagmask = SLAP_CTRL_SEARCH;
317                                 break;
318                         case LDAP_REQ_UNBIND:
319                                 tagmask = SLAP_CTRL_UNBIND;
320                                 break;
321                         case LDAP_REQ_ABANDON:
322                                 tagmask = SLAP_CTRL_ABANDON;
323                                 break;
324                         case LDAP_REQ_EXTENDED:
325                                 /* FIXME: check list of extended operations */
326                                 tagmask = ~0U;
327                                 break;
328                         default:
329                                 rc = LDAP_OTHER;
330                                 errmsg = "controls internal error";
331                                 goto return_results;
332                         }
333
334                         if (( sc->sc_mask & tagmask ) == tagmask ) {
335                                 /* available extension */
336
337                                 if( !sc->sc_parse ) {
338                                         rc = LDAP_OTHER;
339                                         errmsg = "not yet implemented";
340                                         goto return_results;
341                                 }
342
343                                 rc = sc->sc_parse( conn, op, c, &errmsg );
344
345                                 if( rc != LDAP_SUCCESS ) goto return_results;
346
347                                 if ( sc->sc_mask & SLAP_CTRL_FRONTEND ) {
348                                         /* kludge to disable backend_control() check */
349                                         c->ldctl_iscritical = 0;
350
351                                 } else if ( tagmask == SLAP_CTRL_SEARCH &&
352                                         sc->sc_mask & SLAP_CTRL_FRONTEND_SEARCH )
353                                 {
354                                         /* kludge to disable backend_control() check */
355                                         c->ldctl_iscritical = 0;
356                                 }
357
358                         } else if( c->ldctl_iscritical ) {
359                                 /* unavailable CRITICAL control */
360                                 rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
361                                 errmsg = "critical extension is unavailable";
362                                 goto return_results;
363                         }
364
365                 } else if( c->ldctl_iscritical ) {
366                         /* unrecognized CRITICAL control */
367                         rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
368                         errmsg = "critical extension is not recognized";
369                         goto return_results;
370                 }
371         }
372
373 return_results:
374 #ifdef NEW_LOGGING
375         LDAP_LOG( OPERATION, RESULTS, 
376                 "get_ctrls: n=%d rc=%d err=%s\n", nctrls, rc, errmsg ? errmsg : "" );
377 #else
378         Debug( LDAP_DEBUG_TRACE, "<= get_ctrls: n=%d rc=%d err=%s\n",
379                 nctrls, rc, errmsg ? errmsg : "");
380 #endif
381
382         if( sendres && rc != LDAP_SUCCESS ) {
383                 if( rc == SLAPD_DISCONNECT ) {
384                         send_ldap_disconnect( conn, op, LDAP_PROTOCOL_ERROR, errmsg );
385                 } else {
386                         send_ldap_result( conn, op, rc,
387                                 NULL, errmsg, NULL, NULL );
388                 }
389         }
390
391         return rc;
392 }
393
394 static int parseManageDSAit (
395         Connection *conn,
396         Operation *op,
397         LDAPControl *ctrl,
398         const char **text )
399 {
400         if ( op->o_managedsait != SLAP_NO_CONTROL ) {
401                 *text = "manageDSAit control specified multiple times";
402                 return LDAP_PROTOCOL_ERROR;
403         }
404
405         if ( ctrl->ldctl_value.bv_len ) {
406                 *text = "manageDSAit control value not empty";
407                 return LDAP_PROTOCOL_ERROR;
408         }
409
410         op->o_managedsait = ctrl->ldctl_iscritical
411                 ? SLAP_CRITICAL_CONTROL
412                 : SLAP_NONCRITICAL_CONTROL;
413
414         return LDAP_SUCCESS;
415 }
416
417 #ifdef LDAP_CONTROL_SUBENTRIES
418 static int parseSubentries (
419         Connection *conn,
420         Operation *op,
421         LDAPControl *ctrl,
422         const char **text )
423 {
424         if ( op->o_subentries != SLAP_NO_CONTROL ) {
425                 *text = "subentries control specified multiple times";
426                 return LDAP_PROTOCOL_ERROR;
427         }
428
429         /* FIXME: should use BER library */
430         if( ( ctrl->ldctl_value.bv_len != 3 )
431                 && ( ctrl->ldctl_value.bv_val[0] != 0x01 )
432                 && ( ctrl->ldctl_value.bv_val[1] != 0x01 ))
433         {
434                 *text = "subentries control value encoding is bogus";
435                 return LDAP_PROTOCOL_ERROR;
436         }
437
438         op->o_subentries = ctrl->ldctl_iscritical
439                 ? SLAP_CRITICAL_CONTROL
440                 : SLAP_NONCRITICAL_CONTROL;
441
442         op->o_subentries_visibility = (ctrl->ldctl_value.bv_val[2] != 0x00);
443
444         return LDAP_SUCCESS;
445 }
446 #endif
447
448 #ifdef LDAP_CONTROL_NOOP
449 static int parseNoOp (
450         Connection *conn,
451         Operation *op,
452         LDAPControl *ctrl,
453         const char **text )
454 {
455         if ( op->o_noop != SLAP_NO_CONTROL ) {
456                 *text = "noop control specified multiple times";
457                 return LDAP_PROTOCOL_ERROR;
458         }
459
460         if ( ctrl->ldctl_value.bv_len ) {
461                 *text = "noop control value not empty";
462                 return LDAP_PROTOCOL_ERROR;
463         }
464
465         op->o_noop = ctrl->ldctl_iscritical
466                 ? SLAP_CRITICAL_CONTROL
467                 : SLAP_NONCRITICAL_CONTROL;
468
469         return LDAP_SUCCESS;
470 }
471 #endif
472
473 #ifdef LDAP_CONTROL_PAGEDRESULTS
474 static int parsePagedResults (
475         Connection *conn,
476         Operation *op,
477         LDAPControl *ctrl,
478         const char **text )
479 {
480         ber_tag_t tag;
481         ber_int_t size;
482         BerElement *ber;
483         struct berval cookie = { 0, NULL };
484
485         if ( op->o_pagedresults != SLAP_NO_CONTROL ) {
486                 *text = "paged results control specified multiple times";
487                 return LDAP_PROTOCOL_ERROR;
488         }
489
490         if ( ctrl->ldctl_value.bv_len == 0 ) {
491                 *text = "paged results control value is empty";
492                 return LDAP_PROTOCOL_ERROR;
493         }
494
495         /* Parse the control value
496          *      realSearchControlValue ::= SEQUENCE {
497          *              size    INTEGER (0..maxInt),
498          *                              -- requested page size from client
499          *                              -- result set size estimate from server
500          *              cookie  OCTET STRING
501          */
502         ber = ber_init( &ctrl->ldctl_value );
503         if( ber == NULL ) {
504                 *text = "internal error";
505                 return LDAP_OTHER;
506         }
507
508         tag = ber_scanf( ber, "{im}", &size, &cookie );
509         (void) ber_free( ber, 1 );
510
511         if( tag == LBER_ERROR ) {
512                 *text = "paged results control could not be decoded";
513                 return LDAP_PROTOCOL_ERROR;
514         }
515
516         if( size < 0 ) {
517                 *text = "paged results control size invalid";
518                 return LDAP_PROTOCOL_ERROR;
519         }
520
521         if( cookie.bv_len ) {
522                 PagedResultsCookie reqcookie;
523                 if( cookie.bv_len != sizeof( reqcookie ) ) {
524                         /* bad cookie */
525                         *text = "paged results cookie is invalid";
526                         return LDAP_PROTOCOL_ERROR;
527                 }
528
529                 AC_MEMCPY( &reqcookie, cookie.bv_val, sizeof( reqcookie ));
530
531                 if( reqcookie > op->o_pagedresults_state.ps_cookie ) {
532                         /* bad cookie */
533                         *text = "paged results cookie is invalid";
534                         return LDAP_PROTOCOL_ERROR;
535
536                 } else if( reqcookie < op->o_pagedresults_state.ps_cookie ) {
537                         *text = "paged results cookie is invalid or old";
538                         return LDAP_UNWILLING_TO_PERFORM;
539                 }
540         } else {
541                 /* Initial request.  Initialize state. */
542                 op->o_pagedresults_state.ps_cookie = 0;
543                 op->o_pagedresults_state.ps_id = NOID;
544         }
545
546         op->o_pagedresults_size = size;
547
548         op->o_pagedresults = ctrl->ldctl_iscritical
549                 ? SLAP_CRITICAL_CONTROL
550                 : SLAP_NONCRITICAL_CONTROL;
551
552         return LDAP_SUCCESS;
553 }
554 #endif /* LDAP_CONTROL_PAGEDRESULTS */
555
556 #ifdef LDAP_CONTROL_VALUESRETURNFILTER
557 int parseValuesReturnFilter (
558         Connection *conn,
559         Operation *op,
560         LDAPControl *ctrl,
561         const char **text )
562 {
563         int             rc;
564         BerElement      *ber;
565         struct berval   fstr = { 0, NULL };
566         const char *err_msg = "";
567
568         if ( op->o_valuesreturnfilter != SLAP_NO_CONTROL ) {
569                 *text = "valuesreturnfilter control specified multiple times";
570                 return LDAP_PROTOCOL_ERROR;
571         }
572
573         ber = ber_init( &(ctrl->ldctl_value) );
574         if (ber == NULL) {
575                 *text = "internal error";
576                 return LDAP_OTHER;
577         }
578         
579         rc = get_vrFilter( conn, ber, &(op->vrFilter), &err_msg);
580
581         if( rc != LDAP_SUCCESS ) {
582                 text = &err_msg;
583                 if( rc == SLAPD_DISCONNECT ) {
584                         send_ldap_disconnect( conn, op,
585                                 LDAP_PROTOCOL_ERROR, *text );
586                 } else {
587                         send_ldap_result( conn, op, rc,
588                                 NULL, *text, NULL, NULL );
589                 }
590                 if( fstr.bv_val != NULL) free( fstr.bv_val );
591                 if( op->vrFilter != NULL) vrFilter_free( op->vrFilter ); 
592
593         } else {
594                 vrFilter2bv( op->vrFilter, &fstr );
595         }
596
597 #ifdef NEW_LOGGING
598         LDAP_LOG( OPERATION, ARGS, 
599                 "parseValuesReturnFilter: conn %d       vrFilter: %s\n", 
600                 conn->c_connid, fstr.bv_len ? fstr.bv_val : "empty" , 0 );
601 #else
602         Debug( LDAP_DEBUG_ARGS, "       vrFilter: %s\n",
603                 fstr.bv_len ? fstr.bv_val : "empty", 0, 0 );
604 #endif
605
606         op->o_valuesreturnfilter = ctrl->ldctl_iscritical
607                 ? SLAP_CRITICAL_CONTROL
608                 : SLAP_NONCRITICAL_CONTROL;
609
610         return LDAP_SUCCESS;
611 }
612 #endif
613
614 #ifdef LDAP_CLIENT_UPDATE
615 static int parseClientUpdate (
616         Connection *conn,
617         Operation *op,
618         LDAPControl *ctrl,
619         const char **text )
620 {
621         ber_tag_t tag;
622         BerElement *ber;
623         ber_int_t type;
624         ber_int_t interval;
625         ber_len_t len;
626         struct berval scheme = { 0, NULL };
627         struct berval cookie = { 0, NULL };
628
629         if ( op->o_clientupdate != SLAP_NO_CONTROL ) {
630                 *text = "LCUP client update control specified multiple times";
631                 return LDAP_PROTOCOL_ERROR;
632         }
633
634         if ( ctrl->ldctl_value.bv_len == 0 ) {
635                 *text = "LCUP client update control value is empty";
636                 return LDAP_PROTOCOL_ERROR;
637         }
638
639         /* Parse the control value
640          *      ClientUpdateControlValue ::= SEQUENCE {
641          *              updateType      ENUMERATED {
642          *                                      synchronizeOnly {0},
643          *                                      synchronizeAndPersist {1},
644          *                                      persistOnly {2} },
645          *              sendCookieInterval INTEGER OPTIONAL,
646          *              cookie          LCUPCookie OPTIONAL
647          *      }
648          */
649
650         ber = ber_init( &ctrl->ldctl_value );
651         if( ber == NULL ) {
652                 *text = "internal error";
653                 return LDAP_OTHER;
654         }
655
656         if ( (tag = ber_scanf( ber, "{i" /*}*/, &type )) == LBER_ERROR ) {
657                 *text = "LCUP client update control : decoding error";
658                 return LDAP_PROTOCOL_ERROR;
659         }
660
661         switch( type ) {
662         case LDAP_CUP_SYNC_ONLY:
663                 type = SLAP_LCUP_SYNC;
664                 break;
665         case LDAP_CUP_SYNC_AND_PERSIST:
666                 type = SLAP_LCUP_SYNC_AND_PERSIST;
667                 break;
668         case LDAP_CUP_PERSIST_ONLY:
669                 type = SLAP_LCUP_PERSIST;
670                 break;
671         default:
672                 *text = "LCUP client update control : unknown update type";
673                 return LDAP_PROTOCOL_ERROR;
674         }
675
676         if ( (tag = ber_peek_tag( ber, &len )) == LBER_DEFAULT ) {
677                 *text = "LCUP client update control : decoding error";
678                 return LDAP_PROTOCOL_ERROR;
679         }
680
681         if ( tag == LDAP_TAG_INTERVAL ) {
682                 if ( (tag = ber_scanf( ber, "i", &interval )) == LBER_ERROR ) {
683                         *text = "LCUP client update control : decoding error";
684                         return LDAP_PROTOCOL_ERROR;
685                 }
686                 
687                 if ( interval <= 0 ) {
688                         /* server chooses interval */
689                         interval = LDAP_CUP_DEFAULT_SEND_COOKIE_INTERVAL;
690                 }
691
692         } else {
693                 /* server chooses interval */
694                 interval = LDAP_CUP_DEFAULT_SEND_COOKIE_INTERVAL;
695         }
696
697         if ( (tag = ber_peek_tag( ber, &len )) == LBER_DEFAULT ) {
698                 *text = "LCUP client update control : decoding error";
699                 return LDAP_PROTOCOL_ERROR;
700         }
701
702         if ( tag == LDAP_TAG_COOKIE ) {
703                 if ( (tag = ber_scanf( ber, /*{*/ "{mm}}",
704                                         &scheme, &cookie )) == LBER_ERROR ) {
705                         *text = "LCUP client update control : decoding error";
706                         return LDAP_PROTOCOL_ERROR;
707                 }
708         }
709
710         /* TODO : Cookie Scheme Validation */
711 #if 0
712         if ( lcup_cookie_scheme_validate(scheme) != LDAP_SUCCESS ) {
713                 *text = "Unsupported LCUP cookie scheme";
714                 return LCUP_UNSUPPORTED_SCHEME;
715         }
716
717         if ( lcup_cookie_validate(scheme, cookie) != LDAP_SUCCESS ) {
718                 *text = "Invalid LCUP cookie";
719                 return LCUP_INVALID_COOKIE;
720         }
721 #endif
722
723         ber_dupbv( &op->o_clientupdate_state, &cookie );
724
725         (void) ber_free( ber, 1 );
726
727         op->o_clientupdate_type = (char) type;
728         op->o_clientupdate_interval = interval;
729
730         op->o_clientupdate = ctrl->ldctl_iscritical
731                 ? SLAP_CRITICAL_CONTROL
732                 : SLAP_NONCRITICAL_CONTROL;
733
734         return LDAP_SUCCESS;
735 }
736 #endif /* LDAP_CLIENT_UPDATE */