]> git.sur5r.net Git - openldap/blob - servers/slapd/controls.c
ITS#1991 fix + use struct berval
[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
24 #define SLAP_CTRL_OPFLAGS       0x0000FFFFU
25 #define SLAP_CTRL_ABANDON       0x00000001U
26 #define SLAP_CTRL_ADD           0x00002002U
27 #define SLAP_CTRL_BIND          0x00000004U
28 #define SLAP_CTRL_COMPARE       0x00001008U
29 #define SLAP_CTRL_DELETE        0x00002010U
30 #define SLAP_CTRL_MODIFY        0x00002020U
31 #define SLAP_CTRL_RENAME        0x00002040U
32 #define SLAP_CTRL_SEARCH        0x00001080U
33 #define SLAP_CTRL_UNBIND        0x00000100U
34
35 #define SLAP_CTRL_INTROGATE     (SLAP_CTRL_COMPARE|SLAP_CTRL_SEARCH)
36 #define SLAP_CTRL_UPDATE \
37         (SLAP_CTRL_ADD|SLAP_CTRL_DELETE|SLAP_CTRL_MODIFY|SLAP_CTRL_RENAME)
38 #define SLAP_CTRL_ACCESS        (SLAP_CTRL_INTROGATE|SLAP_CTRL_UPDATE)
39
40 typedef int (SLAP_CTRL_PARSE_FN) LDAP_P((
41         Connection *conn,
42         Operation *op,
43         LDAPControl *ctrl,
44         const char **text ));
45
46 static SLAP_CTRL_PARSE_FN parseManageDSAit;
47 static SLAP_CTRL_PARSE_FN parseSubentries;
48 static SLAP_CTRL_PARSE_FN parseNoOp;
49 static SLAP_CTRL_PARSE_FN parsePagedResults;
50 static SLAP_CTRL_PARSE_FN parseValuesReturnFilter;
51
52 #undef sc_mask /* avoid conflict with Irix 6.5 <sys/signal.h> */
53
54 static struct slap_control {
55         char *sc_oid;
56         slap_mask_t sc_mask;
57         char **sc_extendedops;
58         SLAP_CTRL_PARSE_FN *sc_parse;
59
60 } supportedControls[] = {
61         { LDAP_CONTROL_MANAGEDSAIT,
62                 SLAP_CTRL_ACCESS, NULL,
63                 parseManageDSAit },
64 #ifdef LDAP_CONTROL_SUBENTRIES
65         { LDAP_CONTROL_SUBENTRIES,
66                 SLAP_CTRL_SEARCH, NULL,
67                 parseSubentries },
68 #endif
69 #ifdef LDAP_CONTROL_NOOP
70         { LDAP_CONTROL_NOOP,
71                 SLAP_CTRL_UPDATE, NULL,
72                 parseNoOp },
73 #endif
74 #ifdef LDAP_CONTROL_PAGEDRESULTS_REQUEST
75         { LDAP_CONTROL_PAGEDRESULTS_REQUEST,
76                 SLAP_CTRL_SEARCH, NULL,
77                 parsePagedResults },
78 #endif
79 #ifdef LDAP_CONTROL_VALUESRETURNFILTER
80         { LDAP_CONTROL_VALUESRETURNFILTER,
81                 SLAP_CTRL_SEARCH, NULL,
82                 parseValuesReturnFilter },
83 #endif
84         { NULL }
85 };
86
87 char *
88 get_supported_ctrl(int index)
89 {
90         return supportedControls[index].sc_oid;
91 }
92
93 static struct slap_control *
94 find_ctrl( const char *oid )
95 {
96         int i;
97         for( i=0; supportedControls[i].sc_oid; i++ ) {
98                 if( strcmp( oid, supportedControls[i].sc_oid ) == 0 ) {
99                         return &supportedControls[i];
100                 }
101         }
102         return NULL;
103 }
104
105 int get_ctrls(
106         Connection *conn,
107         Operation *op,
108         int sendres )
109 {
110         int nctrls = 0;
111         ber_tag_t tag;
112         ber_len_t len;
113         char *opaque;
114         BerElement *ber = op->o_ber;
115         struct slap_control *sc;
116         int rc = LDAP_SUCCESS;
117         const char *errmsg = NULL;
118
119         len = ber_pvt_ber_remaining(ber);
120
121         if( len == 0) {
122                 /* no controls */
123                 rc = LDAP_SUCCESS;
124                 return rc;
125         }
126
127         if(( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) {
128                 if( tag == LBER_ERROR ) {
129                         rc = SLAPD_DISCONNECT;
130                         errmsg = "unexpected data in PDU";
131                 }
132
133                 goto return_results;
134         }
135
136 #ifdef NEW_LOGGING
137         LDAP_LOG( OPERATION, ENTRY, "get_ctrls: conn %lu\n", conn->c_connid, 0, 0 );
138 #else
139         Debug( LDAP_DEBUG_TRACE, "=> get_ctrls\n", 0, 0, 0 );
140 #endif
141         if( op->o_protocol < LDAP_VERSION3 ) {
142                 rc = SLAPD_DISCONNECT;
143                 errmsg = "controls require LDAPv3";
144                 goto return_results;
145         }
146
147         /* one for first control, one for termination */
148         op->o_ctrls = ch_malloc( 2 * sizeof(LDAPControl *) );
149
150 #if 0
151         if( op->ctrls == NULL ) {
152                 rc = LDAP_NO_MEMORY;
153                 errmsg = "no memory";
154                 goto return_results;
155         }
156 #endif
157
158         op->o_ctrls[nctrls] = NULL;
159
160         /* step through each element */
161         for( tag = ber_first_element( ber, &len, &opaque );
162                 tag != LBER_ERROR;
163                 tag = ber_next_element( ber, &len, opaque ) )
164         {
165                 LDAPControl *c;
166                 LDAPControl **tctrls;
167
168                 c = ch_calloc( 1, sizeof(LDAPControl) );
169
170 #if 0
171                 if( c == NULL ) {
172                         ldap_controls_free(op->o_ctrls);
173                         op->o_ctrls = NULL;
174
175                         rc = LDAP_NO_MEMORY;
176                         errmsg = "no memory";
177                         goto return_results;
178                 }
179 #endif
180
181                 /* allocate pointer space for current controls (nctrls)
182                  * + this control + extra NULL
183                  */
184                 tctrls = ch_realloc( op->o_ctrls,
185                         (nctrls+2) * sizeof(LDAPControl *));
186
187 #if 0
188                 if( tctrls == NULL ) {
189                         ch_free( c );
190                         ldap_controls_free(op->o_ctrls);
191                         op->o_ctrls = NULL;
192
193                         rc = LDAP_NO_MEMORY;
194                         errmsg = "no memory";
195                         goto return_results;
196                 }
197 #endif
198                 op->o_ctrls = tctrls;
199
200                 op->o_ctrls[nctrls++] = c;
201                 op->o_ctrls[nctrls] = NULL;
202
203                 tag = ber_scanf( ber, "{a" /*}*/, &c->ldctl_oid );
204
205                 if( tag == LBER_ERROR ) {
206 #ifdef NEW_LOGGING
207                         LDAP_LOG( OPERATION, INFO, "get_ctrls: conn %lu get OID failed.\n",
208                                 conn->c_connid, 0, 0 );
209 #else
210                         Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get oid failed.\n",
211                                 0, 0, 0 );
212 #endif
213                         ldap_controls_free( op->o_ctrls );
214                         op->o_ctrls = NULL;
215                         rc = SLAPD_DISCONNECT;
216                         errmsg = "decoding controls error";
217                         goto return_results;
218                 }
219
220                 tag = ber_peek_tag( ber, &len );
221
222                 if( tag == LBER_BOOLEAN ) {
223                         ber_int_t crit;
224                         tag = ber_scanf( ber, "b", &crit );
225
226                         if( tag == LBER_ERROR ) {
227 #ifdef NEW_LOGGING
228                                 LDAP_LOG( OPERATION, INFO, 
229                                         "get_ctrls: conn %lu get crit failed.\n", 
230                                         conn->c_connid, 0, 0 );
231 #else
232                                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get crit failed.\n",
233                                         0, 0, 0 );
234 #endif
235                                 ldap_controls_free( op->o_ctrls );
236                                 op->o_ctrls = NULL;
237                                 rc = SLAPD_DISCONNECT;
238                                 errmsg = "decoding controls error";
239                                 goto return_results;
240                         }
241
242                         c->ldctl_iscritical = (crit != 0);
243                         tag = ber_peek_tag( ber, &len );
244                 }
245
246                 if( tag == LBER_OCTETSTRING ) {
247                         tag = ber_scanf( ber, "o", &c->ldctl_value );
248
249                         if( tag == LBER_ERROR ) {
250 #ifdef NEW_LOGGING
251                                 LDAP_LOG( OPERATION, INFO, "get_ctrls: conn %lu: "
252                                         "%s (%scritical): get value failed.\n",
253                                         conn->c_connid, c->ldctl_oid ? c->ldctl_oid : "(NULL)",
254                                         c->ldctl_iscritical ? "" : "non" );
255 #else
256                                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: conn %lu: "
257                                         "%s (%scritical): get value failed.\n",
258                                         conn->c_connid,
259                                         c->ldctl_oid ? c->ldctl_oid : "(NULL)",
260                                         c->ldctl_iscritical ? "" : "non" );
261 #endif
262                                 ldap_controls_free( op->o_ctrls );
263                                 op->o_ctrls = NULL;
264                                 rc = SLAPD_DISCONNECT;
265                                 errmsg = "decoding controls error";
266                                 goto return_results;
267                         }
268                 }
269
270 #ifdef NEW_LOGGING
271                 LDAP_LOG( OPERATION, INFO, 
272                         "get_ctrls: conn %lu oid=\"%s\" (%scritical)\n",
273                         conn->c_connid, c->ldctl_oid ? c->ldctl_oid : "(NULL)",
274                         c->ldctl_iscritical ? "" : "non" );
275 #else
276                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: oid=\"%s\" (%scritical)\n",
277                         c->ldctl_oid ? c->ldctl_oid : "(NULL)",
278                         c->ldctl_iscritical ? "" : "non",
279                         0 );
280 #endif
281
282                 sc = find_ctrl( c->ldctl_oid );
283                 if( sc != NULL ) {
284                         /* recognized control */
285                         slap_mask_t tagmask;
286                         switch( op->o_tag ) {
287                         case LDAP_REQ_ADD:
288                                 tagmask = SLAP_CTRL_ADD;
289                                 break;
290                         case LDAP_REQ_BIND:
291                                 tagmask = SLAP_CTRL_BIND;
292                                 break;
293                         case LDAP_REQ_COMPARE:
294                                 tagmask = SLAP_CTRL_COMPARE;
295                                 break;
296                         case LDAP_REQ_DELETE:
297                                 tagmask = SLAP_CTRL_DELETE;
298                                 break;
299                         case LDAP_REQ_MODIFY:
300                                 tagmask = SLAP_CTRL_MODIFY;
301                                 break;
302                         case LDAP_REQ_RENAME:
303                                 tagmask = SLAP_CTRL_RENAME;
304                                 break;
305                         case LDAP_REQ_SEARCH:
306                                 tagmask = SLAP_CTRL_SEARCH;
307                                 break;
308                         case LDAP_REQ_UNBIND:
309                                 tagmask = SLAP_CTRL_UNBIND;
310                                 break;
311                         case LDAP_REQ_EXTENDED:
312                                 /* FIXME: check list of extended operations */
313                                 tagmask = ~0U;
314                                 break;
315                         default:
316                                 rc = LDAP_OTHER;
317                                 errmsg = "controls internal error";
318                                 goto return_results;
319                         }
320
321                         if (( sc->sc_mask & tagmask ) == tagmask ) {
322                                 /* available extension */
323
324                                 if( !sc->sc_parse ) {
325                                         rc = LDAP_OTHER;
326                                         errmsg = "not yet implemented";
327                                         goto return_results;
328                                 }
329
330                                 rc = sc->sc_parse( conn, op, c, &errmsg );
331
332                                 if( rc != LDAP_SUCCESS ) goto return_results;
333
334                                 if( sc->sc_mask & SLAP_CTRL_FRONTEND ) {
335                                         /* kludge to disable backend_control() check */
336                                         c->ldctl_iscritical = 0;
337                                 }
338
339                         } else if( c->ldctl_iscritical ) {
340                                 /* unavailable CRITICAL control */
341                                 rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
342                                 errmsg = "critical extension is unavailable";
343                                 goto return_results;
344                         }
345
346                 } else if( c->ldctl_iscritical ) {
347                         /* unrecognized CRITICAL control */
348                         rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
349                         errmsg = "critical extension is not recognized";
350                         goto return_results;
351                 }
352         }
353
354 return_results:
355 #ifdef NEW_LOGGING
356         LDAP_LOG( OPERATION, RESULTS, 
357                 "get_ctrls: n=%d rc=%d err=%s\n", nctrls, rc, errmsg ? errmsg : "" );
358 #else
359         Debug( LDAP_DEBUG_TRACE, "<= get_ctrls: n=%d rc=%d err=%s\n",
360                 nctrls, rc, errmsg ? errmsg : "");
361 #endif
362
363         if( sendres && rc != LDAP_SUCCESS ) {
364                 if( rc == SLAPD_DISCONNECT ) {
365                         send_ldap_disconnect( conn, op, LDAP_PROTOCOL_ERROR, errmsg );
366                 } else {
367                         send_ldap_result( conn, op, rc,
368                                 NULL, errmsg, NULL, NULL );
369                 }
370         }
371
372         return rc;
373 }
374
375 static int parseManageDSAit (
376         Connection *conn,
377         Operation *op,
378         LDAPControl *ctrl,
379         const char **text )
380 {
381         if ( op->o_managedsait != SLAP_NO_CONTROL ) {
382                 *text = "manageDSAit control specified multiple times";
383                 return LDAP_PROTOCOL_ERROR;
384         }
385
386         if ( ctrl->ldctl_value.bv_len ) {
387                 *text = "manageDSAit control value not empty";
388                 return LDAP_PROTOCOL_ERROR;
389         }
390
391         op->o_managedsait = ctrl->ldctl_iscritical
392                 ? SLAP_CRITICAL_CONTROL
393                 : SLAP_NONCRITICAL_CONTROL;
394
395         return LDAP_SUCCESS;
396 }
397
398 #ifdef LDAP_CONTROL_SUBENTRIES
399 static int parseSubentries (
400         Connection *conn,
401         Operation *op,
402         LDAPControl *ctrl,
403         const char **text )
404 {
405         if ( op->o_subentries != SLAP_NO_CONTROL ) {
406                 *text = "subentries control specified multiple times";
407                 return LDAP_PROTOCOL_ERROR;
408         }
409
410         /* FIXME: should use BER library */
411         if( ( ctrl->ldctl_value.bv_len != 3 )
412                 && ( ctrl->ldctl_value.bv_val[0] != 0x01 )
413                 && ( ctrl->ldctl_value.bv_val[1] != 0x01 ))
414         {
415                 *text = "subentries control value encoding is bogus";
416                 return LDAP_PROTOCOL_ERROR;
417         }
418
419         op->o_subentries = ctrl->ldctl_iscritical
420                 ? SLAP_CRITICAL_CONTROL
421                 : SLAP_NONCRITICAL_CONTROL;
422
423         op->o_subentries_visibility = (ctrl->ldctl_value.bv_val[2] != 0x00);
424
425         return LDAP_SUCCESS;
426 }
427 #endif
428
429 #ifdef LDAP_CONTROL_NOOP
430 static int parseNoOp (
431         Connection *conn,
432         Operation *op,
433         LDAPControl *ctrl,
434         const char **text )
435 {
436         if ( op->o_noop != SLAP_NO_CONTROL ) {
437                 *text = "noop control specified multiple times";
438                 return LDAP_PROTOCOL_ERROR;
439         }
440
441         if ( ctrl->ldctl_value.bv_len ) {
442                 *text = "noop control value not empty";
443                 return LDAP_PROTOCOL_ERROR;
444         }
445
446         op->o_noop = ctrl->ldctl_iscritical
447                 ? SLAP_CRITICAL_CONTROL
448                 : SLAP_NONCRITICAL_CONTROL;
449
450         return LDAP_SUCCESS;
451 }
452 #endif
453
454 #ifdef LDAP_CONTROL_PAGEDRESULTS_REQUEST
455 static int parsePagedResults (
456         Connection *conn,
457         Operation *op,
458         LDAPControl *ctrl,
459         const char **text )
460 {
461         ber_tag_t tag;
462         ber_int_t size;
463         BerElement *ber;
464         struct berval cookie = { 0, NULL };
465
466         if ( op->o_pagedresults != SLAP_NO_CONTROL ) {
467                 *text = "paged results control specified multiple times";
468                 return LDAP_PROTOCOL_ERROR;
469         }
470
471         if ( ctrl->ldctl_value.bv_len == 0 ) {
472                 *text = "paged results control value is empty";
473                 return LDAP_PROTOCOL_ERROR;
474         }
475
476         /* Parse the control value
477          *      realSearchControlValue ::= SEQUENCE {
478          *              size    INTEGER (0..maxInt),
479          *                              -- requested page size from client
480          *                              -- result set size estimate from server
481          *              cookie  OCTET STRING
482          */
483         ber = ber_init( &ctrl->ldctl_value );
484         if( ber == NULL ) {
485                 *text = "internal error";
486                 return LDAP_OTHER;
487         }
488
489         tag = ber_scanf( ber, "{im}", &size, &cookie );
490         (void) ber_free( ber, 1 );
491
492         if( tag == LBER_ERROR ) {
493                 *text = "paged results control could not be decoded";
494                 return LDAP_PROTOCOL_ERROR;
495         }
496
497         if( size <= 0 ) {
498                 *text = "paged results control size invalid";
499                 return LDAP_PROTOCOL_ERROR;
500         }
501
502         if( cookie.bv_len ) {
503                 PagedResultsCookie reqcookie;
504                 if( cookie.bv_len != sizeof( reqcookie ) ) {
505                         /* bad cookie */
506                         *text = "paged results cookie is invalid";
507                         return LDAP_PROTOCOL_ERROR;
508                 }
509
510                 AC_MEMCPY( &reqcookie, cookie.bv_val, sizeof( reqcookie ));
511
512                 if( reqcookie > op->o_pagedresults_state.ps_cookie ) {
513                         /* bad cookie */
514                         *text = "paged results cookie is invalid";
515                         return LDAP_PROTOCOL_ERROR;
516
517                 } else if( reqcookie < op->o_pagedresults_state.ps_cookie ) {
518                         *text = "paged results cookie is invalid or old";
519                         return LDAP_UNWILLING_TO_PERFORM;
520                 }
521         }
522
523         op->o_pagedresults_state.ps_cookie = op->o_opid;
524         op->o_pagedresults_size = size;
525
526         op->o_pagedresults = ctrl->ldctl_iscritical
527                 ? SLAP_CRITICAL_CONTROL
528                 : SLAP_NONCRITICAL_CONTROL;
529
530         return LDAP_SUCCESS;
531 }
532 #endif
533
534 #ifdef LDAP_CONTROL_VALUESRETURNFILTER
535 int parseValuesReturnFilter (
536         Connection *conn,
537         Operation *op,
538         LDAPControl *ctrl,
539         const char **text )
540 {
541         int             rc;
542         BerElement      *ber;
543         struct berval   fstr = { 0, NULL };
544         const char *err_msg = "";
545
546         if ( op->o_valuesreturnfilter != SLAP_NO_CONTROL ) {
547                 *text = "valuesreturnfilter control specified multiple times";
548                 return LDAP_PROTOCOL_ERROR;
549         }
550
551         ber = ber_init( &(ctrl->ldctl_value) );
552         if (ber == NULL) {
553                 *text = "internal error";
554                 return LDAP_OTHER;
555         }
556         
557         rc = get_vrFilter( conn, ber, &(op->vrFilter), &err_msg);
558
559         if( rc != LDAP_SUCCESS ) {
560                 text = &err_msg;
561                 if( rc == SLAPD_DISCONNECT ) {
562                         send_ldap_disconnect( conn, op,
563                                 LDAP_PROTOCOL_ERROR, *text );
564                 } else {
565                         send_ldap_result( conn, op, rc,
566                                 NULL, *text, NULL, NULL );
567                 }
568                 if( fstr.bv_val != NULL) free( fstr.bv_val );
569                 if( op->vrFilter != NULL) vrFilter_free( op->vrFilter ); 
570
571         } else {
572                 vrFilter2bv( op->vrFilter, &fstr );
573         }
574
575 #ifdef NEW_LOGGING
576         LDAP_LOG( OPERATION, ARGS, 
577                 "parseValuesReturnFilter: conn %d       vrFilter: %s\n", 
578                 conn->c_connid, fstr.bv_len ? fstr.bv_val : "empty" , 0 );
579 #else
580         Debug( LDAP_DEBUG_ARGS, "       vrFilter: %s\n",
581                 fstr.bv_len ? fstr.bv_val : "empty", 0, 0 );
582 #endif
583
584         op->o_valuesreturnfilter = ctrl->ldctl_iscritical
585                 ? SLAP_CRITICAL_CONTROL
586                 : SLAP_NONCRITICAL_CONTROL;
587
588         return LDAP_SUCCESS;
589 }
590 #endif