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