1*549b59edSchristos /* $NetBSD: extended.c,v 1.3 2021/08/14 16:14:55 christos Exp $ */
24e6df137Slukem
3d11b170bStron /* $OpenLDAP$ */
42de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
52de962bdSlukem *
6*549b59edSchristos * Copyright 1998-2021 The OpenLDAP Foundation.
72de962bdSlukem * All rights reserved.
82de962bdSlukem *
92de962bdSlukem * Redistribution and use in source and binary forms, with or without
102de962bdSlukem * modification, are permitted only as authorized by the OpenLDAP
112de962bdSlukem * Public License.
122de962bdSlukem *
132de962bdSlukem * A copy of this license is available in the file LICENSE in the
142de962bdSlukem * top-level directory of the distribution or, alternatively, at
152de962bdSlukem * <http://www.OpenLDAP.org/license.html>.
162de962bdSlukem */
172de962bdSlukem
18376af7d7Schristos #include <sys/cdefs.h>
19*549b59edSchristos __RCSID("$NetBSD: extended.c,v 1.3 2021/08/14 16:14:55 christos Exp $");
20376af7d7Schristos
212de962bdSlukem #include "portable.h"
222de962bdSlukem
232de962bdSlukem #include <stdio.h>
242de962bdSlukem #include <ac/stdlib.h>
252de962bdSlukem
262de962bdSlukem #include <ac/socket.h>
272de962bdSlukem #include <ac/string.h>
282de962bdSlukem #include <ac/time.h>
292de962bdSlukem
302de962bdSlukem #include "ldap-int.h"
312de962bdSlukem #include "ldap_log.h"
322de962bdSlukem
33376af7d7Schristos BerElement *
ldap_build_extended_req(LDAP * ld,LDAP_CONST char * reqoid,struct berval * reqdata,LDAPControl ** sctrls,LDAPControl ** cctrls,ber_int_t * msgidp)34376af7d7Schristos ldap_build_extended_req(
35376af7d7Schristos LDAP *ld,
36376af7d7Schristos LDAP_CONST char *reqoid,
37376af7d7Schristos struct berval *reqdata,
38376af7d7Schristos LDAPControl **sctrls,
39376af7d7Schristos LDAPControl **cctrls,
40376af7d7Schristos ber_int_t *msgidp )
41376af7d7Schristos {
42376af7d7Schristos BerElement *ber;
43376af7d7Schristos int rc;
44376af7d7Schristos
45376af7d7Schristos /* create a message to send */
46376af7d7Schristos if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
47376af7d7Schristos return( NULL );
48376af7d7Schristos }
49376af7d7Schristos
50376af7d7Schristos LDAP_NEXT_MSGID( ld, *msgidp );
51376af7d7Schristos if ( reqdata != NULL ) {
52376af7d7Schristos rc = ber_printf( ber, "{it{tstON}", /* '}' */
53376af7d7Schristos *msgidp, LDAP_REQ_EXTENDED,
54376af7d7Schristos LDAP_TAG_EXOP_REQ_OID, reqoid,
55376af7d7Schristos LDAP_TAG_EXOP_REQ_VALUE, reqdata );
56376af7d7Schristos
57376af7d7Schristos } else {
58376af7d7Schristos rc = ber_printf( ber, "{it{tsN}", /* '}' */
59376af7d7Schristos *msgidp, LDAP_REQ_EXTENDED,
60376af7d7Schristos LDAP_TAG_EXOP_REQ_OID, reqoid );
61376af7d7Schristos }
62376af7d7Schristos
63376af7d7Schristos if( rc == -1 ) {
64376af7d7Schristos ld->ld_errno = LDAP_ENCODING_ERROR;
65376af7d7Schristos ber_free( ber, 1 );
66376af7d7Schristos return( NULL );
67376af7d7Schristos }
68376af7d7Schristos
69376af7d7Schristos /* Put Server Controls */
70376af7d7Schristos if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
71376af7d7Schristos ber_free( ber, 1 );
72376af7d7Schristos return( NULL );
73376af7d7Schristos }
74376af7d7Schristos
75376af7d7Schristos if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
76376af7d7Schristos ld->ld_errno = LDAP_ENCODING_ERROR;
77376af7d7Schristos ber_free( ber, 1 );
78376af7d7Schristos return( NULL );
79376af7d7Schristos }
80376af7d7Schristos
81376af7d7Schristos return( ber );
82376af7d7Schristos }
83376af7d7Schristos
842de962bdSlukem /*
852de962bdSlukem * LDAPv3 Extended Operation Request
862de962bdSlukem * ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
872de962bdSlukem * requestName [0] LDAPOID,
882de962bdSlukem * requestValue [1] OCTET STRING OPTIONAL
892de962bdSlukem * }
902de962bdSlukem *
912de962bdSlukem * LDAPv3 Extended Operation Response
922de962bdSlukem * ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
932de962bdSlukem * COMPONENTS OF LDAPResult,
942de962bdSlukem * responseName [10] LDAPOID OPTIONAL,
952de962bdSlukem * response [11] OCTET STRING OPTIONAL
962de962bdSlukem * }
972de962bdSlukem *
982de962bdSlukem * (Source RFC 4511)
992de962bdSlukem */
1002de962bdSlukem
1012de962bdSlukem int
ldap_extended_operation(LDAP * ld,LDAP_CONST char * reqoid,struct berval * reqdata,LDAPControl ** sctrls,LDAPControl ** cctrls,int * msgidp)1022de962bdSlukem ldap_extended_operation(
1032de962bdSlukem LDAP *ld,
1042de962bdSlukem LDAP_CONST char *reqoid,
1052de962bdSlukem struct berval *reqdata,
1062de962bdSlukem LDAPControl **sctrls,
1072de962bdSlukem LDAPControl **cctrls,
1082de962bdSlukem int *msgidp )
1092de962bdSlukem {
1102de962bdSlukem BerElement *ber;
1112de962bdSlukem ber_int_t id;
1122de962bdSlukem
113*549b59edSchristos Debug0( LDAP_DEBUG_TRACE, "ldap_extended_operation\n" );
1142de962bdSlukem
1152de962bdSlukem assert( ld != NULL );
1162de962bdSlukem assert( LDAP_VALID( ld ) );
1172de962bdSlukem assert( reqoid != NULL && *reqoid != '\0' );
1182de962bdSlukem assert( msgidp != NULL );
1192de962bdSlukem
1202de962bdSlukem /* must be version 3 (or greater) */
1212de962bdSlukem if ( ld->ld_version < LDAP_VERSION3 ) {
1222de962bdSlukem ld->ld_errno = LDAP_NOT_SUPPORTED;
1232de962bdSlukem return( ld->ld_errno );
1242de962bdSlukem }
1252de962bdSlukem
126376af7d7Schristos ber = ldap_build_extended_req( ld, reqoid, reqdata,
127376af7d7Schristos sctrls, cctrls, &id );
128376af7d7Schristos if ( !ber )
1292de962bdSlukem return( ld->ld_errno );
1302de962bdSlukem
1312de962bdSlukem /* send the message */
1322de962bdSlukem *msgidp = ldap_send_initial_request( ld, LDAP_REQ_EXTENDED, NULL, ber, id );
1332de962bdSlukem
1342de962bdSlukem return( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS );
1352de962bdSlukem }
1362de962bdSlukem
1372de962bdSlukem int
ldap_extended_operation_s(LDAP * ld,LDAP_CONST char * reqoid,struct berval * reqdata,LDAPControl ** sctrls,LDAPControl ** cctrls,char ** retoidp,struct berval ** retdatap)1382de962bdSlukem ldap_extended_operation_s(
1392de962bdSlukem LDAP *ld,
1402de962bdSlukem LDAP_CONST char *reqoid,
1412de962bdSlukem struct berval *reqdata,
1422de962bdSlukem LDAPControl **sctrls,
1432de962bdSlukem LDAPControl **cctrls,
1442de962bdSlukem char **retoidp,
1452de962bdSlukem struct berval **retdatap )
1462de962bdSlukem {
1472de962bdSlukem int rc;
1482de962bdSlukem int msgid;
1492de962bdSlukem LDAPMessage *res;
1502de962bdSlukem
151*549b59edSchristos Debug0( LDAP_DEBUG_TRACE, "ldap_extended_operation_s\n" );
1522de962bdSlukem
1532de962bdSlukem assert( ld != NULL );
1542de962bdSlukem assert( LDAP_VALID( ld ) );
1552de962bdSlukem assert( reqoid != NULL && *reqoid != '\0' );
1562de962bdSlukem
1572de962bdSlukem rc = ldap_extended_operation( ld, reqoid, reqdata,
1582de962bdSlukem sctrls, cctrls, &msgid );
1592de962bdSlukem
1602de962bdSlukem if ( rc != LDAP_SUCCESS ) {
1612de962bdSlukem return( rc );
1622de962bdSlukem }
1632de962bdSlukem
1642de962bdSlukem if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) {
1652de962bdSlukem return( ld->ld_errno );
1662de962bdSlukem }
1672de962bdSlukem
1682de962bdSlukem if ( retoidp != NULL ) *retoidp = NULL;
1692de962bdSlukem if ( retdatap != NULL ) *retdatap = NULL;
1702de962bdSlukem
1712de962bdSlukem rc = ldap_parse_extended_result( ld, res, retoidp, retdatap, 0 );
1722de962bdSlukem
1732de962bdSlukem if( rc != LDAP_SUCCESS ) {
1742de962bdSlukem ldap_msgfree( res );
1752de962bdSlukem return rc;
1762de962bdSlukem }
1772de962bdSlukem
1782de962bdSlukem return( ldap_result2error( ld, res, 1 ) );
1792de962bdSlukem }
1802de962bdSlukem
1812de962bdSlukem /* Parse an extended result */
1822de962bdSlukem int
ldap_parse_extended_result(LDAP * ld,LDAPMessage * res,char ** retoidp,struct berval ** retdatap,int freeit)1832de962bdSlukem ldap_parse_extended_result (
1842de962bdSlukem LDAP *ld,
1852de962bdSlukem LDAPMessage *res,
1862de962bdSlukem char **retoidp,
1872de962bdSlukem struct berval **retdatap,
1882de962bdSlukem int freeit )
1892de962bdSlukem {
1902de962bdSlukem BerElement *ber;
1912de962bdSlukem ber_tag_t rc;
1922de962bdSlukem ber_tag_t tag;
1932de962bdSlukem ber_len_t len;
1942de962bdSlukem struct berval *resdata;
1952de962bdSlukem ber_int_t errcode;
1962de962bdSlukem char *resoid;
1972de962bdSlukem
1982de962bdSlukem assert( ld != NULL );
1992de962bdSlukem assert( LDAP_VALID( ld ) );
2002de962bdSlukem assert( res != NULL );
2012de962bdSlukem
202*549b59edSchristos Debug0( LDAP_DEBUG_TRACE, "ldap_parse_extended_result\n" );
2032de962bdSlukem
2042de962bdSlukem if( ld->ld_version < LDAP_VERSION3 ) {
2052de962bdSlukem ld->ld_errno = LDAP_NOT_SUPPORTED;
2062de962bdSlukem return ld->ld_errno;
2072de962bdSlukem }
2082de962bdSlukem
2092de962bdSlukem if( res->lm_msgtype != LDAP_RES_EXTENDED ) {
2102de962bdSlukem ld->ld_errno = LDAP_PARAM_ERROR;
2112de962bdSlukem return ld->ld_errno;
2122de962bdSlukem }
2132de962bdSlukem
2142de962bdSlukem if( retoidp != NULL ) *retoidp = NULL;
2152de962bdSlukem if( retdatap != NULL ) *retdatap = NULL;
2162de962bdSlukem
2172de962bdSlukem if ( ld->ld_error ) {
2182de962bdSlukem LDAP_FREE( ld->ld_error );
2192de962bdSlukem ld->ld_error = NULL;
2202de962bdSlukem }
2212de962bdSlukem
2222de962bdSlukem if ( ld->ld_matched ) {
2232de962bdSlukem LDAP_FREE( ld->ld_matched );
2242de962bdSlukem ld->ld_matched = NULL;
2252de962bdSlukem }
2262de962bdSlukem
2272de962bdSlukem ber = ber_dup( res->lm_ber );
2282de962bdSlukem
2292de962bdSlukem if ( ber == NULL ) {
2302de962bdSlukem ld->ld_errno = LDAP_NO_MEMORY;
2312de962bdSlukem return ld->ld_errno;
2322de962bdSlukem }
2332de962bdSlukem
2342de962bdSlukem rc = ber_scanf( ber, "{eAA" /*}*/, &errcode,
2352de962bdSlukem &ld->ld_matched, &ld->ld_error );
2362de962bdSlukem
2372de962bdSlukem if( rc == LBER_ERROR ) {
2382de962bdSlukem ld->ld_errno = LDAP_DECODING_ERROR;
2392de962bdSlukem ber_free( ber, 0 );
2402de962bdSlukem return ld->ld_errno;
2412de962bdSlukem }
2422de962bdSlukem
2432de962bdSlukem resoid = NULL;
2442de962bdSlukem resdata = NULL;
2452de962bdSlukem
2462de962bdSlukem tag = ber_peek_tag( ber, &len );
2472de962bdSlukem
2482de962bdSlukem if( tag == LDAP_TAG_REFERRAL ) {
2492de962bdSlukem /* skip over referral */
2502de962bdSlukem if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
2512de962bdSlukem ld->ld_errno = LDAP_DECODING_ERROR;
2522de962bdSlukem ber_free( ber, 0 );
2532de962bdSlukem return ld->ld_errno;
2542de962bdSlukem }
2552de962bdSlukem
2562de962bdSlukem tag = ber_peek_tag( ber, &len );
2572de962bdSlukem }
2582de962bdSlukem
2592de962bdSlukem if( tag == LDAP_TAG_EXOP_RES_OID ) {
2602de962bdSlukem /* we have a resoid */
2612de962bdSlukem if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) {
2622de962bdSlukem ld->ld_errno = LDAP_DECODING_ERROR;
2632de962bdSlukem ber_free( ber, 0 );
2642de962bdSlukem return ld->ld_errno;
2652de962bdSlukem }
2662de962bdSlukem
2672de962bdSlukem assert( resoid[ 0 ] != '\0' );
2682de962bdSlukem
2692de962bdSlukem tag = ber_peek_tag( ber, &len );
2702de962bdSlukem }
2712de962bdSlukem
2722de962bdSlukem if( tag == LDAP_TAG_EXOP_RES_VALUE ) {
2732de962bdSlukem /* we have a resdata */
2742de962bdSlukem if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) {
2752de962bdSlukem ld->ld_errno = LDAP_DECODING_ERROR;
2762de962bdSlukem ber_free( ber, 0 );
2772de962bdSlukem if( resoid != NULL ) LDAP_FREE( resoid );
2782de962bdSlukem return ld->ld_errno;
2792de962bdSlukem }
2802de962bdSlukem }
2812de962bdSlukem
2822de962bdSlukem ber_free( ber, 0 );
2832de962bdSlukem
2842de962bdSlukem if( retoidp != NULL ) {
2852de962bdSlukem *retoidp = resoid;
2862de962bdSlukem } else {
2872de962bdSlukem LDAP_FREE( resoid );
2882de962bdSlukem }
2892de962bdSlukem
2902de962bdSlukem if( retdatap != NULL ) {
2912de962bdSlukem *retdatap = resdata;
2922de962bdSlukem } else {
2932de962bdSlukem ber_bvfree( resdata );
2942de962bdSlukem }
2952de962bdSlukem
2962de962bdSlukem ld->ld_errno = errcode;
2972de962bdSlukem
2982de962bdSlukem if( freeit ) {
2992de962bdSlukem ldap_msgfree( res );
3002de962bdSlukem }
3012de962bdSlukem
3022de962bdSlukem return LDAP_SUCCESS;
3032de962bdSlukem }
3042de962bdSlukem
3052de962bdSlukem
3062de962bdSlukem /* Parse an extended partial */
3072de962bdSlukem int
ldap_parse_intermediate(LDAP * ld,LDAPMessage * res,char ** retoidp,struct berval ** retdatap,LDAPControl *** serverctrls,int freeit)3082de962bdSlukem ldap_parse_intermediate (
3092de962bdSlukem LDAP *ld,
3102de962bdSlukem LDAPMessage *res,
3112de962bdSlukem char **retoidp,
3122de962bdSlukem struct berval **retdatap,
3132de962bdSlukem LDAPControl ***serverctrls,
3142de962bdSlukem int freeit )
3152de962bdSlukem {
3162de962bdSlukem BerElement *ber;
3172de962bdSlukem ber_tag_t tag;
3182de962bdSlukem ber_len_t len;
3192de962bdSlukem struct berval *resdata;
3202de962bdSlukem char *resoid;
3212de962bdSlukem
3222de962bdSlukem assert( ld != NULL );
3232de962bdSlukem assert( LDAP_VALID( ld ) );
3242de962bdSlukem assert( res != NULL );
3252de962bdSlukem
326*549b59edSchristos Debug0( LDAP_DEBUG_TRACE, "ldap_parse_intermediate\n" );
3272de962bdSlukem
3282de962bdSlukem if( ld->ld_version < LDAP_VERSION3 ) {
3292de962bdSlukem ld->ld_errno = LDAP_NOT_SUPPORTED;
3302de962bdSlukem return ld->ld_errno;
3312de962bdSlukem }
3322de962bdSlukem
3332de962bdSlukem if( res->lm_msgtype != LDAP_RES_INTERMEDIATE ) {
3342de962bdSlukem ld->ld_errno = LDAP_PARAM_ERROR;
3352de962bdSlukem return ld->ld_errno;
3362de962bdSlukem }
3372de962bdSlukem
3382de962bdSlukem if( retoidp != NULL ) *retoidp = NULL;
3392de962bdSlukem if( retdatap != NULL ) *retdatap = NULL;
3402de962bdSlukem if( serverctrls != NULL ) *serverctrls = NULL;
3412de962bdSlukem
3422de962bdSlukem ber = ber_dup( res->lm_ber );
3432de962bdSlukem
3442de962bdSlukem if ( ber == NULL ) {
3452de962bdSlukem ld->ld_errno = LDAP_NO_MEMORY;
3462de962bdSlukem return ld->ld_errno;
3472de962bdSlukem }
3482de962bdSlukem
3492de962bdSlukem tag = ber_scanf( ber, "{" /*}*/ );
3502de962bdSlukem
3512de962bdSlukem if( tag == LBER_ERROR ) {
3522de962bdSlukem ld->ld_errno = LDAP_DECODING_ERROR;
3532de962bdSlukem ber_free( ber, 0 );
3542de962bdSlukem return ld->ld_errno;
3552de962bdSlukem }
3562de962bdSlukem
3572de962bdSlukem resoid = NULL;
3582de962bdSlukem resdata = NULL;
3592de962bdSlukem
3602de962bdSlukem tag = ber_peek_tag( ber, &len );
3612de962bdSlukem
3622de962bdSlukem /*
3632de962bdSlukem * NOTE: accept intermediate and extended response tag values
3642de962bdSlukem * as older versions of slapd(8) incorrectly used extended
3652de962bdSlukem * response tags.
3662de962bdSlukem * Should be removed when 2.2 is moved to Historic.
3672de962bdSlukem */
3682de962bdSlukem if( tag == LDAP_TAG_IM_RES_OID || tag == LDAP_TAG_EXOP_RES_OID ) {
3692de962bdSlukem /* we have a resoid */
3702de962bdSlukem if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) {
3712de962bdSlukem ld->ld_errno = LDAP_DECODING_ERROR;
3722de962bdSlukem ber_free( ber, 0 );
3732de962bdSlukem return ld->ld_errno;
3742de962bdSlukem }
3752de962bdSlukem
3762de962bdSlukem assert( resoid[ 0 ] != '\0' );
3772de962bdSlukem
3782de962bdSlukem tag = ber_peek_tag( ber, &len );
3792de962bdSlukem }
3802de962bdSlukem
3812de962bdSlukem if( tag == LDAP_TAG_IM_RES_VALUE || tag == LDAP_TAG_EXOP_RES_VALUE ) {
3822de962bdSlukem /* we have a resdata */
3832de962bdSlukem if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) {
3842de962bdSlukem ld->ld_errno = LDAP_DECODING_ERROR;
3852de962bdSlukem ber_free( ber, 0 );
3862de962bdSlukem if( resoid != NULL ) LDAP_FREE( resoid );
3872de962bdSlukem return ld->ld_errno;
3882de962bdSlukem }
3892de962bdSlukem }
3902de962bdSlukem
3912de962bdSlukem if ( serverctrls == NULL ) {
3922de962bdSlukem ld->ld_errno = LDAP_SUCCESS;
3932de962bdSlukem goto free_and_return;
3942de962bdSlukem }
3952de962bdSlukem
3962de962bdSlukem if ( ber_scanf( ber, /*{*/ "}" ) == LBER_ERROR ) {
3972de962bdSlukem ld->ld_errno = LDAP_DECODING_ERROR;
3982de962bdSlukem goto free_and_return;
3992de962bdSlukem }
4002de962bdSlukem
4012de962bdSlukem ld->ld_errno = ldap_pvt_get_controls( ber, serverctrls );
4022de962bdSlukem
4032de962bdSlukem free_and_return:
4042de962bdSlukem ber_free( ber, 0 );
4052de962bdSlukem
4062de962bdSlukem if( retoidp != NULL ) {
4072de962bdSlukem *retoidp = resoid;
4082de962bdSlukem } else {
4092de962bdSlukem LDAP_FREE( resoid );
4102de962bdSlukem }
4112de962bdSlukem
4122de962bdSlukem if( retdatap != NULL ) {
4132de962bdSlukem *retdatap = resdata;
4142de962bdSlukem } else {
4152de962bdSlukem ber_bvfree( resdata );
4162de962bdSlukem }
4172de962bdSlukem
4182de962bdSlukem if( freeit ) {
4192de962bdSlukem ldap_msgfree( res );
4202de962bdSlukem }
4212de962bdSlukem
4222de962bdSlukem return ld->ld_errno;
4232de962bdSlukem }
4242de962bdSlukem
425