openldap/servers/slapd/overlays/deref.c

489 lines
12 KiB
C
Raw Normal View History

/* deref.c - dereference overlay */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2008 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 "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 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" ] } } }
*/
#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
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 )
{
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 != slap_schema.si_syn_distinguishedName ) {
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_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;
BackendInfo *bi = op->o_bd->bd_info;
struct berval bv = BER_BVNULL;
int nDerefRes = 0, nDerefVals = 0, nAttrs = 0, nVals = 0;
struct berval ctrlval;
LDAPControl *ctrl, **ctrlsp;
int i;
op->o_bd->bd_info = (BackendInfo *)dc->dc_on->on_info;
for ( ds = dc->dc_ds; ds; ds = ds->ds_next ) {
Attribute *a = attr_find( rs->sr_entry->e_attrs, ds->ds_derefAttr );
if ( a != NULL ) {
DerefVal *dv;
BerVarray *bva;
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;
dv[i].dv_derefSpecVal = a->a_vals[ i ];
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;
for ( j = 0; j < ds->ds_nattrs; j++ ) {
Attribute *aa = attr_find( e->e_attrs, ds->ds_attributes[ j ] );
if ( aa != NULL ) {
int k;
dv[i].dv_attrVals[ j ] = aa->a_vals;
bv.bv_len += ds->ds_attributes[ j ]->ad_cname.bv_len;
for ( k = 0; !BER_BVISNULL( &aa->a_vals[ k ] ); k++ ) {
bv.bv_len += aa->a_vals[ k ].bv_len;
nVals++;
}
nAttrs++;
}
}
overlay_entry_release_ov( op, e, 0, dc->dc_on );
}
}
*drp = dr;
drp = &dr->dr_next;
}
}
op->o_bd->bd_info = bi;
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;
rc = ber_printf( ber, "{OO{" /*}*/,
&dr->dr_spec.ds_derefAttr->ad_cname,
&dr->dr_vals[ i ].dv_derefSpecVal );
for ( j = 0; j < dr->dr_spec.ds_nattrs; j++ ) {
if ( dr->dr_vals[ i ].dv_attrVals[ j ] != NULL ) {
rc = ber_printf( ber, "{O[W]}",
&dr->dr_spec.ds_attributes[ j ]->ad_cname,
dr->dr_vals[ i ].dv_attrVals[ j ] );
}
}
rc = ber_printf( ber, /*{*/ "}N}" );
}
}
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;
lutil_strncopy( 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 );
i = 0;
if ( rs->sr_ctrls ) {
for ( ; rs->sr_ctrls[ i ] != NULL; i++ )
/* count'em */ ;
}
i += 2;
ctrlsp = op->o_tmpcalloc( i, sizeof(LDAPControl *), op->o_tmpmemctx );
i = 0;
if ( rs->sr_ctrls != NULL ) {
for ( ; rs->sr_ctrls[ i ] != NULL; i++ ) {
ctrlsp[ i ] = rs->sr_ctrls[ i ];
}
}
ctrlsp[ i++ ] = ctrl;
ctrlsp[ i++ ] = NULL;
if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED ) {
op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
}
rs->sr_ctrls = ctrlsp;
rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
rc = SLAP_CB_CONTINUE;
cleanup:;
/* release all */
for ( ; drhead != NULL; ) {
DerefRes *drnext = drhead->dr_next;
op->o_tmpfree( drhead, op->o_tmpmemctx );
drhead = drnext;
}
}
return rc;
}
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_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;
}
int
deref_initialize(void)
{
int rc;
rc = register_supported_control( LDAP_CONTROL_X_DEREF,
SLAP_CTRL_SEARCH, NULL,
deref_parseCtrl, &deref_cid );
if ( rc != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_ANY,
"deref_init: Failed to register control (%d)\n",
rc, 0, 0 );
return -1;
}
deref.on_bi.bi_type = "deref";
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 */