]> git.sur5r.net Git - openldap/blob - servers/slapd/controls.c
7c3bf61b6bdab7126c06800d4b3bb505e0849906
[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
50 static struct slap_control {
51         char *sc_oid;
52         slap_mask_t sc_mask;
53         char **sc_extendedops;
54         SLAP_CTRL_PARSE_FN *sc_parse;
55
56 } supportedControls[] = {
57         { LDAP_CONTROL_MANAGEDSAIT,
58                 SLAP_CTRL_ACCESS, NULL,
59                 parseManageDSAit },
60 #ifdef LDAP_CONTROL_SUBENTRIES
61         { LDAP_CONTROL_SUBENTRIES,
62                 SLAP_CTRL_SEARCH, NULL,
63                 parseSubentries },
64 #endif
65 #ifdef LDAP_CONTROL_NOOP
66         { LDAP_CONTROL_NOOP,
67                 SLAP_CTRL_UPDATE, NULL,
68                 parseNoOp },
69 #endif
70         { NULL }
71 };
72
73 char *
74 get_supported_ctrl(int index)
75 {
76         return supportedControls[index].sc_oid;
77 }
78
79 static struct slap_control *
80 find_ctrl( const char *oid )
81 {
82         int i;
83         for( i=0; supportedControls[i].sc_oid; i++ ) {
84                 if( strcmp( oid, supportedControls[i].sc_oid ) == 0 ) {
85                         return &supportedControls[i];
86                 }
87         }
88         return NULL;
89 }
90
91 int get_ctrls(
92         Connection *conn,
93         Operation *op,
94         int sendres )
95 {
96         int nctrls;
97         ber_tag_t tag;
98         ber_len_t len;
99         char *opaque;
100         BerElement *ber = op->o_ber;
101         struct slap_control *sc;
102         int rc = LDAP_SUCCESS;
103         const char *errmsg = NULL;
104
105         len = ber_pvt_ber_remaining(ber);
106
107         if( len == 0) {
108                 /* no controls */
109                 rc = LDAP_SUCCESS;
110                 return rc;
111         }
112
113         if(( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) {
114                 if( tag == LBER_ERROR ) {
115                         rc = SLAPD_DISCONNECT;
116                         errmsg = "unexpected data in PDU";
117                 }
118
119                 goto return_results;
120         }
121
122 #ifdef NEW_LOGGING
123         LDAP_LOG(( "operation", LDAP_LEVEL_ENTRY,
124                 "get_ctrls: conn %d\n", conn->c_connid ));
125 #else
126         Debug( LDAP_DEBUG_TRACE, "=> get_ctrls\n", 0, 0, 0 );
127 #endif
128         if( op->o_protocol < LDAP_VERSION3 ) {
129                 rc = SLAPD_DISCONNECT;
130                 errmsg = "controls require LDAPv3";
131                 goto return_results;
132         }
133
134         /* one for first control, one for termination */
135         op->o_ctrls = ch_malloc( 2 * sizeof(LDAPControl *) );
136
137 #if 0
138         if( op->ctrls == NULL ) {
139                 rc = LDAP_NO_MEMORY;
140                 errmsg = "no memory";
141                 goto return_results;
142         }
143 #endif
144
145         op->o_ctrls[nctrls=0] = NULL;
146
147         /* step through each element */
148         for( tag = ber_first_element( ber, &len, &opaque );
149                 tag != LBER_ERROR;
150                 tag = ber_next_element( ber, &len, opaque ) )
151         {
152                 LDAPControl *c;
153                 LDAPControl **tctrls;
154
155                 c = ch_calloc( 1, sizeof(LDAPControl) );
156
157 #if 0
158                 if( c == NULL ) {
159                         ldap_controls_free(op->o_ctrls);
160                         op->o_ctrls = NULL;
161
162                         rc = LDAP_NO_MEMORY;
163                         errmsg = "no memory";
164                         goto return_results;
165                 }
166 #endif
167
168                 /* allocate pointer space for current controls (nctrls)
169                  * + this control + extra NULL
170                  */
171                 tctrls = ch_realloc( op->o_ctrls,
172                         (nctrls+2) * sizeof(LDAPControl *));
173
174 #if 0
175                 if( tctrls == NULL ) {
176                         ch_free( c );
177                         ldap_controls_free(op->o_ctrls);
178                         op->o_ctrls = NULL;
179
180                         rc = LDAP_NO_MEMORY;
181                         errmsg = "no memory";
182                         goto return_results;
183                 }
184 #endif
185                 op->o_ctrls = tctrls;
186
187                 op->o_ctrls[nctrls++] = c;
188                 op->o_ctrls[nctrls] = NULL;
189
190                 tag = ber_scanf( ber, "{a" /*}*/, &c->ldctl_oid );
191
192                 if( tag == LBER_ERROR ) {
193 #ifdef NEW_LOGGING
194                         LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
195                                 "get_ctrls: conn %d get OID failed.\n",
196                                 conn->c_connid ));
197 #else
198                         Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get oid failed.\n",
199                                 0, 0, 0 );
200 #endif
201                         ldap_controls_free( op->o_ctrls );
202                         op->o_ctrls = NULL;
203                         rc = SLAPD_DISCONNECT;
204                         errmsg = "decoding controls error";
205                         goto return_results;
206                 }
207
208                 tag = ber_peek_tag( ber, &len );
209
210                 if( tag == LBER_BOOLEAN ) {
211                         ber_int_t crit;
212                         tag = ber_scanf( ber, "b", &crit );
213
214                         if( tag == LBER_ERROR ) {
215 #ifdef NEW_LOGGING
216                                 LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
217                                         "get_ctrls: conn %d get crit failed.\n",
218                                         conn->c_connid ));
219 #else
220                                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get crit 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                         c->ldctl_iscritical = (crit != 0);
231                         tag = ber_peek_tag( ber, &len );
232                 }
233
234                 if( tag == LBER_OCTETSTRING ) {
235                         tag = ber_scanf( ber, "o", &c->ldctl_value );
236
237                         if( tag == LBER_ERROR ) {
238 #ifdef NEW_LOGGING
239                                 LDAP_LOG(( "operation", LDAP_LEVEL_INFO, "get_ctrls: conn %d: "
240                                         "%s (%scritical): get value failed.\n",
241                                         conn->c_connid,
242                                         c->ldctl_oid ? c->ldctl_oid : "(NULL)",
243                                         c->ldctl_iscritical ? "" : "non" ));
244 #else
245                                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: conn %d: "
246                                         "%s (%scritical): get value failed.\n",
247                                         conn->c_connid,
248                                         c->ldctl_oid ? c->ldctl_oid : "(NULL)",
249                                         c->ldctl_iscritical ? "" : "non" );
250 #endif
251                                 ldap_controls_free( op->o_ctrls );
252                                 op->o_ctrls = NULL;
253                                 rc = SLAPD_DISCONNECT;
254                                 errmsg = "decoding controls error";
255                                 goto return_results;
256                         }
257                 }
258
259 #ifdef NEW_LOGGING
260                 LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
261                         "get_ctrls: conn %d oid=\"%s\" (%scritical)\n",
262                         conn->c_connid,
263                         c->ldctl_oid ? c->ldctl_oid : "(NULL)",
264                         c->ldctl_iscritical ? "" : "non" ));
265 #else
266                 Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: oid=\"%s\" (%scritical)\n",
267                         c->ldctl_oid ? c->ldctl_oid : "(NULL)",
268                         c->ldctl_iscritical ? "" : "non",
269                         0 );
270 #endif
271
272                 sc = find_ctrl( c->ldctl_oid );
273                 if( sc != NULL ) {
274                         /* recognized control */
275                         slap_mask_t tagmask;
276                         switch( op->o_tag ) {
277                         case LDAP_REQ_ADD:
278                                 tagmask = SLAP_CTRL_ADD;
279                                 break;
280                         case LDAP_REQ_BIND:
281                                 tagmask = SLAP_CTRL_BIND;
282                                 break;
283                         case LDAP_REQ_COMPARE:
284                                 tagmask = SLAP_CTRL_COMPARE;
285                                 break;
286                         case LDAP_REQ_DELETE:
287                                 tagmask = SLAP_CTRL_DELETE;
288                                 break;
289                         case LDAP_REQ_MODIFY:
290                                 tagmask = SLAP_CTRL_MODIFY;
291                                 break;
292                         case LDAP_REQ_RENAME:
293                                 tagmask = SLAP_CTRL_RENAME;
294                                 break;
295                         case LDAP_REQ_SEARCH:
296                                 tagmask = SLAP_CTRL_SEARCH;
297                                 break;
298                         case LDAP_REQ_UNBIND:
299                                 tagmask = SLAP_CTRL_UNBIND;
300                                 break;
301                         case LDAP_REQ_EXTENDED:
302                                 /* FIXME: check list of extended operations */
303                                 tagmask = ~0U;
304                                 break;
305                         default:
306                                 rc = LDAP_OTHER;
307                                 errmsg = "controls internal error";
308                                 goto return_results;
309                         }
310
311                         if (( sc->sc_mask & tagmask ) == tagmask ) {
312                                 /* available extension */
313
314                                 if( !sc->sc_parse ) {
315                                         rc = LDAP_OTHER;
316                                         errmsg = "not yet implemented";
317                                         goto return_results;
318                                 }
319
320                                 rc = sc->sc_parse( conn, op, c, &errmsg );
321
322                                 if( rc != LDAP_SUCCESS ) goto return_results;
323
324                                 if( sc->sc_mask & SLAP_CTRL_FRONTEND ) {
325                                         /* kludge to disable backend_control() check */
326                                         c->ldctl_iscritical = 0;
327                                 }
328
329                         } else if( c->ldctl_iscritical ) {
330                                 /* unavailable CRITICAL control */
331                                 rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
332                                 errmsg = "critical extension is unavailable";
333                                 goto return_results;
334                         }
335
336                 } else if( c->ldctl_iscritical ) {
337                         /* unrecognized CRITICAL control */
338                         rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
339                         errmsg = "critical extension is not recognized";
340                         goto return_results;
341                 }
342         }
343
344 return_results:
345 #ifdef NEW_LOGGING
346         LDAP_LOG(( "operation", LDAP_LEVEL_RESULTS,
347                 "get_ctrls: conn=%d     n=%d rc=%d err=%s\n",
348                 conn->c_connid, nctrls, rc, errmsg ? errmsg : "" ));
349 #else
350         Debug( LDAP_DEBUG_TRACE, "<= get_ctrls: n=%d rc=%d err=%s\n",
351                 nctrls, rc, errmsg ? errmsg : "");
352 #endif
353
354         if( sendres && rc != LDAP_SUCCESS ) {
355                 if( rc == SLAPD_DISCONNECT ) {
356                         send_ldap_disconnect( conn, op, LDAP_PROTOCOL_ERROR, errmsg );
357                 } else {
358                         send_ldap_result( conn, op, rc,
359                                 NULL, errmsg, NULL, NULL );
360                 }
361         }
362
363         return rc;
364 }
365
366 static int parseManageDSAit (
367         Connection *conn,
368         Operation *op,
369         LDAPControl *ctrl,
370         const char **text )
371 {
372         if ( op->o_managedsait != SLAP_NO_CONTROL ) {
373                 *text = "manageDSAit control specified multiple times";
374                 return LDAP_PROTOCOL_ERROR;
375         }
376
377         if ( ctrl->ldctl_value.bv_len ) {
378                 *text = "manageDSAit control value not empty";
379                 return LDAP_PROTOCOL_ERROR;
380         }
381
382         op->o_managedsait = ctrl->ldctl_iscritical
383                 ? SLAP_CRITICAL_CONTROL
384                 : SLAP_NONCRITICAL_CONTROL;
385
386         return LDAP_SUCCESS;
387 }
388
389 #ifdef LDAP_CONTROL_SUBENTRIES
390 static int parseSubentries (
391         Connection *conn,
392         Operation *op,
393         LDAPControl *ctrl,
394         const char **text )
395 {
396         if ( op->o_subentries != SLAP_NO_CONTROL ) {
397                 *text = "subentries control specified multiple times";
398                 return LDAP_PROTOCOL_ERROR;
399         }
400
401         /* FIXME: should use BER library */
402         if( ( ctrl->ldctl_value.bv_len != 3 )
403                 && ( ctrl->ldctl_value.bv_val[0] != 0x01 )
404                 && ( ctrl->ldctl_value.bv_val[1] != 0x01 ))
405         {
406                 *text = "subentries control value encoding is bogus";
407                 return LDAP_PROTOCOL_ERROR;
408         }
409
410         op->o_subentries = ctrl->ldctl_iscritical
411                 ? SLAP_CRITICAL_CONTROL
412                 : SLAP_NONCRITICAL_CONTROL;
413
414         op->o_subentries_visibility = (ctrl->ldctl_value.bv_val[2] != 0x00);
415
416         return LDAP_SUCCESS;
417 }
418 #endif
419
420 #ifdef LDAP_CONTROL_NOOP
421 static int parseNoOp (
422         Connection *conn,
423         Operation *op,
424         LDAPControl *ctrl,
425         const char **text )
426 {
427         if ( op->o_noop != SLAP_NO_CONTROL ) {
428                 *text = "noop control specified multiple times";
429                 return LDAP_PROTOCOL_ERROR;
430         }
431
432         if ( ctrl->ldctl_value.bv_len ) {
433                 *text = "noop control value not empty";
434                 return LDAP_PROTOCOL_ERROR;
435         }
436
437         op->o_noop = ctrl->ldctl_iscritical
438                 ? SLAP_CRITICAL_CONTROL
439                 : SLAP_NONCRITICAL_CONTROL;
440
441         return LDAP_SUCCESS;
442 }
443 #endif
444