From: Pierangelo Masarati Date: Wed, 22 Oct 2008 22:19:49 +0000 (+0000) Subject: add support for (experimental) dereference control (ITS#5768); need to re-run autocon... X-Git-Tag: ACLCHECK_0~1202 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=91e14ca63868293ad089e4cf77c8a7ad59236852;p=openldap add support for (experimental) dereference control (ITS#5768); need to re-run autoconf (and autoheader?) --- diff --git a/clients/tools/common.c b/clients/tools/common.c index 5222d6321d..4f38ede9fd 100644 --- a/clients/tools/common.c +++ b/clients/tools/common.c @@ -135,6 +135,9 @@ static int print_paged_results( LDAP *ld, LDAPControl *ctrl ); static int print_ppolicy( LDAP *ld, LDAPControl *ctrl ); #endif static int print_sss( LDAP *ld, LDAPControl *ctrl ); +#ifdef LDAP_CONTROL_X_DEREF +static int print_deref( LDAP *ld, LDAPControl *ctrl ); +#endif static struct tool_ctrls_t { const char *oid; @@ -148,6 +151,9 @@ static struct tool_ctrls_t { { LDAP_CONTROL_PASSWORDPOLICYRESPONSE, TOOL_ALL, print_ppolicy }, #endif { LDAP_CONTROL_SORTRESPONSE, TOOL_SEARCH, print_sss }, +#ifdef LDAP_CONTROL_X_DEREF + { LDAP_CONTROL_X_DEREF, TOOL_SEARCH, print_deref }, +#endif { NULL, 0, NULL } }; @@ -1890,6 +1896,76 @@ print_sss( LDAP *ld, LDAPControl *ctrl ) return rc; } +#ifdef LDAP_CONTROL_X_DEREF +static int +print_deref( LDAP *ld, LDAPControl *ctrl ) +{ + LDAPDerefRes *drhead = NULL, *dr; + int rc; + + rc = ldap_parse_derefresponse_control( ld, ctrl, &drhead ); + if ( rc != LDAP_SUCCESS ) { + return rc; + } + + for ( dr = drhead; dr != NULL; dr = dr->next ) { + LDAPDerefVal *dv; + ber_len_t len; + char *buf, *ptr; + + len = strlen( dr->derefAttr ) + STRLENOF(": "); + + for ( dv = dr->attrVals; dv != NULL; dv = dv->next ) { + if ( dv->vals != NULL ) { + int j; + ber_len_t tlen = strlen(dv->type); + + for ( j = 0; dv->vals[ j ].bv_val != NULL; j++ ) { + len += STRLENOF("<=>;") + tlen + dv->vals[ j ].bv_len; + } + } + } + len += dr->derefVal.bv_len; + buf = ldap_memalloc( len + 1 ); + if ( buf == NULL ) { + rc = LDAP_NO_MEMORY; + goto done; + } + + ptr = buf; + ptr = lutil_strcopy( ptr, dr->derefAttr ); + *ptr++ = ':'; + *ptr++ = ' '; + for ( dv = dr->attrVals; dv != NULL; dv = dv->next ) { + if ( dv->vals != NULL ) { + int j; + for ( j = 0; dv->vals[ j ].bv_val != NULL; j++ ) { + *ptr++ = '<'; + ptr = lutil_strcopy( ptr, dv->type ); + *ptr++ = '='; + ptr = lutil_strncopy( ptr, dv->vals[ j ].bv_val, dv->vals[ j ].bv_len ); + *ptr++ = '>'; + *ptr++ = ';'; + } + } + } + ptr = lutil_strncopy( ptr, dr->derefVal.bv_val, dr->derefVal.bv_len ); + *ptr++ = '\0'; + + tool_write_ldif( LDIF_PUT_COMMENT, NULL, buf, len ); + + ldap_memfree( buf ); + } + + rc = LDAP_SUCCESS; + +done:; + ldap_derefresponse_free( drhead ); + + return rc; +} +#endif + #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST static int print_ppolicy( LDAP *ld, LDAPControl *ctrl ) diff --git a/clients/tools/ldapsearch.c b/clients/tools/ldapsearch.c index 7e69fa5992..213f8c1f0f 100644 --- a/clients/tools/ldapsearch.c +++ b/clients/tools/ldapsearch.c @@ -223,6 +223,12 @@ static LDAPControl *c = NULL; static int nctrls = 0; static int save_nctrls = 0; +#ifdef LDAP_CONTROL_X_DEREF +static int derefcrit; +static LDAPDerefSpec *ds; +static struct berval derefval; +#endif + static int ctrl_add( void ) { @@ -491,6 +497,43 @@ handle_private_option( int i ) } if ( crit ) ldapsync *= -1; +#ifdef LDAP_CONTROL_X_DEREF + } else if ( strcasecmp( control, "deref" ) == 0 ) { + int ispecs; + char **specs; + + /* cvalue is something like + * + * derefAttr:attr[,attr[...]][;derefAttr:attr[,attr[...]]]" + */ + + specs = ldap_str2charray( cvalue, ";" ); + for ( ispecs = 0; specs[ ispecs ] != NULL; ispecs++ ) + /* count'em */ + + ds = ldap_memcalloc( ispecs + 1, sizeof( LDAPDerefSpec ) ); + if ( ds == NULL ) { + /* error */ + } + + for ( ispecs = 0; specs[ ispecs ] != NULL; ispecs++ ) { + char *ptr; + + ptr = strchr( specs[ ispecs ], ':' ); + if ( ptr == NULL ) { + /* error */ + } + + ds[ ispecs ].derefAttr = specs[ ispecs ]; + *ptr++ = '\0'; + ds[ ispecs ].attributes = ldap_str2charray( ptr, "," ); + } + + derefcrit = 1 + crit; + + ldap_memfree( specs ); +#endif /* LDAP_CONTROL_X_DEREF */ + } else if ( tool_is_oid( control ) ) { if ( ctrl_add() ) { exit( EXIT_FAILURE ); @@ -780,6 +823,9 @@ getNextPage: if ( nctrls > 0 #ifdef LDAP_CONTROL_DONTUSECOPY || dontUseCopy +#endif +#ifdef LDAP_CONTROL_X_DEREF + || derefcrit #endif || domainScope || pagedResults @@ -933,6 +979,32 @@ getNextPage: c[i].ldctl_iscritical = sss > 1; i++; } + +#ifdef LDAP_CONTROL_X_DEREF + if ( ds ) { + if ( derefval.bv_val == NULL ) { + int i; + if ( ldap_create_deref_control_value( ld, ds, &derefval ) != LDAP_SUCCESS ) { + return EXIT_FAILURE; + } + + for ( i = 0; ds[ i ].derefAttr != NULL; i++ ) { + ldap_memfree( ds[ i ].derefAttr ); + ldap_charray_free( ds[ i ].attributes ); + } + ldap_memfree( ds ); + } + + if ( ctrl_add() ) { + exit( EXIT_FAILURE ); + } + + c[ i ].ldctl_iscritical = derefcrit > 1; + c[ i ].ldctl_oid = LDAP_CONTROL_X_DEREF; + c[ i ].ldctl_value = derefval; + i++; + } +#endif /* LDAP_CONTROL_X_DEREF */ } tool_server_controls( ld, c, i ); @@ -1019,6 +1091,12 @@ getNextPage: printf(_("\n# with server side sorting %scontrol"), sss > 1 ? _("critical ") : "" ); } +#ifdef LDAP_CONTROL_X_DEREF + if ( sss ) { + printf(_("\n# with dereference %scontrol"), + sss > 1 ? _("critical ") : "" ); + } +#endif printf( _("\n#\n\n") ); diff --git a/configure.in b/configure.in index b4618fdcae..7e758534fc 100644 --- a/configure.in +++ b/configure.in @@ -338,6 +338,7 @@ Overlays="accesslog \ collect \ constraint \ dds \ + deref \ dyngroup \ dynlist \ memberof \ @@ -367,6 +368,8 @@ OL_ARG_ENABLE(constraint,[ --enable-constraint Attribute Constraint overlay no, [no yes mod], ol_enable_overlays) OL_ARG_ENABLE(dds,[ --enable-dds Dynamic Directory Services overlay], no, [no yes mod], ol_enable_overlays) +OL_ARG_ENABLE(deref,[ --enable-deref Dereference overlay], + no, [no yes mod], ol_enable_overlays) OL_ARG_ENABLE(dyngroup,[ --enable-dyngroup Dynamic Group overlay], no, [no yes mod], ol_enable_overlays) OL_ARG_ENABLE(dynlist,[ --enable-dynlist Dynamic List overlay], @@ -2846,6 +2849,18 @@ if test "$ol_enable_dds" != no ; then AC_DEFINE_UNQUOTED(SLAPD_OVER_DDS,$MFLAG,[define for Dynamic Directory Services overlay]) fi +if test "$ol_enable_deref" != no ; then + BUILD_DDS=$ol_enable_deref + if test "$ol_enable_deref" = mod ; then + MFLAG=SLAPD_MOD_DYNAMIC + SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS deref.la" + else + MFLAG=SLAPD_MOD_STATIC + SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS deref.o" + fi + AC_DEFINE_UNQUOTED(SLAPD_OVER_DEREF,$MFLAG,[define for Dynamic Directory Services overlay]) +fi + if test "$ol_enable_dyngroup" != no ; then BUILD_DYNGROUP=$ol_enable_dyngroup if test "$ol_enable_dyngroup" = mod ; then diff --git a/include/ldap.h b/include/ldap.h index b86071aadb..99c2474723 100644 --- a/include/ldap.h +++ b/include/ldap.h @@ -321,6 +321,8 @@ typedef struct ldapcontrol { LDAP_CONTROL_X_SESSION_TRACKING ".2" #define LDAP_CONTROL_X_SESSION_TRACKING_USERNAME \ LDAP_CONTROL_X_SESSION_TRACKING ".3" +/* Dereference Control (work in progress) */ +#define LDAP_CONTROL_X_DEREF "1.3.6.1.4.1.4203.666.5.16" #endif /* LDAP_DEVEL */ /* various expired works */ @@ -2400,5 +2402,56 @@ ldap_create_assertion_control LDAP_P(( int iscritical, LDAPControl **ctrlp )); +/* + * in deref.c + */ + +typedef struct LDAPDerefSpec { + char *derefAttr; + char **attributes; +} LDAPDerefSpec; + +typedef struct LDAPDerefVal { + char *type; + BerVarray vals; + struct LDAPDerefVal *next; +} LDAPDerefVal; + +typedef struct LDAPDerefRes { + char *derefAttr; + struct berval derefVal; + LDAPDerefVal *attrVals; + struct LDAPDerefRes *next; +} LDAPDerefRes; + +LDAP_F( int ) +ldap_create_deref_control_value LDAP_P(( + LDAP *ld, + LDAPDerefSpec *ds, + struct berval *value )); + +LDAP_F( int ) +ldap_create_deref_control LDAP_P(( + LDAP *ld, + LDAPDerefSpec *ds, + int iscritical, + LDAPControl **ctrlp )); + +LDAP_F( void ) +ldap_derefresponse_free LDAP_P(( + LDAPDerefRes *dr )); + +LDAP_F( int ) +ldap_parse_derefresponse_control LDAP_P(( + LDAP *ld, + LDAPControl *ctrl, + LDAPDerefRes **drp )); + +LDAP_F( int ) +ldap_parse_deref_control LDAP_P(( + LDAP *ld, + LDAPControl **ctrls, + LDAPDerefRes **drp )); + LDAP_END_DECL #endif /* _LDAP_H */ diff --git a/include/portable.hin b/include/portable.hin index eaaaa3aeb5..e5ff22e8ff 100644 --- a/include/portable.hin +++ b/include/portable.hin @@ -978,6 +978,9 @@ /* define for Dynamic Directory Services overlay */ #undef SLAPD_OVER_DDS +/* define for Dynamic Directory Services overlay */ +#undef SLAPD_OVER_DEREF + /* define for Dynamic Group overlay */ #undef SLAPD_OVER_DYNGROUP diff --git a/libraries/libldap/Makefile.in b/libraries/libldap/Makefile.in index c62e56ffc0..ed8c2b17e6 100644 --- a/libraries/libldap/Makefile.in +++ b/libraries/libldap/Makefile.in @@ -27,7 +27,7 @@ SRCS = bind.c open.c result.c error.c compare.c search.c \ init.c options.c print.c string.c util-int.c schema.c \ charray.c tls.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \ turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \ - assertion.c + assertion.c deref.c OBJS = bind.lo open.lo result.lo error.lo compare.lo search.lo \ controls.lo messages.lo references.lo extended.lo cyrus.lo \ @@ -39,7 +39,7 @@ OBJS = bind.lo open.lo result.lo error.lo compare.lo search.lo \ init.lo options.lo print.lo string.lo util-int.lo schema.lo \ charray.lo tls.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \ turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \ - assertion.lo + assertion.lo deref.lo LDAP_INCDIR= ../../include LDAP_LIBDIR= ../../libraries diff --git a/libraries/libldap/deref.c b/libraries/libldap/deref.c new file mode 100644 index 0000000000..d5a52ae43b --- /dev/null +++ b/libraries/libldap/deref.c @@ -0,0 +1,279 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Pierangelo Masarati + * for inclusion in OpenLDAP Software. + */ + +#include "portable.h" + +#include +#include +#include +#include + +#include "ldap-int.h" + +int +ldap_create_deref_control_value( + LDAP *ld, + LDAPDerefSpec *ds, + struct berval *value ) +{ + BerElement *ber = NULL; + ber_tag_t tag; + int i; + + if ( ld == NULL || value == NULL || ds == NULL ) + { + if ( ld ) + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + assert( LDAP_VALID( ld ) ); + + value->bv_val = NULL; + value->bv_len = 0; + ld->ld_errno = LDAP_SUCCESS; + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{" /*}*/ ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + for ( i = 0; ds[i].derefAttr != NULL; i++ ) { + int j; + + tag = ber_printf( ber, "{s{" /*}}*/ , ds[i].derefAttr ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + for ( j = 0; ds[i].attributes[j] != NULL; j++ ) { + tag = ber_printf( ber, "s", ds[i].attributes[ j ] ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + } + + tag = ber_printf( ber, /*{{*/ "}N}" ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + } + + tag = ber_printf( ber, /*{*/ "}" ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + +int +ldap_create_deref_control( + LDAP *ld, + LDAPDerefSpec *ds, + int iscritical, + LDAPControl **ctrlp ) +{ + struct berval value; + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_deref_control_value( ld, ds, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_PAGEDRESULTS, + iscritical, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + +void +ldap_derefresponse_free( LDAPDerefRes *dr ) +{ + for ( ; dr; ) { + LDAPDerefRes *drnext = dr->next; + LDAPDerefVal *dv; + + LDAP_FREE( dr->derefAttr ); + LDAP_FREE( dr->derefVal.bv_val ); + + for ( dv = dr->attrVals; dv; ) { + LDAPDerefVal *dvnext = dv->next; + LDAP_FREE( dv->type ); + ber_bvarray_free( dv->vals ); + LDAP_FREE( dv ); + dv = dvnext; + } + + LDAP_FREE( dr ); + + dr = drnext; + } +} + +int +ldap_parse_derefresponse_control( + LDAP *ld, + LDAPControl *ctrl, + LDAPDerefRes **drp2 ) +{ + BerElement *ber; + ber_tag_t tag; + ber_len_t len; + char *last; + LDAPDerefRes *drhead = NULL, **drp; + + if ( ld == NULL || ctrl == NULL || drp2 == NULL ) { + if ( ld ) + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init( &ctrl->ldctl_value ); + + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + /* Extract the count and cookie from the control. */ + drp = &drhead; + for ( tag = ber_first_element( ber, &len, &last ); + tag != LBER_DEFAULT; + tag = ber_next_element( ber, &len, last ) ) + { + LDAPDerefRes *dr; + LDAPDerefVal **dvp; + char *last2; + + dr = LDAP_CALLOC( 1, sizeof(LDAPDerefRes) ); + dvp = &dr->attrVals; + + tag = ber_scanf( ber, "{ao", &dr->derefAttr, &dr->derefVal ); + if ( tag == LBER_ERROR ) { + goto done; + } + + for ( tag = ber_first_element( ber, &len, &last2 ); + tag != LBER_DEFAULT; + tag = ber_next_element( ber, &len, last2 ) ) + { + LDAPDerefVal *dv; + + dv = LDAP_CALLOC( 1, sizeof(LDAPDerefVal) ); + + tag = ber_scanf( ber, "{a[W]}", &dv->type, &dv->vals ); + if ( tag == LBER_ERROR ) { + goto done; + } + + *dvp = dv; + dvp = &dv->next; + } + + tag = ber_scanf( ber, "}" ); + if ( tag == LBER_ERROR ) { + goto done; + } + + *drp = dr; + drp = &dr->next; + } + + tag = 0; + +done:; + ber_free( ber, 1 ); + + if ( tag == LBER_ERROR ) { + if ( drhead != NULL ) { + ldap_derefresponse_free( drhead ); + } + + *drp2 = NULL; + ld->ld_errno = LDAP_DECODING_ERROR; + + } else { + *drp2 = drhead; + ld->ld_errno = LDAP_SUCCESS; + } + + return ld->ld_errno; +} + +int +ldap_parse_deref_control( + LDAP *ld, + LDAPControl **ctrls, + LDAPDerefRes **drp ) +{ + LDAPControl *c; + + if ( drp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + *drp = NULL; + + if ( ctrls == NULL ) { + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return ld->ld_errno; + } + + c = ldap_control_find( LDAP_CONTROL_X_DEREF, ctrls, NULL ); + if ( c == NULL ) { + /* No deref control was found. */ + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return ld->ld_errno; + } + + ld->ld_errno = ldap_parse_derefresponse_control( ld, c, drp ); + + return ld->ld_errno; +} + diff --git a/servers/slapd/overlays/Makefile.in b/servers/slapd/overlays/Makefile.in index 07b4b1ebb4..ce60327a4e 100644 --- a/servers/slapd/overlays/Makefile.in +++ b/servers/slapd/overlays/Makefile.in @@ -18,6 +18,7 @@ SRCS = overlays.c \ auditlog.c \ constraint.c \ dds.c \ + deref.c \ dyngroup.c \ dynlist.c \ memberof.c \ @@ -72,6 +73,9 @@ constraint.la : constraint.lo dds.la : dds.lo $(LTLINK_MOD) -module -o $@ dds.lo version.lo $(LINK_LIBS) +deref.la : deref.lo + $(LTLINK_MOD) -module -o $@ deref.lo version.lo $(LINK_LIBS) + dyngroup.la : dyngroup.lo $(LTLINK_MOD) -module -o $@ dyngroup.lo version.lo $(LINK_LIBS) diff --git a/servers/slapd/overlays/deref.c b/servers/slapd/overlays/deref.c new file mode 100644 index 0000000000..96b210aa4f --- /dev/null +++ b/servers/slapd/overlays/deref.c @@ -0,0 +1,517 @@ +/* deref.c - dereference overlay */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Pierangelo Masarati + * for inclusion in OpenLDAP Software. + */ + +#include "portable.h" + +#ifdef SLAPD_OVER_DEREF + +#include + +#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 ); + +#if 0 + rc = ber_printf( ber, "{" /*}*/ ); + for ( dr = drhead; dr != NULL; dr = dr->dr_next ) { + rc = ber_printf( ber, "{{O{" /*}}}*/, + &dr->dr_spec.ds_derefAttr->ad_cname ); + for ( i = 0; i < dr->dr_spec.ds_nattrs; i++ ) { + rc = ber_printf( ber, "O", + &dr->dr_spec.ds_attributes[ i ]->ad_cname ); + } + rc = ber_printf( ber, /*{{*/ "}}{" /*}*/ ); + for ( i = 0; !BER_BVISNULL( &dr->dr_vals[ i ].dv_derefSpecVal ); i++ ) { + int j; + + rc = ber_printf( ber, "{O{" /*}}*/ , + &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, "[W]", dr->dr_vals[ i ].dv_attrVals[ j ] ); + } else { + rc = ber_printf( ber, "b", 0 ); + } + } + rc = ber_printf( ber, /*{{*/ "}}" ); + } + rc = ber_printf( ber, /*{{*/ "}}" ); + } + rc = ber_printf( ber, /*{*/ "}" ); +#endif + + 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() +{ + 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 rc; + } + + 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 */