/* ***** 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 ); }