diff options
Diffstat (limited to 'ldap/c-sdk/libldap/control.c')
-rw-r--r-- | ldap/c-sdk/libldap/control.c | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/ldap/c-sdk/libldap/control.c b/ldap/c-sdk/libldap/control.c new file mode 100644 index 0000000000..5f2eb73758 --- /dev/null +++ b/ldap/c-sdk/libldap/control.c @@ -0,0 +1,559 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* control.c - routines to handle ldapv3 controls */ + +#include "ldap-int.h" + +static LDAPControl *ldap_control_dup( LDAPControl *ctrl ); +static int ldap_control_copy_contents( LDAPControl *ctrl_dst, + LDAPControl *ctrl_src ); + +/* + * Append a list of LDAPv3 controls to ber. If ctrls is NULL, use default + * set of controls from ld. + * Return an LDAP error code (LDAP_SUCCESS if all goes well). + * If closeseq is non-zero, we do an extra ber_put_seq() as well. + */ +int +nsldapi_put_controls( LDAP *ld, LDAPControl **ctrls, int closeseq, + BerElement *ber ) +{ + LDAPControl *c; + int rc, i; + + rc = LDAP_ENCODING_ERROR; /* the most popular error */ + + /* if no controls were passed in, use global list from LDAP * */ + LDAP_MUTEX_LOCK( ld, LDAP_CTRL_LOCK ); + if ( ctrls == NULL ) { + ctrls = ld->ld_servercontrols; + } + + /* if there are no controls then we are done */ + if ( ctrls == NULL || ctrls[ 0 ] == NULL ) { + goto clean_exit; + } + + /* + * If we're using LDAPv2 or earlier we can't send any controls, so + * we just ignore them unless one is marked critical, in which case + * we return an error. + */ + if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) { + for ( i = 0; ctrls != NULL && ctrls[i] != NULL; i++ ) { + if ( ctrls[i]->ldctl_iscritical ) { + rc = LDAP_NOT_SUPPORTED; + goto error_exit; + } + } + goto clean_exit; + } + + /* + * encode the controls as a Sequence of Sequence + */ + if ( ber_printf( ber, "t{", LDAP_TAG_CONTROLS ) == -1 ) { + goto error_exit; + } + + for ( i = 0; ctrls[i] != NULL; i++ ) { + c = ctrls[i]; + + if ( ber_printf( ber, "{s", c->ldctl_oid ) == -1 ) { + goto error_exit; + } + + /* criticality is "BOOLEAN DEFAULT FALSE" */ + /* therefore, it should only be encoded if it exists AND is TRUE */ + if ( c->ldctl_iscritical ) { + if ( ber_printf( ber, "b", (int)c->ldctl_iscritical ) + == -1 ) { + goto error_exit; + } + } + + if ( c->ldctl_value.bv_val != NULL ) { + if ( ber_printf( ber, "o", c->ldctl_value.bv_val, + c->ldctl_value.bv_len ) + == -1 ) { + goto error_exit; + } + } + + if ( ber_put_seq( ber ) == -1 ) { + goto error_exit; + } + } + + if ( ber_put_seq( ber ) == -1 ) { + goto error_exit; + } + +clean_exit: + LDAP_MUTEX_UNLOCK( ld, LDAP_CTRL_LOCK ); + if ( closeseq && ber_put_seq( ber ) == -1 ) { + goto error_exit; + } + return( LDAP_SUCCESS ); + +error_exit: + LDAP_MUTEX_UNLOCK( ld, LDAP_CTRL_LOCK ); + LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); + return( rc ); +} + + +/* + * Pull controls out of "ber" (if any present) and return them in "controlsp." + * Returns an LDAP error code. + */ +int +nsldapi_get_controls( BerElement *ber, LDAPControl ***controlsp ) +{ + LDAPControl *newctrl; + ber_tag_t tag; + ber_len_t len; + int rc, maxcontrols, curcontrols; + char *last; + + /* + * Each LDAPMessage can have a set of controls appended + * to it. Controls are used to extend the functionality + * of an LDAP operation (e.g., add an attribute size limit + * to the search operation). These controls look like this: + * + * Controls ::= SEQUENCE OF Control + * + * Control ::= SEQUENCE { + * controlType LDAPOID, + * criticality BOOLEAN DEFAULT FALSE, + * controlValue OCTET STRING + * } + */ + LDAPDebug( LDAP_DEBUG_TRACE, "=> nsldapi_get_controls\n", 0, 0, 0 ); + + *controlsp = NULL; + + /* + * check to see if controls were included + */ + if ( ber_get_option( ber, LBER_OPT_REMAINING_BYTES, &len ) != 0 ) { + return( LDAP_DECODING_ERROR ); /* unexpected error */ + } + if ( len == 0 ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "<= nsldapi_get_controls no controls\n", 0, 0, 0 ); + return( LDAP_SUCCESS ); /* no controls */ + } + if (( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) { + if ( tag == LBER_ERROR ) { + LDAPDebug( LDAP_DEBUG_TRACE, + "<= nsldapi_get_controls LDAP_PROTOCOL_ERROR\n", + 0, 0, 0 ); + return( LDAP_DECODING_ERROR ); /* decoding error */ + } + /* + * We found something other than controls. This should never + * happen in LDAPv3, but we don't treat this is a hard error -- + * we just ignore the extra stuff. + */ + LDAPDebug( LDAP_DEBUG_TRACE, + "<= nsldapi_get_controls ignoring unrecognized data in message (tag 0x%x)\n", + tag, 0, 0 ); + return( LDAP_SUCCESS ); + } + + maxcontrols = curcontrols = 0; + for ( tag = ber_first_element( ber, &len, &last ); + tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET; + tag = ber_next_element( ber, &len, last ) ) { + if ( curcontrols >= maxcontrols - 1 ) { +#define CONTROL_GRABSIZE 5 + maxcontrols += CONTROL_GRABSIZE; + *controlsp = (struct ldapcontrol **)NSLDAPI_REALLOC( + (char *)*controlsp, maxcontrols * + sizeof(struct ldapcontrol *) ); + if ( *controlsp == NULL ) { + rc = LDAP_NO_MEMORY; + goto free_and_return; + } + } + if (( newctrl = (struct ldapcontrol *)NSLDAPI_CALLOC( 1, + sizeof(LDAPControl))) == NULL ) { + rc = LDAP_NO_MEMORY; + goto free_and_return; + } + + (*controlsp)[curcontrols++] = newctrl; + (*controlsp)[curcontrols] = NULL; + + if ( ber_scanf( ber, "{a", &newctrl->ldctl_oid ) + == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto free_and_return; + } + + /* the criticality is optional */ + if ( ber_peek_tag( ber, &len ) == LBER_BOOLEAN ) { + int aint; + + if ( ber_scanf( ber, "b", &aint ) == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto free_and_return; + } + newctrl->ldctl_iscritical = (char)aint; /* XXX lossy cast */ + } else { + /* absent is synonomous with FALSE */ + newctrl->ldctl_iscritical = 0; + } + + /* the control value is optional */ + if ( ber_peek_tag( ber, &len ) == LBER_OCTETSTRING ) { + if ( ber_scanf( ber, "o", &newctrl->ldctl_value ) + == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto free_and_return; + } + } else { + (newctrl->ldctl_value).bv_val = NULL; + (newctrl->ldctl_value).bv_len = 0; + } + + } + + if ( tag == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto free_and_return; + } + + LDAPDebug( LDAP_DEBUG_TRACE, + "<= nsldapi_get_controls found %d controls\n", curcontrols, 0, 0 ); + return( LDAP_SUCCESS ); + +free_and_return:; + ldap_controls_free( *controlsp ); + *controlsp = NULL; + LDAPDebug( LDAP_DEBUG_TRACE, + "<= nsldapi_get_controls error 0x%x\n", rc, 0, 0 ); + return( rc ); +} + +/* + * Skips forward in a ber to find a control tag, then calls on + * nsldapi_get_controls() to parse them into an LDAPControl list. + * Returns an LDAP error code. + */ +int +nsldapi_find_controls( BerElement *ber, LDAPControl ***controlsp ) +{ + ber_tag_t tag; + ber_len_t len; + + if ( ber == NULLBER ) { + return( LDAP_DECODING_ERROR ); + } + + tag = ber_peek_tag( ber, &len ); + + while( tag != LDAP_TAG_CONTROLS && tag != LBER_DEFAULT ) { + tag = ber_skip_tag( ber, &len ); + /* Skip ahead to the next sequence */ + ber->ber_ptr += len; + tag = ber_peek_tag( ber, &len ); + } + + return( nsldapi_get_controls( ber, controlsp ) ); +} + + +void +LDAP_CALL +ldap_control_free( LDAPControl *ctrl ) +{ + if ( ctrl != NULL ) { + if ( ctrl->ldctl_oid != NULL ) { + NSLDAPI_FREE( ctrl->ldctl_oid ); + } + if ( ctrl->ldctl_value.bv_val != NULL ) { + NSLDAPI_FREE( ctrl->ldctl_value.bv_val ); + } + NSLDAPI_FREE( (char *)ctrl ); + } +} + + +void +LDAP_CALL +ldap_controls_free( LDAPControl **ctrls ) +{ + int i; + + if ( ctrls != NULL ) { + for ( i = 0; ctrls[i] != NULL; i++ ) { + ldap_control_free( ctrls[i] ); + } + NSLDAPI_FREE( (char *)ctrls ); + } +} + +LDAPControl * +LDAP_CALL +ldap_find_control( const char *oid, LDAPControl **ctrls ) +{ + int i, foundControl; + LDAPControl *Ctrlp = NULL; + + /* find the control in the list of controls if it exists */ + if ( ctrls == NULL ) { + return ( NULL ); + } + foundControl = 0; + for ( i = 0; (( ctrls[i] != NULL ) && ( !foundControl )); i++ ) { + foundControl = !strcmp( ctrls[i]->ldctl_oid, oid ); + } + if ( !foundControl ) { + return ( NULL ); + } else { + /* let local var point to the control */ + Ctrlp = ctrls[i-1]; + } + + return( Ctrlp ); +} + +#if 0 +LDAPControl ** +LDAP_CALL +ldap_control_append( LDAPControl **ctrl_src, LDAPControl *ctrl ) +{ + int nctrls = 0; + LDAPControl **ctrlp; + int i; + + if ( NULL == ctrl ) + return ( NULL ); + + /* Count the existing controls */ + if ( NULL != ctrl_src ) { + while( NULL != ctrl_src[nctrls] ) { + nctrls++; + } + } + + /* allocate the new control structure */ + if ( ( ctrlp = (LDAPControl **)NSLDAPI_MALLOC( sizeof(LDAPControl *) + * (nctrls + 2) ) ) == NULL ) { + return( NULL ); + } + memset( ctrlp, 0, sizeof(*ctrlp) * (nctrls + 2) ); + + for( i = 0; i < (nctrls + 1); i++ ) { + if ( i < nctrls ) { + ctrlp[i] = ldap_control_dup( ctrl_src[i] ); + } else { + ctrlp[i] = ldap_control_dup( ctrl ); + } + if ( NULL == ctrlp[i] ) { + ldap_controls_free( ctrlp ); + return( NULL ); + } + } + return ctrlp; +} +#endif /* 0 */ + + +/* + * Replace *ldctrls with a copy of newctrls. + * returns 0 if successful. + * return -1 if not and set error code inside LDAP *ld. + */ +int +nsldapi_dup_controls( LDAP *ld, LDAPControl ***ldctrls, LDAPControl **newctrls ) +{ + int count; + + if ( *ldctrls != NULL ) { + ldap_controls_free( *ldctrls ); + } + + if ( newctrls == NULL || newctrls[0] == NULL ) { + *ldctrls = NULL; + return( 0 ); + } + + for ( count = 0; newctrls[ count ] != NULL; ++count ) { + ; + } + + if (( *ldctrls = (LDAPControl **)NSLDAPI_MALLOC(( count + 1 ) * + sizeof( LDAPControl *))) == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + (*ldctrls)[ count ] = NULL; + + for ( count = 0; newctrls[ count ] != NULL; ++count ) { + if (( (*ldctrls)[ count ] = + ldap_control_dup( newctrls[ count ] )) == NULL ) { + ldap_controls_free( *ldctrls ); + *ldctrls = NULL; + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + } + + return( 0 ); +} + + +/* + * return a malloc'd copy of "ctrl" (NULL if memory allocation fails) + */ +static LDAPControl * +/* LDAP_CALL */ /* keep this routine internal for now */ +ldap_control_dup( LDAPControl *ctrl ) +{ + LDAPControl *rctrl; + + if (( rctrl = (LDAPControl *)NSLDAPI_MALLOC( sizeof( LDAPControl ))) + == NULL ) { + return( NULL ); + } + + if ( ldap_control_copy_contents( rctrl, ctrl ) != LDAP_SUCCESS ) { + NSLDAPI_FREE( rctrl ); + return( NULL ); + } + + return( rctrl ); +} + + +/* + * duplicate the contents of "ctrl_src" and place in "ctrl_dst" + */ +static int +/* LDAP_CALL */ /* keep this routine internal for now */ +ldap_control_copy_contents( LDAPControl *ctrl_dst, LDAPControl *ctrl_src ) +{ + size_t len; + + if ( NULL == ctrl_dst || NULL == ctrl_src ) { + return( LDAP_PARAM_ERROR ); + } + + ctrl_dst->ldctl_iscritical = ctrl_src->ldctl_iscritical; + + /* fill in the fields of this new control */ + if (( ctrl_dst->ldctl_oid = nsldapi_strdup( ctrl_src->ldctl_oid )) + == NULL ) { + return( LDAP_NO_MEMORY ); + } + + len = (size_t)(ctrl_src->ldctl_value).bv_len; + if ( ctrl_src->ldctl_value.bv_val == NULL || len <= 0 ) { + ctrl_dst->ldctl_value.bv_len = 0; + ctrl_dst->ldctl_value.bv_val = NULL; + } else { + ctrl_dst->ldctl_value.bv_len = len; + if (( ctrl_dst->ldctl_value.bv_val = NSLDAPI_MALLOC( len )) + == NULL ) { + NSLDAPI_FREE( ctrl_dst->ldctl_oid ); + return( LDAP_NO_MEMORY ); + } + SAFEMEMCPY( ctrl_dst->ldctl_value.bv_val, + ctrl_src->ldctl_value.bv_val, len ); + } + + return ( LDAP_SUCCESS ); +} + + + +/* + * build an allocated LDAPv3 control. Returns an LDAP error code. + */ +int +nsldapi_build_control( char *oid, BerElement *ber, int freeber, char iscritical, + LDAPControl **ctrlp ) +{ + int rc; + struct berval *bvp; + + if ( ber == NULL ) { + bvp = NULL; + } else { + /* allocate struct berval with contents of the BER encoding */ + rc = ber_flatten( ber, &bvp ); + if ( freeber ) { + ber_free( ber, 1 ); + } + if ( rc == -1 ) { + return( LDAP_NO_MEMORY ); + } + } + + /* allocate the new control structure */ + if (( *ctrlp = (LDAPControl *)NSLDAPI_MALLOC( sizeof(LDAPControl))) + == NULL ) { + if ( bvp != NULL ) { + ber_bvfree( bvp ); + } + return( LDAP_NO_MEMORY ); + } + + /* fill in the fields of this new control */ + (*ctrlp)->ldctl_iscritical = iscritical; + if (( (*ctrlp)->ldctl_oid = nsldapi_strdup( oid )) == NULL ) { + NSLDAPI_FREE( *ctrlp ); + if ( bvp != NULL ) { + ber_bvfree( bvp ); + } + return( LDAP_NO_MEMORY ); + } + + if ( bvp == NULL ) { + (*ctrlp)->ldctl_value.bv_len = 0; + (*ctrlp)->ldctl_value.bv_val = NULL; + } else { + (*ctrlp)->ldctl_value = *bvp; /* struct copy */ + NSLDAPI_FREE( bvp ); /* free container, not contents! */ + } + + return( LDAP_SUCCESS ); +} |