xref: /netbsd-src/external/bsd/openldap/dist/libraries/libldap/extended.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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 
33 BerElement *
ldap_build_extended_req(LDAP * ld,LDAP_CONST char * reqoid,struct berval * reqdata,LDAPControl ** sctrls,LDAPControl ** cctrls,ber_int_t * msgidp)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
ldap_extended_operation(LDAP * ld,LDAP_CONST char * reqoid,struct berval * reqdata,LDAPControl ** sctrls,LDAPControl ** cctrls,int * msgidp)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
ldap_extended_operation_s(LDAP * ld,LDAP_CONST char * reqoid,struct berval * reqdata,LDAPControl ** sctrls,LDAPControl ** cctrls,char ** retoidp,struct berval ** retdatap)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
ldap_parse_extended_result(LDAP * ld,LDAPMessage * res,char ** retoidp,struct berval ** retdatap,int freeit)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
ldap_parse_intermediate(LDAP * ld,LDAPMessage * res,char ** retoidp,struct berval ** retdatap,LDAPControl *** serverctrls,int freeit)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