diff options
Diffstat (limited to '')
-rw-r--r-- | servers/slapd/overlays/deref.c | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/servers/slapd/overlays/deref.c b/servers/slapd/overlays/deref.c new file mode 100644 index 0000000..89dc227 --- /dev/null +++ b/servers/slapd/overlays/deref.c @@ -0,0 +1,586 @@ +/* deref.c - dereference overlay */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * Portions Copyright 2008 Pierangelo Masarati. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Pierangelo Masarati + * for inclusion in OpenLDAP Software. + */ + +#include "portable.h" + +#ifdef SLAPD_OVER_DEREF + +#include <stdio.h> + +#include "ac/string.h" +#include "ac/socket.h" + +#include "slap.h" +#include "slap-config.h" + +#include "lutil.h" + +/* + * 1. Specification + * + * 1.1. Request + * + * controlValue ::= SEQUENCE OF derefSpec DerefSpec + * + * DerefSpec ::= SEQUENCE { + * derefAttr attributeDescription, ; DN-valued + * attributes AttributeList } + * + * AttributeList ::= SEQUENCE OF attr AttributeDescription + * + * derefAttr MUST be unique within controlValue + * + * + * 1.2. Response + * + * controlValue ::= SEQUENCE OF DerefRes + * + * From RFC 4511: + * PartialAttribute ::= SEQUENCE { + * type AttributeDescription, + * vals SET OF value AttributeValue } + * + * PartialAttributeList ::= SEQUENCE OF + * partialAttribute PartialAttribute + * + * DerefRes ::= SEQUENCE { + * derefAttr AttributeDescription, + * derefVal LDAPDN, + * attrVals [0] PartialAttributeList OPTIONAL } + * + * If vals is empty, partialAttribute is omitted. + * If all vals in attrVals are empty, attrVals is omitted. + * + * 2. Examples + * + * 2.1. Example + * + * 2.1.1. Request + * + * { { member, { GUID, SID } }, { memberOf, { GUID, SID } } } + * + * 2.1.2. Response + * + * { { memberOf, "cn=abartlet,cn=users,dc=abartlet,dc=net", + * { { GUID, [ "0bc11d00-e431-40a0-8767-344a320142fa" ] }, + * { SID, [ "S-1-2-3-2345" ] } } }, + * { memberOf, "cn=ando,cn=users,dc=sys-net,dc=it", + * { { GUID, [ "0bc11d00-e431-40a0-8767-344a320142fb" ] }, + * { SID, [ "S-1-2-3-2346" ] } } } } + * + * 2.2. Example + * + * 2.2.1. Request + * + * { { member, { cn, uid, drink } } } + * + * 2.2.2. Response + * + * { { member, "cn=ando,cn=users,dc=sys-net,dc=it", + * { { cn, [ "ando", "Pierangelo Masarati" ] }, + * { uid, [ "ando" ] } } }, + * { member, "dc=sys-net,dc=it" } } + * + * + * 3. Security considerations + * + * The control result must not disclose information the client's + * identity could not have accessed directly by performing the related + * search operations. The presence of a derefVal in the control + * response does not imply neither the existence of nor any access + * privilege to the corresponding entry. It is merely a consequence + * of the read access the client's identity has on the corresponding + * attribute's value. + */ + +#define o_deref o_ctrlflag[deref_cid] +#define o_ctrlderef o_controls[deref_cid] + +typedef struct DerefSpec { + AttributeDescription *ds_derefAttr; + AttributeDescription **ds_attributes; + int ds_nattrs; + struct DerefSpec *ds_next; +} DerefSpec; + +typedef struct DerefVal { + struct berval dv_derefSpecVal; + BerVarray *dv_attrVals; +} DerefVal; + +typedef struct DerefRes { + DerefSpec dr_spec; + DerefVal *dr_vals; + struct DerefRes *dr_next; +} DerefRes; + +typedef struct deref_cb_t { + slap_overinst *dc_on; + DerefSpec *dc_ds; +} deref_cb_t; + +static int deref_cid; +static slap_overinst deref; +static int ov_count; + +static int +deref_parseCtrl ( + Operation *op, + SlapReply *rs, + LDAPControl *ctrl ) +{ + ber_tag_t tag; + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_len_t len; + char *last; + DerefSpec *dshead = NULL, **dsp = &dshead; + BerVarray attributes = NULL; + + if ( op->o_deref != SLAP_CONTROL_NONE ) { + rs->sr_text = "Dereference control specified multiple times"; + return LDAP_PROTOCOL_ERROR; + } + + if ( BER_BVISNULL( &ctrl->ldctl_value ) ) { + rs->sr_text = "Dereference control value is absent"; + return LDAP_PROTOCOL_ERROR; + } + + if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) { + rs->sr_text = "Dereference control value is empty"; + return LDAP_PROTOCOL_ERROR; + } + + ber_init2( ber, &ctrl->ldctl_value, 0 ); + + for ( tag = ber_first_element( ber, &len, &last ); + tag != LBER_DEFAULT; + tag = ber_next_element( ber, &len, last ) ) + { + struct berval derefAttr; + DerefSpec *ds, *dstmp; + const char *text; + int rc; + ber_len_t cnt = sizeof(struct berval); + ber_len_t off = 0; + + if ( ber_scanf( ber, "{m{M}}", &derefAttr, &attributes, &cnt, off ) == LBER_ERROR + || !cnt ) + { + rs->sr_text = "Dereference control: derefSpec decoding error"; + rs->sr_err = LDAP_PROTOCOL_ERROR; + goto done; + } + + ds = (DerefSpec *)op->o_tmpcalloc( 1, + sizeof(DerefSpec) + sizeof(AttributeDescription *)*(cnt + 1), + op->o_tmpmemctx ); + ds->ds_attributes = (AttributeDescription **)&ds[ 1 ]; + ds->ds_nattrs = cnt; + + rc = slap_bv2ad( &derefAttr, &ds->ds_derefAttr, &text ); + if ( rc != LDAP_SUCCESS ) { + rs->sr_text = "Dereference control: derefAttr decoding error"; + rs->sr_err = LDAP_PROTOCOL_ERROR; + goto done; + } + + for ( dstmp = dshead; dstmp && dstmp != ds; dstmp = dstmp->ds_next ) { + if ( dstmp->ds_derefAttr == ds->ds_derefAttr ) { + rs->sr_text = "Dereference control: derefAttr must be unique within control"; + rs->sr_err = LDAP_PROTOCOL_ERROR; + goto done; + } + } + + if ( !( ds->ds_derefAttr->ad_type->sat_syntax->ssyn_flags & SLAP_SYNTAX_DN )) { + if ( ctrl->ldctl_iscritical ) { + rs->sr_text = "Dereference control: derefAttr syntax not distinguishedName"; + rs->sr_err = LDAP_PROTOCOL_ERROR; + goto done; + } + + rs->sr_err = LDAP_SUCCESS; + goto justcleanup; + } + + for ( cnt = 0; !BER_BVISNULL( &attributes[ cnt ] ); cnt++ ) { + rc = slap_bv2ad( &attributes[ cnt ], &ds->ds_attributes[ cnt ], &text ); + if ( rc != LDAP_SUCCESS ) { + rs->sr_text = "Dereference control: attribute decoding error"; + rs->sr_err = LDAP_PROTOCOL_ERROR; + goto done; + } + } + + ber_memfree_x( attributes, op->o_tmpmemctx ); + attributes = NULL; + + *dsp = ds; + dsp = &ds->ds_next; + } + + op->o_ctrlderef = (void *)dshead; + + op->o_deref = ctrl->ldctl_iscritical + ? SLAP_CONTROL_CRITICAL + : SLAP_CONTROL_NONCRITICAL; + + rs->sr_err = LDAP_SUCCESS; + +done:; + if ( rs->sr_err != LDAP_SUCCESS ) { +justcleanup:; + for ( ; dshead; ) { + DerefSpec *dsnext = dshead->ds_next; + op->o_tmpfree( dshead, op->o_tmpmemctx ); + dshead = dsnext; + } + } + + if ( attributes != NULL ) { + ber_memfree_x( attributes, op->o_tmpmemctx ); + } + + return rs->sr_err; +} + +static int +deref_cleanup( Operation *op, SlapReply *rs ) +{ + if ( rs->sr_type == REP_RESULT || rs->sr_err == SLAPD_ABANDON ) { + op->o_tmpfree( op->o_callback, op->o_tmpmemctx ); + op->o_callback = NULL; + + op->o_tmpfree( op->o_ctrlderef, op->o_tmpmemctx ); + op->o_ctrlderef = NULL; + } + + return SLAP_CB_CONTINUE; +} + +static int +deref_response( Operation *op, SlapReply *rs ) +{ + int rc = SLAP_CB_CONTINUE; + + if ( rs->sr_type == REP_SEARCH ) { + BerElementBuffer berbuf; + BerElement *ber = (BerElement *) &berbuf; + deref_cb_t *dc = (deref_cb_t *)op->o_callback->sc_private; + DerefSpec *ds; + DerefRes *dr, *drhead = NULL, **drp = &drhead; + struct berval bv = BER_BVNULL; + int nDerefRes = 0, nDerefVals = 0, nAttrs = 0, nVals = 0; + struct berval ctrlval; + LDAPControl *ctrl, *ctrlsp[2]; + AccessControlState acl_state = ACL_STATE_INIT; + static char dummy = '\0'; + Entry *ebase; + int i; + + rc = overlay_entry_get_ov( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &ebase, dc->dc_on ); + if ( rc != LDAP_SUCCESS || ebase == NULL ) { + return SLAP_CB_CONTINUE; + } + + for ( ds = dc->dc_ds; ds; ds = ds->ds_next ) { + Attribute *a = attr_find( ebase->e_attrs, ds->ds_derefAttr ); + + if ( a != NULL ) { + DerefVal *dv; + BerVarray *bva; + + if ( !access_allowed( op, rs->sr_entry, a->a_desc, + NULL, ACL_READ, &acl_state ) ) + { + continue; + } + + dr = op->o_tmpcalloc( 1, + sizeof( DerefRes ) + ( sizeof( DerefVal ) + sizeof( BerVarray * ) * ds->ds_nattrs ) * ( a->a_numvals + 1 ), + op->o_tmpmemctx ); + dr->dr_spec = *ds; + dv = dr->dr_vals = (DerefVal *)&dr[ 1 ]; + bva = (BerVarray *)&dv[ a->a_numvals + 1 ]; + + bv.bv_len += ds->ds_derefAttr->ad_cname.bv_len; + nAttrs++; + nDerefRes++; + + for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) { + Entry *e = NULL; + + dv[ i ].dv_attrVals = bva; + bva += ds->ds_nattrs; + + + if ( !access_allowed( op, rs->sr_entry, a->a_desc, + &a->a_nvals[ i ], ACL_READ, &acl_state ) ) + { + dv[ i ].dv_derefSpecVal.bv_val = &dummy; + continue; + } + + ber_dupbv_x( &dv[ i ].dv_derefSpecVal, &a->a_vals[ i ], op->o_tmpmemctx ); + bv.bv_len += dv[ i ].dv_derefSpecVal.bv_len; + nVals++; + nDerefVals++; + + rc = overlay_entry_get_ov( op, &a->a_nvals[ i ], NULL, NULL, 0, &e, dc->dc_on ); + if ( rc == LDAP_SUCCESS && e != NULL ) { + int j; + + if ( access_allowed( op, e, slap_schema.si_ad_entry, + NULL, ACL_READ, NULL ) ) + { + for ( j = 0; j < ds->ds_nattrs; j++ ) { + Attribute *aa; + + if ( !access_allowed( op, e, ds->ds_attributes[ j ], NULL, + ACL_READ, &acl_state ) ) + { + continue; + } + + aa = attr_find( e->e_attrs, ds->ds_attributes[ j ] ); + if ( aa != NULL ) { + unsigned k, h, last = aa->a_numvals; + + ber_bvarray_dup_x( &dv[ i ].dv_attrVals[ j ], + aa->a_vals, op->o_tmpmemctx ); + + bv.bv_len += ds->ds_attributes[ j ]->ad_cname.bv_len; + + for ( k = 0, h = 0; k < aa->a_numvals; k++ ) { + if ( !access_allowed( op, e, + aa->a_desc, + &aa->a_nvals[ k ], + ACL_READ, &acl_state ) ) + { + op->o_tmpfree( dv[ i ].dv_attrVals[ j ][ h ].bv_val, + op->o_tmpmemctx ); + dv[ i ].dv_attrVals[ j ][ h ] = dv[ i ].dv_attrVals[ j ][ --last ]; + BER_BVZERO( &dv[ i ].dv_attrVals[ j ][ last ] ); + continue; + } + bv.bv_len += dv[ i ].dv_attrVals[ j ][ h ].bv_len; + nVals++; + h++; + } + nAttrs++; + } + } + } + + overlay_entry_release_ov( op, e, 0, dc->dc_on ); + } + } + + *drp = dr; + drp = &dr->dr_next; + } + } + overlay_entry_release_ov( op, ebase, 0, dc->dc_on ); + + if ( drhead == NULL ) { + return SLAP_CB_CONTINUE; + } + + /* cook the control value */ + bv.bv_len += nVals * sizeof(struct berval) + + nAttrs * sizeof(struct berval) + + nDerefVals * sizeof(DerefVal) + + nDerefRes * sizeof(DerefRes); + bv.bv_val = op->o_tmpalloc( bv.bv_len, op->o_tmpmemctx ); + + ber_init2( ber, &bv, LBER_USE_DER ); + ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx ); + + rc = ber_printf( ber, "{" /*}*/ ); + for ( dr = drhead; dr != NULL; dr = dr->dr_next ) { + for ( i = 0; !BER_BVISNULL( &dr->dr_vals[ i ].dv_derefSpecVal ); i++ ) { + int j, first = 1; + + if ( dr->dr_vals[ i ].dv_derefSpecVal.bv_val == &dummy ) { + continue; + } + + rc = ber_printf( ber, "{OO" /*}*/, + &dr->dr_spec.ds_derefAttr->ad_cname, + &dr->dr_vals[ i ].dv_derefSpecVal ); + op->o_tmpfree( dr->dr_vals[ i ].dv_derefSpecVal.bv_val, op->o_tmpmemctx ); + for ( j = 0; j < dr->dr_spec.ds_nattrs; j++ ) { + if ( dr->dr_vals[ i ].dv_attrVals[ j ] != NULL ) { + if ( first ) { + rc = ber_printf( ber, "t{" /*}*/, + (LBER_CONSTRUCTED|LBER_CLASS_CONTEXT) ); + first = 0; + } + rc = ber_printf( ber, "{O[W]}", + &dr->dr_spec.ds_attributes[ j ]->ad_cname, + dr->dr_vals[ i ].dv_attrVals[ j ] ); + op->o_tmpfree( dr->dr_vals[ i ].dv_attrVals[ j ], + op->o_tmpmemctx ); + } + } + if ( !first ) { + rc = ber_printf( ber, /*{{*/ "}N}" ); + } else { + rc = ber_printf( ber, /*{*/ "}" ); + } + } + } + rc = ber_printf( ber, /*{*/ "}" ); + if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) { + if ( op->o_deref == SLAP_CONTROL_CRITICAL ) { + rc = LDAP_CONSTRAINT_VIOLATION; + + } else { + rc = SLAP_CB_CONTINUE; + } + goto cleanup; + } + + ctrl = op->o_tmpcalloc( 1, + sizeof( LDAPControl ) + ctrlval.bv_len + 1, + op->o_tmpmemctx ); + ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ]; + ctrl->ldctl_oid = LDAP_CONTROL_X_DEREF; + ctrl->ldctl_iscritical = 0; + ctrl->ldctl_value.bv_len = ctrlval.bv_len; + AC_MEMCPY( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len ); + ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0'; + + ber_free_buf( ber ); + + ctrlsp[0] = ctrl; + ctrlsp[1] = NULL; + slap_add_ctrls( op, rs, ctrlsp ); + + rc = SLAP_CB_CONTINUE; + +cleanup:; + /* release all */ + for ( ; drhead != NULL; ) { + DerefRes *drnext = drhead->dr_next; + op->o_tmpfree( drhead, op->o_tmpmemctx ); + drhead = drnext; + } + + } else if ( rs->sr_type == REP_RESULT ) { + rc = deref_cleanup( op, rs ); + } + + return rc; +} + +static int +deref_op_search( Operation *op, SlapReply *rs ) +{ + if ( op->o_deref ) { + slap_callback *sc; + deref_cb_t *dc; + + sc = op->o_tmpcalloc( 1, sizeof( slap_callback ) + sizeof( deref_cb_t ), op->o_tmpmemctx ); + + dc = (deref_cb_t *)&sc[ 1 ]; + dc->dc_on = (slap_overinst *)op->o_bd->bd_info; + dc->dc_ds = (DerefSpec *)op->o_ctrlderef; + + sc->sc_response = deref_response; + sc->sc_cleanup = deref_cleanup; + sc->sc_private = (void *)dc; + + sc->sc_next = op->o_callback->sc_next; + op->o_callback->sc_next = sc; + } + + return SLAP_CB_CONTINUE; +} + +static int +deref_db_init( BackendDB *be, ConfigReply *cr) +{ + if ( ov_count == 0 ) { + int rc; + + rc = register_supported_control2( LDAP_CONTROL_X_DEREF, + SLAP_CTRL_SEARCH, + NULL, + deref_parseCtrl, + 1, /* replace */ + &deref_cid ); + if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ANY, + "deref_init: Failed to register control (%d)\n", + rc ); + return rc; + } + } + ov_count++; + return LDAP_SUCCESS; +} + +static int +deref_db_open( BackendDB *be, ConfigReply *cr) +{ + return overlay_register_control( be, LDAP_CONTROL_X_DEREF ); +} + +#ifdef SLAP_CONFIG_DELETE +static int +deref_db_destroy( BackendDB *be, ConfigReply *cr) +{ + ov_count--; + overlay_unregister_control( be, LDAP_CONTROL_X_DEREF ); + if ( ov_count == 0 ) { + unregister_supported_control( LDAP_CONTROL_X_DEREF ); + } + return 0; +} +#endif /* SLAP_CONFIG_DELETE */ + +int +deref_initialize(void) +{ + deref.on_bi.bi_type = "deref"; + deref.on_bi.bi_flags = SLAPO_BFLAG_SINGLE; + deref.on_bi.bi_db_init = deref_db_init; + deref.on_bi.bi_db_open = deref_db_open; +#ifdef SLAP_CONFIG_DELETE + deref.on_bi.bi_db_destroy = deref_db_destroy; +#endif /* SLAP_CONFIG_DELETE */ + deref.on_bi.bi_op_search = deref_op_search; + + return overlay_register( &deref ); +} + +#if SLAPD_OVER_DEREF == SLAPD_MOD_DYNAMIC +int +init_module( int argc, char *argv[] ) +{ + return deref_initialize(); +} +#endif /* SLAPD_OVER_DEREF == SLAPD_MOD_DYNAMIC */ + +#endif /* SLAPD_OVER_DEREF */ |