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