summaryrefslogtreecommitdiffstats
path: root/ldap/c-sdk/libraries/libldap/control.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/c-sdk/libraries/libldap/control.c')
-rw-r--r--ldap/c-sdk/libraries/libldap/control.c559
1 files changed, 559 insertions, 0 deletions
diff --git a/ldap/c-sdk/libraries/libldap/control.c b/ldap/c-sdk/libraries/libldap/control.c
new file mode 100644
index 000000000..5f2eb7375
--- /dev/null
+++ b/ldap/c-sdk/libraries/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 );
+}