xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/deref.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: deref.c,v 1.1.1.3 2014/05/28 09:58:52 tron Exp $	*/
2 
3 /* deref.c - dereference overlay */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 1998-2014 The OpenLDAP Foundation.
8  * Portions Copyright 2008 Pierangelo Masarati.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted only as authorized by the OpenLDAP
13  * Public License.
14  *
15  * A copy of this license is available in the file LICENSE in the
16  * top-level directory of the distribution or, alternatively, at
17  * <http://www.OpenLDAP.org/license.html>.
18  */
19 /* ACKNOWLEDGEMENTS:
20  * This work was initially developed by Pierangelo Masarati
21  * for inclusion in OpenLDAP Software.
22  */
23 
24 #include "portable.h"
25 
26 #ifdef SLAPD_OVER_DEREF
27 
28 #include <stdio.h>
29 
30 #include "ac/string.h"
31 #include "ac/socket.h"
32 
33 #include "slap.h"
34 #include "config.h"
35 
36 #include "lutil.h"
37 
38 /*
39  * 1. Specification
40  *
41  * 1.1. Request
42  *
43  *  controlValue ::= SEQUENCE OF derefSpec DerefSpec
44  *
45  *  DerefSpec ::= SEQUENCE {
46  *      derefAttr       attributeDescription,    ; DN-valued
47  *      attributes      AttributeList }
48  *
49  *  AttributeList ::= SEQUENCE OF attr AttributeDescription
50  *
51  *  derefAttr MUST be unique within controlValue
52  *
53  *
54  * 1.2. Response
55  *
56  *  controlValue ::= SEQUENCE OF DerefRes
57  *
58  * From RFC 4511:
59  *      PartialAttribute ::= SEQUENCE {
60  *           type       AttributeDescription,
61  *           vals       SET OF value AttributeValue }
62  *
63  *      PartialAttributeList ::= SEQUENCE OF
64  *                           partialAttribute PartialAttribute
65  *
66  *  DerefRes ::= SEQUENCE {
67  *      derefAttr       AttributeDescription,
68  *      derefVal        LDAPDN,
69  *      attrVals        [0] PartialAttributeList OPTIONAL }
70  *
71  *  If vals is empty, partialAttribute is omitted.
72  *  If all vals in attrVals are empty, attrVals is omitted.
73  *
74  * 2. Examples
75  *
76  * 2.1. Example
77  *
78  * 2.1.1. Request
79  *
80  * { { member, { GUID, SID } }, { memberOf, { GUID, SID } } }
81  *
82  * 2.1.2. Response
83  *
84  * { { memberOf, "cn=abartlet,cn=users,dc=abartlet,dc=net",
85  *     { { GUID, [ "0bc11d00-e431-40a0-8767-344a320142fa" ] },
86  *       { SID, [ "S-1-2-3-2345" ] } } },
87  *   { memberOf, "cn=ando,cn=users,dc=sys-net,dc=it",
88  *     { { GUID, [ "0bc11d00-e431-40a0-8767-344a320142fb" ] },
89  *       { SID, [ "S-1-2-3-2346" ] } } } }
90  *
91  * 2.2. Example
92  *
93  * 2.2.1. Request
94  *
95  * { { member, { cn, uid, drink } } }
96  *
97  * 2.2.2. Response
98  *
99  * { { member, "cn=ando,cn=users,dc=sys-net,dc=it",
100  *     { { cn, [ "ando", "Pierangelo Masarati" ] },
101  *       { uid, [ "ando" ] } } },
102  *   { member, "dc=sys-net,dc=it" } }
103  *
104  *
105  * 3. Security considerations
106  *
107  * The control result must not disclose information the client's
108  * identity could not have accessed directly by performing the related
109  * search operations.  The presence of a derefVal in the control
110  * response does not imply neither the existence of nor any access
111  * privilege to the corresponding entry.  It is merely a consequence
112  * of the read access the client's identity has on the corresponding
113  * attribute's value.
114  */
115 
116 #define o_deref			o_ctrlflag[deref_cid]
117 #define o_ctrlderef		o_controls[deref_cid]
118 
119 typedef struct DerefSpec {
120 	AttributeDescription	*ds_derefAttr;
121 	AttributeDescription	**ds_attributes;
122 	int			ds_nattrs;
123 	struct DerefSpec	*ds_next;
124 } DerefSpec;
125 
126 typedef struct DerefVal {
127 	struct berval	dv_derefSpecVal;
128 	BerVarray	*dv_attrVals;
129 } DerefVal;
130 
131 typedef struct DerefRes {
132 	DerefSpec		dr_spec;
133 	DerefVal		*dr_vals;
134 	struct DerefRes		*dr_next;
135 } DerefRes;
136 
137 typedef struct deref_cb_t {
138 	slap_overinst *dc_on;
139 	DerefSpec *dc_ds;
140 } deref_cb_t;
141 
142 static int			deref_cid;
143 static slap_overinst 		deref;
144 static int ov_count;
145 
146 static int
147 deref_parseCtrl (
148 	Operation *op,
149 	SlapReply *rs,
150 	LDAPControl *ctrl )
151 {
152 	ber_tag_t tag;
153 	BerElementBuffer berbuf;
154 	BerElement *ber = (BerElement *)&berbuf;
155 	ber_len_t len;
156 	char *last;
157 	DerefSpec *dshead = NULL, **dsp = &dshead;
158 	BerVarray attributes = NULL;
159 
160 	if ( op->o_deref != SLAP_CONTROL_NONE ) {
161 		rs->sr_text = "Dereference control specified multiple times";
162 		return LDAP_PROTOCOL_ERROR;
163 	}
164 
165 	if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
166 		rs->sr_text = "Dereference control value is absent";
167 		return LDAP_PROTOCOL_ERROR;
168 	}
169 
170 	if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
171 		rs->sr_text = "Dereference control value is empty";
172 		return LDAP_PROTOCOL_ERROR;
173 	}
174 
175 	ber_init2( ber, &ctrl->ldctl_value, 0 );
176 
177 	for ( tag = ber_first_element( ber, &len, &last );
178 		tag != LBER_DEFAULT;
179 		tag = ber_next_element( ber, &len, last ) )
180 	{
181 		struct berval derefAttr;
182 		DerefSpec *ds, *dstmp;
183 		const char *text;
184 		int rc;
185 		ber_len_t cnt = sizeof(struct berval);
186 		ber_len_t off = 0;
187 
188 		if ( ber_scanf( ber, "{m{M}}", &derefAttr, &attributes, &cnt, off ) == LBER_ERROR )
189 		{
190 			rs->sr_text = "Dereference control: derefSpec decoding error";
191 			rs->sr_err = LDAP_PROTOCOL_ERROR;
192 			goto done;
193 		}
194 
195 		ds = (DerefSpec *)op->o_tmpcalloc( 1,
196 			sizeof(DerefSpec) + sizeof(AttributeDescription *)*(cnt + 1),
197 			op->o_tmpmemctx );
198 		ds->ds_attributes = (AttributeDescription **)&ds[ 1 ];
199 		ds->ds_nattrs = cnt;
200 
201 		rc = slap_bv2ad( &derefAttr, &ds->ds_derefAttr, &text );
202 		if ( rc != LDAP_SUCCESS ) {
203 			rs->sr_text = "Dereference control: derefAttr decoding error";
204 			rs->sr_err = LDAP_PROTOCOL_ERROR;
205 			goto done;
206 		}
207 
208 		for ( dstmp = dshead; dstmp && dstmp != ds; dstmp = dstmp->ds_next ) {
209 			if ( dstmp->ds_derefAttr == ds->ds_derefAttr ) {
210 				rs->sr_text = "Dereference control: derefAttr must be unique within control";
211 				rs->sr_err = LDAP_PROTOCOL_ERROR;
212 				goto done;
213 			}
214 		}
215 
216 		if ( !( ds->ds_derefAttr->ad_type->sat_syntax->ssyn_flags & SLAP_SYNTAX_DN )) {
217 			if ( ctrl->ldctl_iscritical ) {
218 				rs->sr_text = "Dereference control: derefAttr syntax not distinguishedName";
219 				rs->sr_err = LDAP_PROTOCOL_ERROR;
220 				goto done;
221 			}
222 
223 			rs->sr_err = LDAP_SUCCESS;
224 			goto justcleanup;
225 		}
226 
227 		for ( cnt = 0; !BER_BVISNULL( &attributes[ cnt ] ); cnt++ ) {
228 			rc = slap_bv2ad( &attributes[ cnt ], &ds->ds_attributes[ cnt ], &text );
229 			if ( rc != LDAP_SUCCESS ) {
230 				rs->sr_text = "Dereference control: attribute decoding error";
231 				rs->sr_err = LDAP_PROTOCOL_ERROR;
232 				goto done;
233 			}
234 		}
235 
236 		ber_memfree_x( attributes, op->o_tmpmemctx );
237 		attributes = NULL;
238 
239 		*dsp = ds;
240 		dsp = &ds->ds_next;
241 	}
242 
243 	op->o_ctrlderef = (void *)dshead;
244 
245 	op->o_deref = ctrl->ldctl_iscritical
246 		? SLAP_CONTROL_CRITICAL
247 		: SLAP_CONTROL_NONCRITICAL;
248 
249 	rs->sr_err = LDAP_SUCCESS;
250 
251 done:;
252 	if ( rs->sr_err != LDAP_SUCCESS ) {
253 justcleanup:;
254 		for ( ; dshead; ) {
255 			DerefSpec *dsnext = dshead->ds_next;
256 			op->o_tmpfree( dshead, op->o_tmpmemctx );
257 			dshead = dsnext;
258 		}
259 	}
260 
261 	if ( attributes != NULL ) {
262 		ber_memfree_x( attributes, op->o_tmpmemctx );
263 	}
264 
265 	return rs->sr_err;
266 }
267 
268 static int
269 deref_cleanup( Operation *op, SlapReply *rs )
270 {
271 	if ( rs->sr_type == REP_RESULT || rs->sr_err == SLAPD_ABANDON ) {
272 		op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
273 		op->o_callback = NULL;
274 
275 		op->o_tmpfree( op->o_ctrlderef, op->o_tmpmemctx );
276 		op->o_ctrlderef = NULL;
277 	}
278 
279 	return SLAP_CB_CONTINUE;
280 }
281 
282 static int
283 deref_response( Operation *op, SlapReply *rs )
284 {
285 	int rc = SLAP_CB_CONTINUE;
286 
287 	if ( rs->sr_type == REP_SEARCH ) {
288 		BerElementBuffer berbuf;
289 		BerElement *ber = (BerElement *) &berbuf;
290 		deref_cb_t *dc = (deref_cb_t *)op->o_callback->sc_private;
291 		DerefSpec *ds;
292 		DerefRes *dr, *drhead = NULL, **drp = &drhead;
293 		struct berval bv = BER_BVNULL;
294 		int nDerefRes = 0, nDerefVals = 0, nAttrs = 0, nVals = 0;
295 		struct berval ctrlval;
296 		LDAPControl *ctrl, *ctrlsp[2];
297 		AccessControlState acl_state = ACL_STATE_INIT;
298 		static char dummy = '\0';
299 		Entry *ebase;
300 		int i;
301 
302 		rc = overlay_entry_get_ov( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &ebase, dc->dc_on );
303 		if ( rc != LDAP_SUCCESS || ebase == NULL ) {
304 			return SLAP_CB_CONTINUE;
305 		}
306 
307 		for ( ds = dc->dc_ds; ds; ds = ds->ds_next ) {
308 			Attribute *a = attr_find( ebase->e_attrs, ds->ds_derefAttr );
309 
310 			if ( a != NULL ) {
311 				DerefVal *dv;
312 				BerVarray *bva;
313 
314 				if ( !access_allowed( op, rs->sr_entry, a->a_desc,
315 						NULL, ACL_READ, &acl_state ) )
316 				{
317 					continue;
318 				}
319 
320 				dr = op->o_tmpcalloc( 1,
321 					sizeof( DerefRes ) + ( sizeof( DerefVal ) + sizeof( BerVarray * ) * ds->ds_nattrs ) * ( a->a_numvals + 1 ),
322 					op->o_tmpmemctx );
323 				dr->dr_spec = *ds;
324 				dv = dr->dr_vals = (DerefVal *)&dr[ 1 ];
325 				bva = (BerVarray *)&dv[ a->a_numvals + 1 ];
326 
327 				bv.bv_len += ds->ds_derefAttr->ad_cname.bv_len;
328 				nAttrs++;
329 				nDerefRes++;
330 
331 				for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
332 					Entry *e = NULL;
333 
334 					dv[ i ].dv_attrVals = bva;
335 					bva += ds->ds_nattrs;
336 
337 
338 					if ( !access_allowed( op, rs->sr_entry, a->a_desc,
339 							&a->a_nvals[ i ], ACL_READ, &acl_state ) )
340 					{
341 						dv[ i ].dv_derefSpecVal.bv_val = &dummy;
342 						continue;
343 					}
344 
345 					ber_dupbv_x( &dv[ i ].dv_derefSpecVal, &a->a_vals[ i ], op->o_tmpmemctx );
346 					bv.bv_len += dv[ i ].dv_derefSpecVal.bv_len;
347 					nVals++;
348 					nDerefVals++;
349 
350 					rc = overlay_entry_get_ov( op, &a->a_nvals[ i ], NULL, NULL, 0, &e, dc->dc_on );
351 					if ( rc == LDAP_SUCCESS && e != NULL ) {
352 						int j;
353 
354 						if ( access_allowed( op, e, slap_schema.si_ad_entry,
355 							NULL, ACL_READ, NULL ) )
356 						{
357 							for ( j = 0; j < ds->ds_nattrs; j++ ) {
358 								Attribute *aa;
359 
360 								if ( !access_allowed( op, e, ds->ds_attributes[ j ], NULL,
361 									ACL_READ, &acl_state ) )
362 								{
363 									continue;
364 								}
365 
366 								aa = attr_find( e->e_attrs, ds->ds_attributes[ j ] );
367 								if ( aa != NULL ) {
368 									unsigned k, h, last = aa->a_numvals;
369 
370 									ber_bvarray_dup_x( &dv[ i ].dv_attrVals[ j ],
371 										aa->a_vals, op->o_tmpmemctx );
372 
373 									bv.bv_len += ds->ds_attributes[ j ]->ad_cname.bv_len;
374 
375 									for ( k = 0, h = 0; k < aa->a_numvals; k++ ) {
376 										if ( !access_allowed( op, e,
377 											aa->a_desc,
378 											&aa->a_nvals[ k ],
379 											ACL_READ, &acl_state ) )
380 										{
381 											op->o_tmpfree( dv[ i ].dv_attrVals[ j ][ h ].bv_val,
382 												op->o_tmpmemctx );
383 											dv[ i ].dv_attrVals[ j ][ h ] = dv[ i ].dv_attrVals[ j ][ --last ];
384 											BER_BVZERO( &dv[ i ].dv_attrVals[ j ][ last ] );
385 											continue;
386 										}
387 										bv.bv_len += dv[ i ].dv_attrVals[ j ][ h ].bv_len;
388 										nVals++;
389 										h++;
390 									}
391 									nAttrs++;
392 								}
393 							}
394 						}
395 
396 						overlay_entry_release_ov( op, e, 0, dc->dc_on );
397 					}
398 				}
399 
400 				*drp = dr;
401 				drp = &dr->dr_next;
402 			}
403 		}
404 		overlay_entry_release_ov( op, ebase, 0, dc->dc_on );
405 
406 		if ( drhead == NULL ) {
407 			return SLAP_CB_CONTINUE;
408 		}
409 
410 		/* cook the control value */
411 		bv.bv_len += nVals * sizeof(struct berval)
412 			+ nAttrs * sizeof(struct berval)
413 			+ nDerefVals * sizeof(DerefVal)
414 			+ nDerefRes * sizeof(DerefRes);
415 		bv.bv_val = op->o_tmpalloc( bv.bv_len, op->o_tmpmemctx );
416 
417 		ber_init2( ber, &bv, LBER_USE_DER );
418 		ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
419 
420 		rc = ber_printf( ber, "{" /*}*/ );
421 		for ( dr = drhead; dr != NULL; dr = dr->dr_next ) {
422 			for ( i = 0; !BER_BVISNULL( &dr->dr_vals[ i ].dv_derefSpecVal ); i++ ) {
423 				int j, first = 1;
424 
425 				if ( dr->dr_vals[ i ].dv_derefSpecVal.bv_val == &dummy ) {
426 					continue;
427 				}
428 
429 				rc = ber_printf( ber, "{OO" /*}*/,
430 					&dr->dr_spec.ds_derefAttr->ad_cname,
431 					&dr->dr_vals[ i ].dv_derefSpecVal );
432 				op->o_tmpfree( dr->dr_vals[ i ].dv_derefSpecVal.bv_val, op->o_tmpmemctx );
433 				for ( j = 0; j < dr->dr_spec.ds_nattrs; j++ ) {
434 					if ( dr->dr_vals[ i ].dv_attrVals[ j ] != NULL ) {
435 						if ( first ) {
436 							rc = ber_printf( ber, "t{" /*}*/,
437 								(LBER_CONSTRUCTED|LBER_CLASS_CONTEXT) );
438 							first = 0;
439 						}
440 						rc = ber_printf( ber, "{O[W]}",
441 							&dr->dr_spec.ds_attributes[ j ]->ad_cname,
442 							dr->dr_vals[ i ].dv_attrVals[ j ] );
443 						op->o_tmpfree( dr->dr_vals[ i ].dv_attrVals[ j ],
444 							op->o_tmpmemctx );
445 					}
446 				}
447 				if ( !first ) {
448 					rc = ber_printf( ber, /*{{*/ "}N}" );
449 				} else {
450 					rc = ber_printf( ber, /*{*/ "}" );
451 				}
452 			}
453 		}
454 		rc = ber_printf( ber, /*{*/ "}" );
455 		if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
456 			if ( op->o_deref == SLAP_CONTROL_CRITICAL ) {
457 				rc = LDAP_CONSTRAINT_VIOLATION;
458 
459 			} else {
460 				rc = SLAP_CB_CONTINUE;
461 			}
462 			goto cleanup;
463 		}
464 
465 		ctrl = op->o_tmpcalloc( 1,
466 			sizeof( LDAPControl ) + ctrlval.bv_len + 1,
467 			op->o_tmpmemctx );
468 		ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ];
469 		ctrl->ldctl_oid = LDAP_CONTROL_X_DEREF;
470 		ctrl->ldctl_iscritical = 0;
471 		ctrl->ldctl_value.bv_len = ctrlval.bv_len;
472 		AC_MEMCPY( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len );
473 		ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0';
474 
475 		ber_free_buf( ber );
476 
477 		ctrlsp[0] = ctrl;
478 		ctrlsp[1] = NULL;
479 		slap_add_ctrls( op, rs, ctrlsp );
480 
481 		rc = SLAP_CB_CONTINUE;
482 
483 cleanup:;
484 		/* release all */
485 		for ( ; drhead != NULL; ) {
486 			DerefRes *drnext = drhead->dr_next;
487 			op->o_tmpfree( drhead, op->o_tmpmemctx );
488 			drhead = drnext;
489 		}
490 
491 	} else if ( rs->sr_type == REP_RESULT ) {
492 		rc = deref_cleanup( op, rs );
493 	}
494 
495 	return rc;
496 }
497 
498 static int
499 deref_op_search( Operation *op, SlapReply *rs )
500 {
501 	if ( op->o_deref ) {
502 		slap_callback *sc;
503 		deref_cb_t *dc;
504 
505 		sc = op->o_tmpcalloc( 1, sizeof( slap_callback ) + sizeof( deref_cb_t ), op->o_tmpmemctx );
506 
507 		dc = (deref_cb_t *)&sc[ 1 ];
508 		dc->dc_on = (slap_overinst *)op->o_bd->bd_info;
509 		dc->dc_ds = (DerefSpec *)op->o_ctrlderef;
510 
511 		sc->sc_response = deref_response;
512 		sc->sc_cleanup = deref_cleanup;
513 		sc->sc_private = (void *)dc;
514 
515 		sc->sc_next = op->o_callback->sc_next;
516                 op->o_callback->sc_next = sc;
517 	}
518 
519 	return SLAP_CB_CONTINUE;
520 }
521 
522 static int
523 deref_db_init( BackendDB *be, ConfigReply *cr)
524 {
525 	if ( ov_count == 0 ) {
526 		int rc;
527 
528 		rc = register_supported_control2( LDAP_CONTROL_X_DEREF,
529 			SLAP_CTRL_SEARCH,
530 			NULL,
531 			deref_parseCtrl,
532 			1, /* replace */
533 			&deref_cid );
534 		if ( rc != LDAP_SUCCESS ) {
535 			Debug( LDAP_DEBUG_ANY,
536 				"deref_init: Failed to register control (%d)\n",
537 				rc, 0, 0 );
538 			return rc;
539 		}
540 	}
541 	ov_count++;
542 	return LDAP_SUCCESS;
543 }
544 
545 static int
546 deref_db_open( BackendDB *be, ConfigReply *cr)
547 {
548 	return overlay_register_control( be, LDAP_CONTROL_X_DEREF );
549 }
550 
551 #ifdef SLAP_CONFIG_DELETE
552 static int
553 deref_db_destroy( BackendDB *be, ConfigReply *cr)
554 {
555 	ov_count--;
556 	overlay_unregister_control( be, LDAP_CONTROL_X_DEREF );
557 	if ( ov_count == 0 ) {
558 		unregister_supported_control( LDAP_CONTROL_X_DEREF );
559 	}
560 	return 0;
561 }
562 #endif /* SLAP_CONFIG_DELETE */
563 
564 int
565 deref_initialize(void)
566 {
567 	deref.on_bi.bi_type = "deref";
568 	deref.on_bi.bi_db_init = deref_db_init;
569 	deref.on_bi.bi_db_open = deref_db_open;
570 #ifdef SLAP_CONFIG_DELETE
571 	deref.on_bi.bi_db_destroy = deref_db_destroy;
572 #endif /* SLAP_CONFIG_DELETE */
573 	deref.on_bi.bi_op_search = deref_op_search;
574 
575 	return overlay_register( &deref );
576 }
577 
578 #if SLAPD_OVER_DEREF == SLAPD_MOD_DYNAMIC
579 int
580 init_module( int argc, char *argv[] )
581 {
582 	return deref_initialize();
583 }
584 #endif /* SLAPD_OVER_DEREF == SLAPD_MOD_DYNAMIC */
585 
586 #endif /* SLAPD_OVER_DEREF */
587