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