Home | History | Annotate | Line # | Download | only in libldap
      1 /*	$NetBSD: extended.c,v 1.4 2025/09/05 21:16:21 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 1998-2024 The OpenLDAP Foundation.
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted only as authorized by the OpenLDAP
     11  * Public License.
     12  *
     13  * A copy of this license is available in the file LICENSE in the
     14  * top-level directory of the distribution or, alternatively, at
     15  * <http://www.OpenLDAP.org/license.html>.
     16  */
     17 
     18 #include <sys/cdefs.h>
     19 __RCSID("$NetBSD: extended.c,v 1.4 2025/09/05 21:16:21 christos Exp $");
     20 
     21 #include "portable.h"
     22 
     23 #include <stdio.h>
     24 #include <ac/stdlib.h>
     25 
     26 #include <ac/socket.h>
     27 #include <ac/string.h>
     28 #include <ac/time.h>
     29 
     30 #include "ldap-int.h"
     31 #include "ldap_log.h"
     32 
     33 BerElement *
     34 ldap_build_extended_req(
     35 	LDAP			*ld,
     36 	LDAP_CONST char	*reqoid,
     37 	struct berval	*reqdata,
     38 	LDAPControl		**sctrls,
     39 	LDAPControl		**cctrls,
     40 	ber_int_t		*msgidp )
     41 {
     42 	BerElement *ber;
     43 	int rc;
     44 
     45 	/* create a message to send */
     46 	if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
     47 		return( NULL );
     48 	}
     49 
     50 	LDAP_NEXT_MSGID( ld, *msgidp );
     51 	if ( reqdata != NULL ) {
     52 		rc = ber_printf( ber, "{it{tstON}", /* '}' */
     53 			*msgidp, LDAP_REQ_EXTENDED,
     54 			LDAP_TAG_EXOP_REQ_OID, reqoid,
     55 			LDAP_TAG_EXOP_REQ_VALUE, reqdata );
     56 
     57 	} else {
     58 		rc = ber_printf( ber, "{it{tsN}", /* '}' */
     59 			*msgidp, LDAP_REQ_EXTENDED,
     60 			LDAP_TAG_EXOP_REQ_OID, reqoid );
     61 	}
     62 
     63 	if( rc == -1 ) {
     64 		ld->ld_errno = LDAP_ENCODING_ERROR;
     65 		ber_free( ber, 1 );
     66 		return( NULL );
     67 	}
     68 
     69 	/* Put Server Controls */
     70 	if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
     71 		ber_free( ber, 1 );
     72 		return( NULL );
     73 	}
     74 
     75 	if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
     76 		ld->ld_errno = LDAP_ENCODING_ERROR;
     77 		ber_free( ber, 1 );
     78 		return( NULL );
     79 	}
     80 
     81 	return( ber );
     82 }
     83 
     84 /*
     85  * LDAPv3 Extended Operation Request
     86  *	ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
     87  *		requestName      [0] LDAPOID,
     88  *		requestValue     [1] OCTET STRING OPTIONAL
     89  *	}
     90  *
     91  * LDAPv3 Extended Operation Response
     92  *	ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
     93  *		COMPONENTS OF LDAPResult,
     94  *		responseName     [10] LDAPOID OPTIONAL,
     95  *		response         [11] OCTET STRING OPTIONAL
     96  *	}
     97  *
     98  * (Source RFC 4511)
     99  */
    100 
    101 int
    102 ldap_extended_operation(
    103 	LDAP			*ld,
    104 	LDAP_CONST char	*reqoid,
    105 	struct berval	*reqdata,
    106 	LDAPControl		**sctrls,
    107 	LDAPControl		**cctrls,
    108 	int				*msgidp )
    109 {
    110 	BerElement *ber;
    111 	ber_int_t id;
    112 
    113 	Debug0( LDAP_DEBUG_TRACE, "ldap_extended_operation\n" );
    114 
    115 	assert( ld != NULL );
    116 	assert( LDAP_VALID( ld ) );
    117 	assert( reqoid != NULL && *reqoid != '\0' );
    118 	assert( msgidp != NULL );
    119 
    120 	/* must be version 3 (or greater) */
    121 	if ( ld->ld_version < LDAP_VERSION3 ) {
    122 		ld->ld_errno = LDAP_NOT_SUPPORTED;
    123 		return( ld->ld_errno );
    124 	}
    125 
    126 	ber = ldap_build_extended_req( ld, reqoid, reqdata,
    127 		sctrls, cctrls, &id );
    128 	if ( !ber )
    129 		return( ld->ld_errno );
    130 
    131 	/* send the message */
    132 	*msgidp = ldap_send_initial_request( ld, LDAP_REQ_EXTENDED, NULL, ber, id );
    133 
    134 	return( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS );
    135 }
    136 
    137 int
    138 ldap_extended_operation_s(
    139 	LDAP			*ld,
    140 	LDAP_CONST char	*reqoid,
    141 	struct berval	*reqdata,
    142 	LDAPControl		**sctrls,
    143 	LDAPControl		**cctrls,
    144 	char			**retoidp,
    145 	struct berval	**retdatap )
    146 {
    147     int     rc;
    148     int     msgid;
    149     LDAPMessage *res;
    150 
    151 	Debug0( LDAP_DEBUG_TRACE, "ldap_extended_operation_s\n" );
    152 
    153 	assert( ld != NULL );
    154 	assert( LDAP_VALID( ld ) );
    155 	assert( reqoid != NULL && *reqoid != '\0' );
    156 
    157     rc = ldap_extended_operation( ld, reqoid, reqdata,
    158 		sctrls, cctrls, &msgid );
    159 
    160     if ( rc != LDAP_SUCCESS ) {
    161         return( rc );
    162 	}
    163 
    164     if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) {
    165         return( ld->ld_errno );
    166 	}
    167 
    168 	if ( retoidp != NULL ) *retoidp = NULL;
    169 	if ( retdatap != NULL ) *retdatap = NULL;
    170 
    171 	rc = ldap_parse_extended_result( ld, res, retoidp, retdatap, 0 );
    172 
    173 	if( rc != LDAP_SUCCESS ) {
    174 		ldap_msgfree( res );
    175 		return rc;
    176 	}
    177 
    178     return( ldap_result2error( ld, res, 1 ) );
    179 }
    180 
    181 /* Parse an extended result */
    182 int
    183 ldap_parse_extended_result (
    184 	LDAP			*ld,
    185 	LDAPMessage		*res,
    186 	char			**retoidp,
    187 	struct berval	**retdatap,
    188 	int				freeit )
    189 {
    190 	BerElement *ber;
    191 	ber_tag_t rc;
    192 	ber_tag_t tag;
    193 	ber_len_t len;
    194 	struct berval *resdata;
    195 	ber_int_t errcode;
    196 	char *resoid;
    197 
    198 	assert( ld != NULL );
    199 	assert( LDAP_VALID( ld ) );
    200 	assert( res != NULL );
    201 
    202 	Debug0( LDAP_DEBUG_TRACE, "ldap_parse_extended_result\n" );
    203 
    204 	if( ld->ld_version < LDAP_VERSION3 ) {
    205 		ld->ld_errno = LDAP_NOT_SUPPORTED;
    206 		return ld->ld_errno;
    207 	}
    208 
    209 	if( res->lm_msgtype != LDAP_RES_EXTENDED ) {
    210 		ld->ld_errno = LDAP_PARAM_ERROR;
    211 		return ld->ld_errno;
    212 	}
    213 
    214 	if( retoidp != NULL ) *retoidp = NULL;
    215 	if( retdatap != NULL ) *retdatap = NULL;
    216 
    217 	if ( ld->ld_error ) {
    218 		LDAP_FREE( ld->ld_error );
    219 		ld->ld_error = NULL;
    220 	}
    221 
    222 	if ( ld->ld_matched ) {
    223 		LDAP_FREE( ld->ld_matched );
    224 		ld->ld_matched = NULL;
    225 	}
    226 
    227 	ber = ber_dup( res->lm_ber );
    228 
    229 	if ( ber == NULL ) {
    230 		ld->ld_errno = LDAP_NO_MEMORY;
    231 		return ld->ld_errno;
    232 	}
    233 
    234 	rc = ber_scanf( ber, "{eAA" /*}*/, &errcode,
    235 		&ld->ld_matched, &ld->ld_error );
    236 
    237 	if( rc == LBER_ERROR ) {
    238 		ld->ld_errno = LDAP_DECODING_ERROR;
    239 		ber_free( ber, 0 );
    240 		return ld->ld_errno;
    241 	}
    242 
    243 	resoid = NULL;
    244 	resdata = NULL;
    245 
    246 	tag = ber_peek_tag( ber, &len );
    247 
    248 	if( tag == LDAP_TAG_REFERRAL ) {
    249 		/* skip over referral */
    250 		if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
    251 			ld->ld_errno = LDAP_DECODING_ERROR;
    252 			ber_free( ber, 0 );
    253 			return ld->ld_errno;
    254 		}
    255 
    256 		tag = ber_peek_tag( ber, &len );
    257 	}
    258 
    259 	if( tag == LDAP_TAG_EXOP_RES_OID ) {
    260 		/* we have a resoid */
    261 		if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) {
    262 			ld->ld_errno = LDAP_DECODING_ERROR;
    263 			ber_free( ber, 0 );
    264 			return ld->ld_errno;
    265 		}
    266 
    267 		assert( resoid[ 0 ] != '\0' );
    268 
    269 		tag = ber_peek_tag( ber, &len );
    270 	}
    271 
    272 	if( tag == LDAP_TAG_EXOP_RES_VALUE ) {
    273 		/* we have a resdata */
    274 		if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) {
    275 			ld->ld_errno = LDAP_DECODING_ERROR;
    276 			ber_free( ber, 0 );
    277 			if( resoid != NULL ) LDAP_FREE( resoid );
    278 			return ld->ld_errno;
    279 		}
    280 	}
    281 
    282 	ber_free( ber, 0 );
    283 
    284 	if( retoidp != NULL ) {
    285 		*retoidp = resoid;
    286 	} else {
    287 		LDAP_FREE( resoid );
    288 	}
    289 
    290 	if( retdatap != NULL ) {
    291 		*retdatap = resdata;
    292 	} else {
    293 		ber_bvfree( resdata );
    294 	}
    295 
    296 	ld->ld_errno = errcode;
    297 
    298 	if( freeit ) {
    299 		ldap_msgfree( res );
    300 	}
    301 
    302 	return LDAP_SUCCESS;
    303 }
    304 
    305 
    306 /* Parse an extended partial */
    307 int
    308 ldap_parse_intermediate (
    309 	LDAP			*ld,
    310 	LDAPMessage		*res,
    311 	char			**retoidp,
    312 	struct berval	**retdatap,
    313 	LDAPControl		***serverctrls,
    314 	int				freeit )
    315 {
    316 	BerElement *ber;
    317 	ber_tag_t tag;
    318 	ber_len_t len;
    319 	struct berval *resdata;
    320 	char *resoid;
    321 
    322 	assert( ld != NULL );
    323 	assert( LDAP_VALID( ld ) );
    324 	assert( res != NULL );
    325 
    326 	Debug0( LDAP_DEBUG_TRACE, "ldap_parse_intermediate\n" );
    327 
    328 	if( ld->ld_version < LDAP_VERSION3 ) {
    329 		ld->ld_errno = LDAP_NOT_SUPPORTED;
    330 		return ld->ld_errno;
    331 	}
    332 
    333 	if( res->lm_msgtype != LDAP_RES_INTERMEDIATE ) {
    334 		ld->ld_errno = LDAP_PARAM_ERROR;
    335 		return ld->ld_errno;
    336 	}
    337 
    338 	if( retoidp != NULL ) *retoidp = NULL;
    339 	if( retdatap != NULL ) *retdatap = NULL;
    340 	if( serverctrls != NULL ) *serverctrls = NULL;
    341 
    342 	ber = ber_dup( res->lm_ber );
    343 
    344 	if ( ber == NULL ) {
    345 		ld->ld_errno = LDAP_NO_MEMORY;
    346 		return ld->ld_errno;
    347 	}
    348 
    349 	tag = ber_scanf( ber, "{" /*}*/ );
    350 
    351 	if( tag == LBER_ERROR ) {
    352 		ld->ld_errno = LDAP_DECODING_ERROR;
    353 		ber_free( ber, 0 );
    354 		return ld->ld_errno;
    355 	}
    356 
    357 	resoid = NULL;
    358 	resdata = NULL;
    359 
    360 	tag = ber_peek_tag( ber, &len );
    361 
    362 	/*
    363 	 * NOTE: accept intermediate and extended response tag values
    364 	 * as older versions of slapd(8) incorrectly used extended
    365 	 * response tags.
    366 	 * Should be removed when 2.2 is moved to Historic.
    367 	 */
    368 	if( tag == LDAP_TAG_IM_RES_OID || tag == LDAP_TAG_EXOP_RES_OID ) {
    369 		/* we have a resoid */
    370 		if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) {
    371 			ld->ld_errno = LDAP_DECODING_ERROR;
    372 			ber_free( ber, 0 );
    373 			return ld->ld_errno;
    374 		}
    375 
    376 		assert( resoid[ 0 ] != '\0' );
    377 
    378 		tag = ber_peek_tag( ber, &len );
    379 	}
    380 
    381 	if( tag == LDAP_TAG_IM_RES_VALUE || tag == LDAP_TAG_EXOP_RES_VALUE ) {
    382 		/* we have a resdata */
    383 		if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) {
    384 			ld->ld_errno = LDAP_DECODING_ERROR;
    385 			ber_free( ber, 0 );
    386 			if( resoid != NULL ) LDAP_FREE( resoid );
    387 			return ld->ld_errno;
    388 		}
    389 	}
    390 
    391 	if ( serverctrls == NULL ) {
    392 		ld->ld_errno = LDAP_SUCCESS;
    393 		goto free_and_return;
    394 	}
    395 
    396 	if ( ber_scanf( ber, /*{*/ "}" ) == LBER_ERROR ) {
    397 		ld->ld_errno = LDAP_DECODING_ERROR;
    398 		goto free_and_return;
    399 	}
    400 
    401 	ld->ld_errno = ldap_pvt_get_controls( ber, serverctrls );
    402 
    403 free_and_return:
    404 	ber_free( ber, 0 );
    405 
    406 	if( retoidp != NULL ) {
    407 		*retoidp = resoid;
    408 	} else {
    409 		LDAP_FREE( resoid );
    410 	}
    411 
    412 	if( retdatap != NULL ) {
    413 		*retdatap = resdata;
    414 	} else {
    415 		ber_bvfree( resdata );
    416 	}
    417 
    418 	if( freeit ) {
    419 		ldap_msgfree( res );
    420 	}
    421 
    422 	return ld->ld_errno;
    423 }
    424 
    425