X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Fcontrols.c;h=8a7519d95a77bc9e2695286d425e5b246d3307b8;hb=6549cfc7d09511de67e113d602c4071599127e2c;hp=042fccca8f080fabb569356d14c6a37ff3e59bc8;hpb=1714d00a07b5d5388d9ecdeb8cec81c0f94fb30f;p=openldap diff --git a/servers/slapd/controls.c b/servers/slapd/controls.c index 042fccca8f..8a7519d95a 100644 --- a/servers/slapd/controls.c +++ b/servers/slapd/controls.c @@ -1,7 +1,7 @@ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * Copyright 1998-2008 The OpenLDAP Foundation. + * Copyright 1998-2013 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -48,6 +48,9 @@ static SLAP_CTRL_PARSE_FN parseValuesReturnFilter; #ifdef SLAP_CONTROL_X_SESSION_TRACKING static SLAP_CTRL_PARSE_FN parseSessionTracking; #endif +#ifdef SLAP_CONTROL_X_WHATFAILED +static SLAP_CTRL_PARSE_FN parseWhatFailed; +#endif #undef sc_mask /* avoid conflict with Irix 6.5 */ @@ -120,8 +123,7 @@ static char *session_tracking_extops[] = { static struct slap_control control_defs[] = { { LDAP_CONTROL_ASSERT, (int)offsetof(struct slap_control_ids, sc_assert), - SLAP_CTRL_DELETE|SLAP_CTRL_MODIFY|SLAP_CTRL_RENAME| - SLAP_CTRL_COMPARE|SLAP_CTRL_SEARCH, + SLAP_CTRL_UPDATE|SLAP_CTRL_COMPARE|SLAP_CTRL_SEARCH, NULL, NULL, parseAssert, LDAP_SLIST_ENTRY_INITIALIZER(next) }, { LDAP_CONTROL_PRE_READ, @@ -217,6 +219,14 @@ static struct slap_control control_defs[] = { session_tracking_extops, NULL, parseSessionTracking, LDAP_SLIST_ENTRY_INITIALIZER(next) }, #endif +#ifdef SLAP_CONTROL_X_WHATFAILED + { LDAP_CONTROL_X_WHATFAILED, + (int)offsetof(struct slap_control_ids, sc_whatFailed), + SLAP_CTRL_GLOBAL|SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, + NULL, NULL, + parseWhatFailed, LDAP_SLIST_ENTRY_INITIALIZER(next) }, +#endif + { NULL, 0, 0, NULL, 0, NULL, LDAP_SLIST_ENTRY_INITIALIZER(next) } }; @@ -334,6 +344,38 @@ register_supported_control2(const char *controloid, return LDAP_SUCCESS; } +#ifdef SLAP_CONFIG_DELETE +int +unregister_supported_control( const char *controloid ) +{ + struct slap_control *sc; + int i; + + if ( controloid == NULL || (sc = find_ctrl( controloid )) == NULL ){ + return -1; + } + + for ( i = 0; slap_known_controls[ i ]; i++ ) { + if ( strcmp( controloid, slap_known_controls[ i ] ) == 0 ) { + do { + slap_known_controls[ i ] = slap_known_controls[ i+1 ]; + } while ( slap_known_controls[ i++ ] ); + num_known_controls--; + break; + } + } + + LDAP_SLIST_REMOVE(&controls_list, sc, slap_control, sc_next); + ch_free( sc->sc_oid ); + if ( sc->sc_extendedopsbv != NULL ) { + ber_bvarray_free( sc->sc_extendedopsbv ); + } + ch_free( sc ); + + return 0; +} +#endif /* SLAP_CONFIG_DELETE */ + /* * One-time initialization of internal controls. */ @@ -529,6 +571,37 @@ void slap_free_ctrls( op->o_tmpfree( ctrls, op->o_tmpmemctx ); } +int slap_add_ctrls( + Operation *op, + SlapReply *rs, + LDAPControl **ctrls ) +{ + int i = 0, j; + LDAPControl **ctrlsp; + + if ( rs->sr_ctrls ) { + for ( ; rs->sr_ctrls[ i ]; i++ ) ; + } + + for ( j=0; ctrls[j]; j++ ) ; + + ctrlsp = op->o_tmpalloc(( i+j+1 )*sizeof(LDAPControl *), op->o_tmpmemctx ); + i = 0; + if ( rs->sr_ctrls ) { + for ( ; rs->sr_ctrls[i]; i++ ) + ctrlsp[i] = rs->sr_ctrls[i]; + } + for ( j=0; ctrls[j]; j++) + ctrlsp[i++] = ctrls[j]; + 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; + return i; +} + int slap_parse_ctrl( Operation *op, SlapReply *rs, @@ -536,6 +609,7 @@ int slap_parse_ctrl( const char **text ) { struct slap_control *sc; + int rc = LDAP_SUCCESS; sc = find_ctrl( control->ldctl_oid ); if( sc != NULL ) { @@ -591,31 +665,29 @@ int slap_parse_ctrl( if (( sc->sc_mask & tagmask ) == tagmask ) { /* available extension */ - int rc; + if ( sc->sc_parse ) { + rc = sc->sc_parse( op, rs, control ); + assert( rc != LDAP_UNAVAILABLE_CRITICAL_EXTENSION ); - if( !sc->sc_parse ) { + } else if ( control->ldctl_iscritical ) { *text = "not yet implemented"; - return LDAP_OTHER; + rc = LDAP_OTHER; } - rc = sc->sc_parse( op, rs, control ); - if ( rc ) { - assert( rc != LDAP_UNAVAILABLE_CRITICAL_EXTENSION ); - return rc; - } - } else if( control->ldctl_iscritical ) { + } else if ( control->ldctl_iscritical ) { /* unavailable CRITICAL control */ *text = "critical extension is unavailable"; - return LDAP_UNAVAILABLE_CRITICAL_EXTENSION; + rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION; } - } else if( control->ldctl_iscritical ) { + + } else if ( control->ldctl_iscritical ) { /* unrecognized CRITICAL control */ *text = "critical extension is not recognized"; - return LDAP_UNAVAILABLE_CRITICAL_EXTENSION; + rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION; } - return LDAP_SUCCESS; + return rc; } int get_ctrls( @@ -629,6 +701,15 @@ int get_ctrls( char *opaque; BerElement *ber = op->o_ber; struct berval bv; +#ifdef SLAP_CONTROL_X_WHATFAILED + /* NOTE: right now, slapd checks the validity of each control + * while parsing. As a consequence, it can only detect one + * cause of failure at a time. This results in returning + * exactly one OID with the whatFailed control, or no control + * at all. + */ + char *failed_oid = NULL; +#endif len = ber_pvt_ber_remaining(ber); @@ -769,6 +850,9 @@ int get_ctrls( rs->sr_err = slap_parse_ctrl( op, rs, c, &rs->sr_text ); if ( rs->sr_err != LDAP_SUCCESS ) { +#ifdef SLAP_CONTROL_X_WHATFAILED + failed_oid = c->ldctl_oid; +#endif goto return_results; } } @@ -784,6 +868,71 @@ return_results: send_ldap_disconnect( op, rs ); rs->sr_err = SLAPD_DISCONNECT; } else { +#ifdef SLAP_CONTROL_X_WHATFAILED + /* might have not been parsed yet? */ + if ( failed_oid != NULL ) { + if ( !get_whatFailed( op ) ) { + /* look it up */ + + /* step through each remaining element */ + for ( ; tag != LBER_ERROR; tag = ber_next_element( ber, &len, opaque ) ) + { + LDAPControl c = { 0 }; + + tag = ber_scanf( ber, "{m" /*}*/, &bv ); + c.ldctl_oid = bv.bv_val; + + if ( tag == LBER_ERROR ) { + slap_free_ctrls( op, op->o_ctrls ); + op->o_ctrls = NULL; + break; + + } else if ( c.ldctl_oid == NULL ) { + slap_free_ctrls( op, op->o_ctrls ); + op->o_ctrls = NULL; + break; + } + + tag = ber_peek_tag( ber, &len ); + if ( tag == LBER_BOOLEAN ) { + ber_int_t crit; + tag = ber_scanf( ber, "b", &crit ); + if( tag == LBER_ERROR ) { + slap_free_ctrls( op, op->o_ctrls ); + op->o_ctrls = NULL; + break; + } + + tag = ber_peek_tag( ber, &len ); + } + + if ( tag == LBER_OCTETSTRING ) { + tag = ber_scanf( ber, "m", &c.ldctl_value ); + + if( tag == LBER_ERROR ) { + slap_free_ctrls( op, op->o_ctrls ); + op->o_ctrls = NULL; + break; + } + } + + if ( strcmp( c.ldctl_oid, LDAP_CONTROL_X_WHATFAILED ) == 0 ) { + const char *text; + slap_parse_ctrl( op, rs, &c, &text ); + break; + } + } + } + + if ( get_whatFailed( op ) ) { + char *oids[ 2 ]; + oids[ 0 ] = failed_oid; + oids[ 1 ] = NULL; + slap_ctrl_whatFailed_add( op, rs, oids ); + } + } +#endif + send_ldap_result( op, rs ); } } @@ -875,12 +1024,17 @@ static int parseDontUseCopy ( return LDAP_PROTOCOL_ERROR; } - if ( !ctrl->ldctl_iscritical ) { + if ( ( global_disallows & SLAP_DISALLOW_DONTUSECOPY_N_CRIT ) + && !ctrl->ldctl_iscritical ) + { rs->sr_text = "dontUseCopy criticality of FALSE not allowed"; return LDAP_PROTOCOL_ERROR; } - op->o_dontUseCopy = SLAP_CONTROL_CRITICAL; + op->o_dontUseCopy = ctrl->ldctl_iscritical + ? SLAP_CONTROL_CRITICAL + : SLAP_CONTROL_NONCRITICAL; + return LDAP_SUCCESS; } @@ -946,6 +1100,13 @@ static int parseProxyAuthz ( return LDAP_PROTOCOL_ERROR; } + if ( ( global_disallows & SLAP_DISALLOW_PROXY_AUTHZ_N_CRIT ) + && !ctrl->ldctl_iscritical ) + { + rs->sr_text = "proxied authorization criticality of FALSE not allowed"; + return LDAP_PROTOCOL_ERROR; + } + if ( !( global_allows & SLAP_ALLOW_PROXY_AUTHZ_ANON ) && BER_BVISEMPTY( &op->o_ndn ) ) { @@ -1008,14 +1169,13 @@ static int parseProxyAuthz ( } ch_free( op->o_ndn.bv_val ); - ch_free( op->o_dn.bv_val ); /* * NOTE: since slap_sasl_getdn() returns a normalized dn, * from now on op->o_dn is normalized */ op->o_ndn = dn; - ber_dupbv( &op->o_dn, &dn ); + ber_bvreplace( &op->o_dn, &dn ); Statslog( LDAP_DEBUG_STATS, "%s PROXYAUTHZ dn=\"%s\"\n", op->o_log_prefix, dn.bv_val, 0, 0, 0 ); @@ -1105,6 +1265,8 @@ static int parsePagedResults ( if ( !cookie.bv_len ) { ps->ps_count = 0; ps->ps_cookie = 0; + /* taint ps_cookie, to detect whether it's set */ + op->o_conn->c_pagedresults_state.ps_cookie = NOID; } /* NOTE: according to RFC 2696 3.: @@ -1112,11 +1274,11 @@ static int parsePagedResults ( If the page size is greater than or equal to the sizeLimit value, the server should ignore the control as the request can be satisfied in a single page. - + * NOTE: this assumes that the op->ors_slimit be set * before the controls are parsed. */ - + if ( op->ors_slimit > 0 && size >= op->ors_slimit ) { op->o_pagedresults = SLAP_CONTROL_IGNORED; @@ -1192,7 +1354,7 @@ static int parseAssert ( rs->sr_text = "assert control: internal error"; return LDAP_OTHER; } - + rs->sr_err = get_filter( op, ber, (Filter **)&(op->o_assertion), &rs->sr_text); (void) ber_free( ber, 1 ); @@ -1205,7 +1367,7 @@ static int parseAssert ( send_ldap_result( op, rs ); } if( op->o_assertion != NULL ) { - filter_free_x( op, op->o_assertion ); + filter_free_x( op, op->o_assertion, 1 ); } return rs->sr_err; } @@ -1226,113 +1388,47 @@ static int parseAssert ( return LDAP_SUCCESS; } -static int parsePreRead ( - Operation *op, - SlapReply *rs, - LDAPControl *ctrl ) -{ - ber_len_t siz, off, i; - AttributeName *an = NULL; - BerElement *ber; +#define READMSG(post, msg) \ + ( post ? "postread control: " msg : "preread control: " msg ) - if ( op->o_preread != SLAP_CONTROL_NONE ) { - rs->sr_text = "preread control specified multiple times"; - return LDAP_PROTOCOL_ERROR; - } - - if ( BER_BVISNULL( &ctrl->ldctl_value )) { - rs->sr_text = "preread control value is absent"; - return LDAP_PROTOCOL_ERROR; - } - - if ( BER_BVISEMPTY( &ctrl->ldctl_value )) { - rs->sr_text = "preread control value is empty"; - return LDAP_PROTOCOL_ERROR; - } - -#ifdef LDAP_X_TXN - if ( op->o_txnSpec ) { /* temporary limitation */ - rs->sr_text = "cannot perform pre-read in transaction"; - return LDAP_UNWILLING_TO_PERFORM; - } -#endif - - ber = ber_init( &(ctrl->ldctl_value) ); - if (ber == NULL) { - rs->sr_text = "preread control: internal error"; - return LDAP_OTHER; - } - - rs->sr_err = LDAP_SUCCESS; - - siz = sizeof( AttributeName ); - off = offsetof( AttributeName, an_name ); - if ( ber_scanf( ber, "{M}", &an, &siz, off ) == LBER_ERROR ) { - rs->sr_text = "preread control: decoding error"; - rs->sr_err = LDAP_PROTOCOL_ERROR; - goto done; - } - - for( i=0; isr_err = slap_bv2ad( &an[i].an_name, &an[i].an_desc, &dummy ); - if ( rs->sr_err != LDAP_SUCCESS && ctrl->ldctl_iscritical ) { - rs->sr_text = dummy - ? dummy - : "postread control: unknown attributeType"; - goto done; - } - } - - op->o_preread = ctrl->ldctl_iscritical - ? SLAP_CONTROL_CRITICAL - : SLAP_CONTROL_NONCRITICAL; - - op->o_preread_attrs = an; - -done: - (void) ber_free( ber, 1 ); - return rs->sr_err; -} - -static int parsePostRead ( +static int +parseReadAttrs( Operation *op, SlapReply *rs, - LDAPControl *ctrl ) + LDAPControl *ctrl, + int post ) { - ber_len_t siz, off, i; - AttributeName *an = NULL; + ber_len_t siz, off, i; BerElement *ber; + AttributeName *an = NULL; - if ( op->o_postread != SLAP_CONTROL_NONE ) { - rs->sr_text = "postread control specified multiple times"; + if ( ( post && op->o_postread != SLAP_CONTROL_NONE ) || + ( !post && op->o_preread != SLAP_CONTROL_NONE ) ) + { + rs->sr_text = READMSG( post, "specified multiple times" ); return LDAP_PROTOCOL_ERROR; } - if ( BER_BVISNULL( &ctrl->ldctl_value )) { - rs->sr_text = "postread control value is absent"; + if ( BER_BVISNULL( &ctrl->ldctl_value ) ) { + rs->sr_text = READMSG( post, "value is absent" ); return LDAP_PROTOCOL_ERROR; } - if ( BER_BVISEMPTY( &ctrl->ldctl_value )) { - rs->sr_text = "postread control value is empty"; + if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) { + rs->sr_text = READMSG( post, "value is empty" ); return LDAP_PROTOCOL_ERROR; } #ifdef LDAP_X_TXN if ( op->o_txnSpec ) { /* temporary limitation */ - rs->sr_text = "cannot perform post-read in transaction"; + rs->sr_text = READMSG( post, "cannot perform in transaction" ); return LDAP_UNWILLING_TO_PERFORM; } #endif - ber = ber_init( &(ctrl->ldctl_value) ); - if (ber == NULL) { - rs->sr_text = "postread control: internal error"; + ber = ber_init( &ctrl->ldctl_value ); + if ( ber == NULL ) { + rs->sr_text = READMSG( post, "internal error" ); return LDAP_OTHER; } @@ -1340,7 +1436,7 @@ static int parsePostRead ( siz = sizeof( AttributeName ); off = offsetof( AttributeName, an_name ); if ( ber_scanf( ber, "{M}", &an, &siz, off ) == LBER_ERROR ) { - rs->sr_text = "postread control: decoding error"; + rs->sr_text = READMSG( post, "decoding error" ); rs->sr_err = LDAP_PROTOCOL_ERROR; goto done; } @@ -1351,10 +1447,13 @@ static int parsePostRead ( an[i].an_desc = NULL; an[i].an_oc = NULL; - an[i].an_oc_exclude = 0; + an[i].an_flags = 0; rc = slap_bv2ad( &an[i].an_name, &an[i].an_desc, &dummy ); - if ( rc != LDAP_SUCCESS ) { - int i; + if ( rc == LDAP_SUCCESS ) { + an[i].an_name = an[i].an_desc->ad_cname; + + } else { + int j; static struct berval special_attrs[] = { BER_BVC( LDAP_NO_ATTRS ), BER_BVC( LDAP_ALL_USER_ATTRIBUTES ), @@ -1363,33 +1462,55 @@ static int parsePostRead ( }; /* deal with special attribute types */ - for ( i = 0; !BER_BVISNULL( &special_attrs[ i ] ); i++ ) { - if ( bvmatch( &an[i].an_name, &special_attrs[ i ] ) ) { + for ( j = 0; !BER_BVISNULL( &special_attrs[ j ] ); j++ ) { + if ( bvmatch( &an[i].an_name, &special_attrs[ j ] ) ) { + an[i].an_name = special_attrs[ j ]; break; } } - if ( BER_BVISNULL( &special_attrs[ i ] ) && ctrl->ldctl_iscritical ) { + if ( BER_BVISNULL( &special_attrs[ j ] ) && ctrl->ldctl_iscritical ) { rs->sr_err = rc; - rs->sr_text = dummy - ? dummy - : "postread control: unknown attributeType"; + rs->sr_text = dummy ? dummy + : READMSG( post, "unknown attributeType" ); goto done; } } } - op->o_postread = ctrl->ldctl_iscritical - ? SLAP_CONTROL_CRITICAL - : SLAP_CONTROL_NONCRITICAL; - - op->o_postread_attrs = an; + if ( post ) { + op->o_postread_attrs = an; + op->o_postread = ctrl->ldctl_iscritical + ? SLAP_CONTROL_CRITICAL + : SLAP_CONTROL_NONCRITICAL; + } else { + op->o_preread_attrs = an; + op->o_preread = ctrl->ldctl_iscritical + ? SLAP_CONTROL_CRITICAL + : SLAP_CONTROL_NONCRITICAL; + } done: (void) ber_free( ber, 1 ); return rs->sr_err; } +static int parsePreRead ( + Operation *op, + SlapReply *rs, + LDAPControl *ctrl ) +{ + return parseReadAttrs( op, rs, ctrl, 0 ); +} + +static int parsePostRead ( + Operation *op, + SlapReply *rs, + LDAPControl *ctrl ) +{ + return parseReadAttrs( op, rs, ctrl, 1 ); +} + static int parseValuesReturnFilter ( Operation *op, SlapReply *rs, @@ -1418,7 +1539,7 @@ static int parseValuesReturnFilter ( rs->sr_text = "internal error"; return LDAP_OTHER; } - + rs->sr_err = get_vrFilter( op, ber, (ValuesReturnFilter **)&(op->o_vrFilter), &rs->sr_text); @@ -1492,7 +1613,7 @@ static int parsePermissiveModify ( return LDAP_PROTOCOL_ERROR; } - if ( BER_BVISNULL( &ctrl->ldctl_value )) { + if ( !BER_BVISNULL( &ctrl->ldctl_value )) { rs->sr_text = "permissiveModify control value not absent"; return LDAP_PROTOCOL_ERROR; } @@ -1583,6 +1704,24 @@ static int parseSearchOptions ( return LDAP_PROTOCOL_ERROR; } + if ( search_flags & ~(LDAP_SEARCH_FLAG_DOMAIN_SCOPE) ) { + /* Search flags not recognised so far, + * including: + * LDAP_SEARCH_FLAG_PHANTOM_ROOT + */ + if ( ctrl->ldctl_iscritical ) { + rs->sr_text = "searchOptions contained unrecognized flag"; + return LDAP_UNWILLING_TO_PERFORM; + } + + /* Ignore */ + Debug( LDAP_DEBUG_TRACE, + "searchOptions: conn=%lu unrecognized flag(s) 0x%x (non-critical)\n", + op->o_connid, (unsigned)search_flags, 0 ); + + return LDAP_SUCCESS; + } + if ( search_flags & LDAP_SEARCH_FLAG_DOMAIN_SCOPE ) { if ( op->o_domain_scope != SLAP_CONTROL_NONE ) { rs->sr_text = "searchOptions control specified multiple times " @@ -1595,15 +1734,6 @@ static int parseSearchOptions ( : SLAP_CONTROL_NONCRITICAL; } - if ( search_flags & ~(LDAP_SEARCH_FLAG_DOMAIN_SCOPE) ) { - /* Other search flags not recognised so far, - * including: - * LDAP_SEARCH_FLAG_PHANTOM_ROOM - */ - rs->sr_text = "searchOptions contained unrecognized flag"; - return LDAP_UNWILLING_TO_PERFORM; - } - return LDAP_SUCCESS; } @@ -1906,3 +2036,87 @@ slap_ctrl_session_tracking_request_add( Operation *op, SlapReply *rs, LDAPContro return slap_ctrl_session_tracking_add( op, rs, &ip, &name, &id, ctrl ); } #endif + +#ifdef SLAP_CONTROL_X_WHATFAILED +static int parseWhatFailed( + Operation *op, + SlapReply *rs, + LDAPControl *ctrl ) +{ + if ( op->o_whatFailed != SLAP_CONTROL_NONE ) { + rs->sr_text = "\"WHat Failed?\" control specified multiple times"; + return LDAP_PROTOCOL_ERROR; + } + + if ( !BER_BVISNULL( &ctrl->ldctl_value )) { + rs->sr_text = "\"What Failed?\" control value not absent"; + return LDAP_PROTOCOL_ERROR; + } + + op->o_whatFailed = ctrl->ldctl_iscritical + ? SLAP_CONTROL_CRITICAL + : SLAP_CONTROL_NONCRITICAL; + + return LDAP_SUCCESS; +} + +int +slap_ctrl_whatFailed_add( + Operation *op, + SlapReply *rs, + char **oids ) +{ + BerElementBuffer berbuf; + BerElement *ber = (BerElement *) &berbuf; + LDAPControl **ctrls = NULL; + struct berval ctrlval; + int i, rc = LDAP_SUCCESS; + + ber_init2( ber, NULL, LBER_USE_DER ); + ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx ); + ber_printf( ber, "[" /*]*/ ); + for ( i = 0; oids[ i ] != NULL; i++ ) { + ber_printf( ber, "s", oids[ i ] ); + } + ber_printf( ber, /*[*/ "]" ); + + if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) { + rc = LDAP_OTHER; + goto done; + } + + i = 0; + if ( rs->sr_ctrls != NULL ) { + for ( ; rs->sr_ctrls[ i ] != NULL; i++ ) { + if ( strcmp( rs->sr_ctrls[ i ]->ldctl_oid, LDAP_CONTROL_X_WHATFAILED ) != 0 ) { + /* TODO: add */ + assert( 0 ); + } + } + } + + ctrls = op->o_tmprealloc( rs->sr_ctrls, + sizeof(LDAPControl *)*( i + 2 ) + + sizeof(LDAPControl) + + ctrlval.bv_len + 1, + op->o_tmpmemctx ); + if ( ctrls == NULL ) { + rc = LDAP_OTHER; + goto done; + } + ctrls[ i + 1 ] = NULL; + ctrls[ i ] = (LDAPControl *)&ctrls[ i + 2 ]; + ctrls[ i ]->ldctl_oid = LDAP_CONTROL_X_WHATFAILED; + ctrls[ i ]->ldctl_iscritical = 0; + ctrls[ i ]->ldctl_value.bv_val = (char *)&ctrls[ i ][ 1 ]; + AC_MEMCPY( ctrls[ i ]->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len + 1 ); + ctrls[ i ]->ldctl_value.bv_len = ctrlval.bv_len; + + ber_free_buf( ber ); + + rs->sr_ctrls = ctrls; + +done:; + return rc; +} +#endif