1/*	$NetBSD: extended.c,v 1.1.1.3 2010/12/12 15:21:31 adam Exp $	*/
2
3/* OpenLDAP: pkg/ldap/libraries/libldap/extended.c,v 1.39.2.6 2010/04/13 20:22:56 kurt Exp */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1998-2010 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 "portable.h"
19
20#include <stdio.h>
21#include <ac/stdlib.h>
22
23#include <ac/socket.h>
24#include <ac/string.h>
25#include <ac/time.h>
26
27#include "ldap-int.h"
28#include "ldap_log.h"
29
30/*
31 * LDAPv3 Extended Operation Request
32 *	ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
33 *		requestName      [0] LDAPOID,
34 *		requestValue     [1] OCTET STRING OPTIONAL
35 *	}
36 *
37 * LDAPv3 Extended Operation Response
38 *	ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
39 *		COMPONENTS OF LDAPResult,
40 *		responseName     [10] LDAPOID OPTIONAL,
41 *		response         [11] OCTET STRING OPTIONAL
42 *	}
43 *
44 * (Source RFC 4511)
45 */
46
47int
48ldap_extended_operation(
49	LDAP			*ld,
50	LDAP_CONST char	*reqoid,
51	struct berval	*reqdata,
52	LDAPControl		**sctrls,
53	LDAPControl		**cctrls,
54	int				*msgidp )
55{
56	BerElement *ber;
57	int rc;
58	ber_int_t id;
59
60	Debug( LDAP_DEBUG_TRACE, "ldap_extended_operation\n", 0, 0, 0 );
61
62	assert( ld != NULL );
63	assert( LDAP_VALID( ld ) );
64	assert( reqoid != NULL && *reqoid != '\0' );
65	assert( msgidp != NULL );
66
67	/* must be version 3 (or greater) */
68	if ( ld->ld_version < LDAP_VERSION3 ) {
69		ld->ld_errno = LDAP_NOT_SUPPORTED;
70		return( ld->ld_errno );
71	}
72
73	/* create a message to send */
74	if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
75		ld->ld_errno = LDAP_NO_MEMORY;
76		return( ld->ld_errno );
77	}
78
79	LDAP_NEXT_MSGID( ld, id );
80	if ( reqdata != NULL ) {
81		rc = ber_printf( ber, "{it{tstON}", /* '}' */
82			id, LDAP_REQ_EXTENDED,
83			LDAP_TAG_EXOP_REQ_OID, reqoid,
84			LDAP_TAG_EXOP_REQ_VALUE, reqdata );
85
86	} else {
87		rc = ber_printf( ber, "{it{tsN}", /* '}' */
88			id, LDAP_REQ_EXTENDED,
89			LDAP_TAG_EXOP_REQ_OID, reqoid );
90	}
91
92	if( rc == -1 ) {
93		ld->ld_errno = LDAP_ENCODING_ERROR;
94		ber_free( ber, 1 );
95		return( ld->ld_errno );
96	}
97
98	/* Put Server Controls */
99	if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
100		ber_free( ber, 1 );
101		return ld->ld_errno;
102	}
103
104	if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
105		ld->ld_errno = LDAP_ENCODING_ERROR;
106		ber_free( ber, 1 );
107		return( ld->ld_errno );
108	}
109
110	/* send the message */
111	*msgidp = ldap_send_initial_request( ld, LDAP_REQ_EXTENDED, NULL, ber, id );
112
113	return( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS );
114}
115
116int
117ldap_extended_operation_s(
118	LDAP			*ld,
119	LDAP_CONST char	*reqoid,
120	struct berval	*reqdata,
121	LDAPControl		**sctrls,
122	LDAPControl		**cctrls,
123	char			**retoidp,
124	struct berval	**retdatap )
125{
126    int     rc;
127    int     msgid;
128    LDAPMessage *res;
129
130	Debug( LDAP_DEBUG_TRACE, "ldap_extended_operation_s\n", 0, 0, 0 );
131
132	assert( ld != NULL );
133	assert( LDAP_VALID( ld ) );
134	assert( reqoid != NULL && *reqoid != '\0' );
135
136    rc = ldap_extended_operation( ld, reqoid, reqdata,
137		sctrls, cctrls, &msgid );
138
139    if ( rc != LDAP_SUCCESS ) {
140        return( rc );
141	}
142
143    if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) {
144        return( ld->ld_errno );
145	}
146
147	if ( retoidp != NULL ) *retoidp = NULL;
148	if ( retdatap != NULL ) *retdatap = NULL;
149
150	rc = ldap_parse_extended_result( ld, res, retoidp, retdatap, 0 );
151
152	if( rc != LDAP_SUCCESS ) {
153		ldap_msgfree( res );
154		return rc;
155	}
156
157    return( ldap_result2error( ld, res, 1 ) );
158}
159
160/* Parse an extended result */
161int
162ldap_parse_extended_result (
163	LDAP			*ld,
164	LDAPMessage		*res,
165	char			**retoidp,
166	struct berval	**retdatap,
167	int				freeit )
168{
169	BerElement *ber;
170	ber_tag_t rc;
171	ber_tag_t tag;
172	ber_len_t len;
173	struct berval *resdata;
174	ber_int_t errcode;
175	char *resoid;
176
177	assert( ld != NULL );
178	assert( LDAP_VALID( ld ) );
179	assert( res != NULL );
180
181	Debug( LDAP_DEBUG_TRACE, "ldap_parse_extended_result\n", 0, 0, 0 );
182
183	if( ld->ld_version < LDAP_VERSION3 ) {
184		ld->ld_errno = LDAP_NOT_SUPPORTED;
185		return ld->ld_errno;
186	}
187
188	if( res->lm_msgtype != LDAP_RES_EXTENDED ) {
189		ld->ld_errno = LDAP_PARAM_ERROR;
190		return ld->ld_errno;
191	}
192
193	if( retoidp != NULL ) *retoidp = NULL;
194	if( retdatap != NULL ) *retdatap = NULL;
195
196	if ( ld->ld_error ) {
197		LDAP_FREE( ld->ld_error );
198		ld->ld_error = NULL;
199	}
200
201	if ( ld->ld_matched ) {
202		LDAP_FREE( ld->ld_matched );
203		ld->ld_matched = NULL;
204	}
205
206	ber = ber_dup( res->lm_ber );
207
208	if ( ber == NULL ) {
209		ld->ld_errno = LDAP_NO_MEMORY;
210		return ld->ld_errno;
211	}
212
213	rc = ber_scanf( ber, "{eAA" /*}*/, &errcode,
214		&ld->ld_matched, &ld->ld_error );
215
216	if( rc == LBER_ERROR ) {
217		ld->ld_errno = LDAP_DECODING_ERROR;
218		ber_free( ber, 0 );
219		return ld->ld_errno;
220	}
221
222	resoid = NULL;
223	resdata = NULL;
224
225	tag = ber_peek_tag( ber, &len );
226
227	if( tag == LDAP_TAG_REFERRAL ) {
228		/* skip over referral */
229		if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
230			ld->ld_errno = LDAP_DECODING_ERROR;
231			ber_free( ber, 0 );
232			return ld->ld_errno;
233		}
234
235		tag = ber_peek_tag( ber, &len );
236	}
237
238	if( tag == LDAP_TAG_EXOP_RES_OID ) {
239		/* we have a resoid */
240		if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) {
241			ld->ld_errno = LDAP_DECODING_ERROR;
242			ber_free( ber, 0 );
243			return ld->ld_errno;
244		}
245
246		assert( resoid[ 0 ] != '\0' );
247
248		tag = ber_peek_tag( ber, &len );
249	}
250
251	if( tag == LDAP_TAG_EXOP_RES_VALUE ) {
252		/* we have a resdata */
253		if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) {
254			ld->ld_errno = LDAP_DECODING_ERROR;
255			ber_free( ber, 0 );
256			if( resoid != NULL ) LDAP_FREE( resoid );
257			return ld->ld_errno;
258		}
259	}
260
261	ber_free( ber, 0 );
262
263	if( retoidp != NULL ) {
264		*retoidp = resoid;
265	} else {
266		LDAP_FREE( resoid );
267	}
268
269	if( retdatap != NULL ) {
270		*retdatap = resdata;
271	} else {
272		ber_bvfree( resdata );
273	}
274
275	ld->ld_errno = errcode;
276
277	if( freeit ) {
278		ldap_msgfree( res );
279	}
280
281	return LDAP_SUCCESS;
282}
283
284
285/* Parse an extended partial */
286int
287ldap_parse_intermediate (
288	LDAP			*ld,
289	LDAPMessage		*res,
290	char			**retoidp,
291	struct berval	**retdatap,
292	LDAPControl		***serverctrls,
293	int				freeit )
294{
295	BerElement *ber;
296	ber_tag_t tag;
297	ber_len_t len;
298	struct berval *resdata;
299	char *resoid;
300
301	assert( ld != NULL );
302	assert( LDAP_VALID( ld ) );
303	assert( res != NULL );
304
305	Debug( LDAP_DEBUG_TRACE, "ldap_parse_intermediate\n", 0, 0, 0 );
306
307	if( ld->ld_version < LDAP_VERSION3 ) {
308		ld->ld_errno = LDAP_NOT_SUPPORTED;
309		return ld->ld_errno;
310	}
311
312	if( res->lm_msgtype != LDAP_RES_INTERMEDIATE ) {
313		ld->ld_errno = LDAP_PARAM_ERROR;
314		return ld->ld_errno;
315	}
316
317	if( retoidp != NULL ) *retoidp = NULL;
318	if( retdatap != NULL ) *retdatap = NULL;
319	if( serverctrls != NULL ) *serverctrls = NULL;
320
321	ber = ber_dup( res->lm_ber );
322
323	if ( ber == NULL ) {
324		ld->ld_errno = LDAP_NO_MEMORY;
325		return ld->ld_errno;
326	}
327
328	tag = ber_scanf( ber, "{" /*}*/ );
329
330	if( tag == LBER_ERROR ) {
331		ld->ld_errno = LDAP_DECODING_ERROR;
332		ber_free( ber, 0 );
333		return ld->ld_errno;
334	}
335
336	resoid = NULL;
337	resdata = NULL;
338
339	tag = ber_peek_tag( ber, &len );
340
341	/*
342	 * NOTE: accept intermediate and extended response tag values
343	 * as older versions of slapd(8) incorrectly used extended
344	 * response tags.
345	 * Should be removed when 2.2 is moved to Historic.
346	 */
347	if( tag == LDAP_TAG_IM_RES_OID || tag == LDAP_TAG_EXOP_RES_OID ) {
348		/* we have a resoid */
349		if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) {
350			ld->ld_errno = LDAP_DECODING_ERROR;
351			ber_free( ber, 0 );
352			return ld->ld_errno;
353		}
354
355		assert( resoid[ 0 ] != '\0' );
356
357		tag = ber_peek_tag( ber, &len );
358	}
359
360	if( tag == LDAP_TAG_IM_RES_VALUE || tag == LDAP_TAG_EXOP_RES_VALUE ) {
361		/* we have a resdata */
362		if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) {
363			ld->ld_errno = LDAP_DECODING_ERROR;
364			ber_free( ber, 0 );
365			if( resoid != NULL ) LDAP_FREE( resoid );
366			return ld->ld_errno;
367		}
368	}
369
370	if ( serverctrls == NULL ) {
371		ld->ld_errno = LDAP_SUCCESS;
372		goto free_and_return;
373	}
374
375	if ( ber_scanf( ber, /*{*/ "}" ) == LBER_ERROR ) {
376		ld->ld_errno = LDAP_DECODING_ERROR;
377		goto free_and_return;
378	}
379
380	ld->ld_errno = ldap_pvt_get_controls( ber, serverctrls );
381
382free_and_return:
383	ber_free( ber, 0 );
384
385	if( retoidp != NULL ) {
386		*retoidp = resoid;
387	} else {
388		LDAP_FREE( resoid );
389	}
390
391	if( retdatap != NULL ) {
392		*retdatap = resdata;
393	} else {
394		ber_bvfree( resdata );
395	}
396
397	if( freeit ) {
398		ldap_msgfree( res );
399	}
400
401	return ld->ld_errno;
402}
403
404