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