xref: /netbsd-src/external/bsd/openldap/dist/libraries/libldap/extended.c (revision 376af7d777b42eee10027dba46d5ea44d5db14e7)
1 /*	$NetBSD: extended.c,v 1.1.1.5 2017/02/09 01:46:46 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1998-2016 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.1.1.5 2017/02/09 01:46:46 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 	int rc;
112 	ber_int_t id;
113 
114 	Debug( LDAP_DEBUG_TRACE, "ldap_extended_operation\n", 0, 0, 0 );
115 
116 	assert( ld != NULL );
117 	assert( LDAP_VALID( ld ) );
118 	assert( reqoid != NULL && *reqoid != '\0' );
119 	assert( msgidp != NULL );
120 
121 	/* must be version 3 (or greater) */
122 	if ( ld->ld_version < LDAP_VERSION3 ) {
123 		ld->ld_errno = LDAP_NOT_SUPPORTED;
124 		return( ld->ld_errno );
125 	}
126 
127 	ber = ldap_build_extended_req( ld, reqoid, reqdata,
128 		sctrls, cctrls, &id );
129 	if ( !ber )
130 		return( ld->ld_errno );
131 
132 	/* send the message */
133 	*msgidp = ldap_send_initial_request( ld, LDAP_REQ_EXTENDED, NULL, ber, id );
134 
135 	return( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS );
136 }
137 
138 int
139 ldap_extended_operation_s(
140 	LDAP			*ld,
141 	LDAP_CONST char	*reqoid,
142 	struct berval	*reqdata,
143 	LDAPControl		**sctrls,
144 	LDAPControl		**cctrls,
145 	char			**retoidp,
146 	struct berval	**retdatap )
147 {
148     int     rc;
149     int     msgid;
150     LDAPMessage *res;
151 
152 	Debug( LDAP_DEBUG_TRACE, "ldap_extended_operation_s\n", 0, 0, 0 );
153 
154 	assert( ld != NULL );
155 	assert( LDAP_VALID( ld ) );
156 	assert( reqoid != NULL && *reqoid != '\0' );
157 
158     rc = ldap_extended_operation( ld, reqoid, reqdata,
159 		sctrls, cctrls, &msgid );
160 
161     if ( rc != LDAP_SUCCESS ) {
162         return( rc );
163 	}
164 
165     if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) {
166         return( ld->ld_errno );
167 	}
168 
169 	if ( retoidp != NULL ) *retoidp = NULL;
170 	if ( retdatap != NULL ) *retdatap = NULL;
171 
172 	rc = ldap_parse_extended_result( ld, res, retoidp, retdatap, 0 );
173 
174 	if( rc != LDAP_SUCCESS ) {
175 		ldap_msgfree( res );
176 		return rc;
177 	}
178 
179     return( ldap_result2error( ld, res, 1 ) );
180 }
181 
182 /* Parse an extended result */
183 int
184 ldap_parse_extended_result (
185 	LDAP			*ld,
186 	LDAPMessage		*res,
187 	char			**retoidp,
188 	struct berval	**retdatap,
189 	int				freeit )
190 {
191 	BerElement *ber;
192 	ber_tag_t rc;
193 	ber_tag_t tag;
194 	ber_len_t len;
195 	struct berval *resdata;
196 	ber_int_t errcode;
197 	char *resoid;
198 
199 	assert( ld != NULL );
200 	assert( LDAP_VALID( ld ) );
201 	assert( res != NULL );
202 
203 	Debug( LDAP_DEBUG_TRACE, "ldap_parse_extended_result\n", 0, 0, 0 );
204 
205 	if( ld->ld_version < LDAP_VERSION3 ) {
206 		ld->ld_errno = LDAP_NOT_SUPPORTED;
207 		return ld->ld_errno;
208 	}
209 
210 	if( res->lm_msgtype != LDAP_RES_EXTENDED ) {
211 		ld->ld_errno = LDAP_PARAM_ERROR;
212 		return ld->ld_errno;
213 	}
214 
215 	if( retoidp != NULL ) *retoidp = NULL;
216 	if( retdatap != NULL ) *retdatap = NULL;
217 
218 	if ( ld->ld_error ) {
219 		LDAP_FREE( ld->ld_error );
220 		ld->ld_error = NULL;
221 	}
222 
223 	if ( ld->ld_matched ) {
224 		LDAP_FREE( ld->ld_matched );
225 		ld->ld_matched = NULL;
226 	}
227 
228 	ber = ber_dup( res->lm_ber );
229 
230 	if ( ber == NULL ) {
231 		ld->ld_errno = LDAP_NO_MEMORY;
232 		return ld->ld_errno;
233 	}
234 
235 	rc = ber_scanf( ber, "{eAA" /*}*/, &errcode,
236 		&ld->ld_matched, &ld->ld_error );
237 
238 	if( rc == LBER_ERROR ) {
239 		ld->ld_errno = LDAP_DECODING_ERROR;
240 		ber_free( ber, 0 );
241 		return ld->ld_errno;
242 	}
243 
244 	resoid = NULL;
245 	resdata = NULL;
246 
247 	tag = ber_peek_tag( ber, &len );
248 
249 	if( tag == LDAP_TAG_REFERRAL ) {
250 		/* skip over referral */
251 		if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
252 			ld->ld_errno = LDAP_DECODING_ERROR;
253 			ber_free( ber, 0 );
254 			return ld->ld_errno;
255 		}
256 
257 		tag = ber_peek_tag( ber, &len );
258 	}
259 
260 	if( tag == LDAP_TAG_EXOP_RES_OID ) {
261 		/* we have a resoid */
262 		if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) {
263 			ld->ld_errno = LDAP_DECODING_ERROR;
264 			ber_free( ber, 0 );
265 			return ld->ld_errno;
266 		}
267 
268 		assert( resoid[ 0 ] != '\0' );
269 
270 		tag = ber_peek_tag( ber, &len );
271 	}
272 
273 	if( tag == LDAP_TAG_EXOP_RES_VALUE ) {
274 		/* we have a resdata */
275 		if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) {
276 			ld->ld_errno = LDAP_DECODING_ERROR;
277 			ber_free( ber, 0 );
278 			if( resoid != NULL ) LDAP_FREE( resoid );
279 			return ld->ld_errno;
280 		}
281 	}
282 
283 	ber_free( ber, 0 );
284 
285 	if( retoidp != NULL ) {
286 		*retoidp = resoid;
287 	} else {
288 		LDAP_FREE( resoid );
289 	}
290 
291 	if( retdatap != NULL ) {
292 		*retdatap = resdata;
293 	} else {
294 		ber_bvfree( resdata );
295 	}
296 
297 	ld->ld_errno = errcode;
298 
299 	if( freeit ) {
300 		ldap_msgfree( res );
301 	}
302 
303 	return LDAP_SUCCESS;
304 }
305 
306 
307 /* Parse an extended partial */
308 int
309 ldap_parse_intermediate (
310 	LDAP			*ld,
311 	LDAPMessage		*res,
312 	char			**retoidp,
313 	struct berval	**retdatap,
314 	LDAPControl		***serverctrls,
315 	int				freeit )
316 {
317 	BerElement *ber;
318 	ber_tag_t tag;
319 	ber_len_t len;
320 	struct berval *resdata;
321 	char *resoid;
322 
323 	assert( ld != NULL );
324 	assert( LDAP_VALID( ld ) );
325 	assert( res != NULL );
326 
327 	Debug( LDAP_DEBUG_TRACE, "ldap_parse_intermediate\n", 0, 0, 0 );
328 
329 	if( ld->ld_version < LDAP_VERSION3 ) {
330 		ld->ld_errno = LDAP_NOT_SUPPORTED;
331 		return ld->ld_errno;
332 	}
333 
334 	if( res->lm_msgtype != LDAP_RES_INTERMEDIATE ) {
335 		ld->ld_errno = LDAP_PARAM_ERROR;
336 		return ld->ld_errno;
337 	}
338 
339 	if( retoidp != NULL ) *retoidp = NULL;
340 	if( retdatap != NULL ) *retdatap = NULL;
341 	if( serverctrls != NULL ) *serverctrls = NULL;
342 
343 	ber = ber_dup( res->lm_ber );
344 
345 	if ( ber == NULL ) {
346 		ld->ld_errno = LDAP_NO_MEMORY;
347 		return ld->ld_errno;
348 	}
349 
350 	tag = ber_scanf( ber, "{" /*}*/ );
351 
352 	if( tag == LBER_ERROR ) {
353 		ld->ld_errno = LDAP_DECODING_ERROR;
354 		ber_free( ber, 0 );
355 		return ld->ld_errno;
356 	}
357 
358 	resoid = NULL;
359 	resdata = NULL;
360 
361 	tag = ber_peek_tag( ber, &len );
362 
363 	/*
364 	 * NOTE: accept intermediate and extended response tag values
365 	 * as older versions of slapd(8) incorrectly used extended
366 	 * response tags.
367 	 * Should be removed when 2.2 is moved to Historic.
368 	 */
369 	if( tag == LDAP_TAG_IM_RES_OID || tag == LDAP_TAG_EXOP_RES_OID ) {
370 		/* we have a resoid */
371 		if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) {
372 			ld->ld_errno = LDAP_DECODING_ERROR;
373 			ber_free( ber, 0 );
374 			return ld->ld_errno;
375 		}
376 
377 		assert( resoid[ 0 ] != '\0' );
378 
379 		tag = ber_peek_tag( ber, &len );
380 	}
381 
382 	if( tag == LDAP_TAG_IM_RES_VALUE || tag == LDAP_TAG_EXOP_RES_VALUE ) {
383 		/* we have a resdata */
384 		if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) {
385 			ld->ld_errno = LDAP_DECODING_ERROR;
386 			ber_free( ber, 0 );
387 			if( resoid != NULL ) LDAP_FREE( resoid );
388 			return ld->ld_errno;
389 		}
390 	}
391 
392 	if ( serverctrls == NULL ) {
393 		ld->ld_errno = LDAP_SUCCESS;
394 		goto free_and_return;
395 	}
396 
397 	if ( ber_scanf( ber, /*{*/ "}" ) == LBER_ERROR ) {
398 		ld->ld_errno = LDAP_DECODING_ERROR;
399 		goto free_and_return;
400 	}
401 
402 	ld->ld_errno = ldap_pvt_get_controls( ber, serverctrls );
403 
404 free_and_return:
405 	ber_free( ber, 0 );
406 
407 	if( retoidp != NULL ) {
408 		*retoidp = resoid;
409 	} else {
410 		LDAP_FREE( resoid );
411 	}
412 
413 	if( retdatap != NULL ) {
414 		*retdatap = resdata;
415 	} else {
416 		ber_bvfree( resdata );
417 	}
418 
419 	if( freeit ) {
420 		ldap_msgfree( res );
421 	}
422 
423 	return ld->ld_errno;
424 }
425 
426