1/* $NetBSD: extended.c,v 1.3 2021/08/14 16:14:55 christos Exp $ */ 2 3/* $OpenLDAP$ */ 4/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2021 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.3 2021/08/14 16:14:55 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 33BerElement * 34ldap_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 101int 102ldap_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 137int 138ldap_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 */ 182int 183ldap_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 */ 307int 308ldap_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 403free_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