xref: /illumos-gate/usr/src/lib/libldap5/sources/ldap/common/control.c (revision 42b53e0fbc5c05289c3d334bb864b784fafe5ce4)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * The contents of this file are subject to the Netscape Public License
4  * Version 1.0 (the "NPL"); you may not use this file except in
5  * compliance with the NPL.  You may obtain a copy of the NPL at
6  * http://www.mozilla.org/NPL/
7  *
8  * Software distributed under the NPL is distributed on an "AS IS" basis,
9  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
10  * for the specific language governing rights and limitations under the
11  * NPL.
12  *
13  * The Initial Developer of this code under the NPL is Netscape
14  * Communications Corporation.  Portions created by Netscape are
15  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
16  * Reserved.
17  */
18 /* control.c - routines to handle ldapv3 controls */
19 
20 #include "ldap-int.h"
21 
22 static LDAPControl *ldap_control_dup( LDAPControl *ctrl );
23 static int ldap_control_copy_contents( LDAPControl *ctrl_dst,
24     LDAPControl *ctrl_src );
25 
26 /*
27  * Append a list of LDAPv3 controls to ber.  If ctrls is NULL, use default
28  * set of controls from ld.
29  * Return an LDAP error code (LDAP_SUCCESS if all goes well).
30  * If closeseq is non-zero, we do an extra ber_put_seq() as well.
31  */
32 int
33 nsldapi_put_controls( LDAP *ld, LDAPControl **ctrls, int closeseq,
34     BerElement *ber )
35 {
36 	LDAPControl	*c;
37 	int		rc, i;
38 
39 	rc = LDAP_ENCODING_ERROR;	/* the most popular error */
40 
41 	/* if no controls were passed in, use global list from LDAP * */
42 	LDAP_MUTEX_LOCK( ld, LDAP_CTRL_LOCK );
43 	if ( ctrls == NULL ) {
44 		ctrls = ld->ld_servercontrols;
45 	}
46 
47 	/* if there are no controls then we are done */
48 	if ( ctrls == NULL || ctrls[ 0 ] == NULL ) {
49 		goto clean_exit;
50 	}
51 
52 	/*
53 	 * If we're using LDAPv2 or earlier we can't send any controls, so
54 	 * we just ignore them unless one is marked critical, in which case
55 	 * we return an error.
56 	 */
57 	if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) {
58 		for ( i = 0; ctrls != NULL && ctrls[i] != NULL; i++ ) {
59 			if ( ctrls[i]->ldctl_iscritical ) {
60 				rc = LDAP_NOT_SUPPORTED;
61 				goto error_exit;
62 			}
63 		}
64 		goto clean_exit;
65 	}
66 
67 	/*
68 	 * encode the controls as a Sequence of Sequence
69 	 */
70 	if ( ber_printf( ber, "t{", LDAP_TAG_CONTROLS ) == -1 ) {
71 		goto error_exit;
72 	}
73 
74 	for ( i = 0; ctrls[i] != NULL; i++ ) {
75 		c = ctrls[i];
76 
77 		if ( ber_printf( ber, "{s", c->ldctl_oid ) == -1 ) {
78 			goto error_exit;
79 		}
80 
81 		/* criticality is "BOOLEAN DEFAULT FALSE" */
82 		/* therefore, it should only be encoded if it exists AND is TRUE */
83 		if ( c->ldctl_iscritical ) {
84 			if ( ber_printf( ber, "b", (int)c->ldctl_iscritical )
85 			    == -1 ) {
86 				goto error_exit;
87 			}
88 		}
89 
90 		if ( c->ldctl_value.bv_val != NULL ) {
91 			if ( ber_printf( ber, "o", c->ldctl_value.bv_val,
92 			    (int)c->ldctl_value.bv_len /* XXX lossy cast */ )
93 			    == -1 ) {
94 				goto error_exit;
95 			}
96 		}
97 
98 		if ( ber_put_seq( ber ) == -1 ) {
99 			goto error_exit;
100 		}
101 	}
102 
103 	if ( ber_put_seq( ber ) == -1 ) {
104 		goto error_exit;
105 	}
106 
107 clean_exit:
108 	LDAP_MUTEX_UNLOCK( ld, LDAP_CTRL_LOCK );
109 	if ( closeseq && ber_put_seq( ber ) == -1 ) {
110 		goto error_exit;
111 	}
112 	return( LDAP_SUCCESS );
113 
114 error_exit:
115 	LDAP_MUTEX_UNLOCK( ld, LDAP_CTRL_LOCK );
116 	LDAP_SET_LDERRNO( ld, rc, NULL, NULL );
117 	return( rc );
118 }
119 
120 
121 /*
122  * Pull controls out of "ber" (if any present) and return them in "controlsp."
123  * Returns an LDAP error code.
124  */
125 int
126 nsldapi_get_controls( BerElement *ber, LDAPControl ***controlsp )
127 {
128 	LDAPControl		*newctrl;
129 	ber_tag_t		tag;
130 	ber_len_t		len;
131 	int			rc, maxcontrols, curcontrols;
132 	char			*last;
133 
134 	/*
135 	 * Each LDAPMessage can have a set of controls appended
136 	 * to it. Controls are used to extend the functionality
137 	 * of an LDAP operation (e.g., add an attribute size limit
138 	 * to the search operation). These controls look like this:
139 	 *
140 	 *	Controls ::= SEQUENCE OF Control
141 	 *
142 	 *	Control ::= SEQUENCE {
143 	 *		controlType	LDAPOID,
144 	 *		criticality	BOOLEAN DEFAULT FALSE,
145 	 *		controlValue	OCTET STRING
146 	 *	}
147 	 */
148 	LDAPDebug( LDAP_DEBUG_TRACE, "=> nsldapi_get_controls\n", 0, 0, 0 );
149 
150 	*controlsp = NULL;
151 
152 	/*
153          * check to see if controls were included
154 	 */
155 	if ( ber_get_option( ber, LBER_OPT_REMAINING_BYTES, &len ) != 0 ) {
156 		return( LDAP_DECODING_ERROR );	/* unexpected error */
157 	}
158 	if ( len == 0 ) {
159 		LDAPDebug( LDAP_DEBUG_TRACE,
160 		    "<= nsldapi_get_controls no controls\n", 0, 0, 0 );
161 		return( LDAP_SUCCESS );			/* no controls */
162 	}
163 	if (( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) {
164 		if ( tag == LBER_ERROR ) {
165 			LDAPDebug( LDAP_DEBUG_TRACE,
166 			    "<= nsldapi_get_controls LDAP_PROTOCOL_ERROR\n",
167 			    0, 0, 0 );
168 			return( LDAP_DECODING_ERROR );	/* decoding error */
169 		}
170 		/*
171 		 * We found something other than controls.  This should never
172 		 * happen in LDAPv3, but we don't treat this is a hard error --
173 		 * we just ignore the extra stuff.
174 		 */
175 		LDAPDebug( LDAP_DEBUG_TRACE,
176 		    "<= nsldapi_get_controls ignoring unrecognized data in message (tag 0x%x)\n",
177 		    tag, 0, 0 );
178 		return( LDAP_SUCCESS );
179 	}
180 
181 	maxcontrols = curcontrols = 0;
182 	for ( tag = ber_first_element( ber, &len, &last );
183 	    tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET;
184 	    tag = ber_next_element( ber, &len, last ) ) {
185 		if ( curcontrols >= maxcontrols - 1 ) {
186 #define CONTROL_GRABSIZE	5
187 			maxcontrols += CONTROL_GRABSIZE;
188 			*controlsp = (struct ldapcontrol **)NSLDAPI_REALLOC(
189 			    (char *)*controlsp, maxcontrols *
190 			    sizeof(struct ldapcontrol *) );
191 			if ( *controlsp == NULL ) {
192 			    rc = LDAP_NO_MEMORY;
193 			    goto free_and_return;
194 			}
195 		}
196 		if (( newctrl = (struct ldapcontrol *)NSLDAPI_CALLOC( 1,
197 		    sizeof(LDAPControl))) == NULL ) {
198 			rc = LDAP_NO_MEMORY;
199 			goto free_and_return;
200 		}
201 
202 		(*controlsp)[curcontrols++] = newctrl;
203 		(*controlsp)[curcontrols] = NULL;
204 
205 		if ( ber_scanf( ber, "{a", &newctrl->ldctl_oid )
206 		    == LBER_ERROR ) {
207 			rc = LDAP_DECODING_ERROR;
208 			goto free_and_return;
209 		}
210 
211 		/* the criticality is optional */
212 		if ( ber_peek_tag( ber, &len ) == LBER_BOOLEAN ) {
213 			int		aint;
214 
215 			if ( ber_scanf( ber, "b", &aint ) == LBER_ERROR ) {
216 				rc = LDAP_DECODING_ERROR;
217 				goto free_and_return;
218 			}
219 			newctrl->ldctl_iscritical = (char)aint;	/* XXX lossy cast */
220 		} else {
221 			/* absent is synonomous with FALSE */
222 			newctrl->ldctl_iscritical = 0;
223 		}
224 
225 		/* the control value is optional */
226 		if ( ber_peek_tag( ber, &len ) == LBER_OCTETSTRING ) {
227 			if ( ber_scanf( ber, "o", &newctrl->ldctl_value )
228 			    == LBER_ERROR ) {
229 				rc = LDAP_DECODING_ERROR;
230 				goto free_and_return;
231 			}
232 		} else {
233 			(newctrl->ldctl_value).bv_val = NULL;
234 			(newctrl->ldctl_value).bv_len = 0;
235 		}
236 
237 	}
238 
239 	if ( tag == LBER_ERROR ) {
240 		rc = LDAP_DECODING_ERROR;
241 		goto free_and_return;
242 	}
243 
244 	LDAPDebug( LDAP_DEBUG_TRACE,
245 	    "<= nsldapi_get_controls found %d controls\n", curcontrols, 0, 0 );
246 	return( LDAP_SUCCESS );
247 
248 free_and_return:;
249 	ldap_controls_free( *controlsp );
250 	*controlsp = NULL;
251 	LDAPDebug( LDAP_DEBUG_TRACE,
252 	    "<= nsldapi_get_controls error 0x%x\n", rc, 0, 0 );
253 	return( rc );
254 }
255 
256 
257 void
258 LDAP_CALL
259 ldap_control_free( LDAPControl *ctrl )
260 {
261 	if ( ctrl != NULL ) {
262 		if ( ctrl->ldctl_oid != NULL ) {
263 			NSLDAPI_FREE( ctrl->ldctl_oid );
264 		}
265 		if ( ctrl->ldctl_value.bv_val != NULL ) {
266 			NSLDAPI_FREE( ctrl->ldctl_value.bv_val );
267 		}
268 		NSLDAPI_FREE( (char *)ctrl );
269 	}
270 }
271 
272 
273 void
274 LDAP_CALL
275 ldap_controls_free( LDAPControl **ctrls )
276 {
277 	int	i;
278 
279 	if ( ctrls != NULL ) {
280 		for ( i = 0; ctrls[i] != NULL; i++ ) {
281 			ldap_control_free( ctrls[i] );
282 		}
283 		NSLDAPI_FREE( (char *)ctrls );
284 	}
285 }
286 
287 
288 
289 #if 0
290 LDAPControl **
291 LDAP_CALL
292 ldap_control_append( LDAPControl **ctrl_src, LDAPControl *ctrl )
293 {
294     int nctrls = 0;
295 	LDAPControl **ctrlp;
296 	int i;
297 
298 	if ( NULL == ctrl )
299 	    return ( NULL );
300 
301 	/* Count the existing controls */
302 	if ( NULL != ctrl_src ) {
303 		while( NULL != ctrl_src[nctrls] ) {
304 			nctrls++;
305 		}
306 	}
307 
308 	/* allocate the new control structure */
309 	if ( ( ctrlp = (LDAPControl **)NSLDAPI_MALLOC( sizeof(LDAPControl *)
310 	    * (nctrls + 2) ) ) == NULL ) {
311 		return( NULL );
312 	}
313 	memset( ctrlp, 0, sizeof(*ctrlp) * (nctrls + 2) );
314 
315 	for( i = 0; i < (nctrls + 1); i++ ) {
316 	    if ( i < nctrls ) {
317 		    ctrlp[i] = ldap_control_dup( ctrl_src[i] );
318 	    } else {
319 		    ctrlp[i] = ldap_control_dup( ctrl );
320 	    }
321 	    if ( NULL == ctrlp[i] ) {
322 		    ldap_controls_free( ctrlp );
323 		    return( NULL );
324 	    }
325 	}
326 	return ctrlp;
327 }
328 #endif /* 0 */
329 
330 
331 /*
332  * Replace *ldctrls with a copy of newctrls.
333  * returns 0 if successful.
334  * return -1 if not and set error code inside LDAP *ld.
335  */
336 int
337 nsldapi_dup_controls( LDAP *ld, LDAPControl ***ldctrls, LDAPControl **newctrls )
338 {
339 	int	count;
340 
341 	if ( *ldctrls != NULL ) {
342 		ldap_controls_free( *ldctrls );
343 	}
344 
345 	if ( newctrls == NULL || newctrls[0] == NULL ) {
346 		*ldctrls = NULL;
347 		return( 0 );
348 	}
349 
350 	for ( count = 0; newctrls[ count ] != NULL; ++count ) {
351 		;
352 	}
353 
354 	if (( *ldctrls = (LDAPControl **)NSLDAPI_MALLOC(( count + 1 ) *
355 	    sizeof( LDAPControl *))) == NULL ) {
356 		LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
357 		return( -1 );
358 	}
359 	(*ldctrls)[ count ] = NULL;
360 
361 	for ( count = 0; newctrls[ count ] != NULL; ++count ) {
362 		if (( (*ldctrls)[ count ] =
363 		    ldap_control_dup( newctrls[ count ] )) == NULL ) {
364 			ldap_controls_free( *ldctrls );
365 			*ldctrls = NULL;
366 			LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
367 			return( -1 );
368 		}
369 	}
370 
371 	return( 0 );
372 }
373 
374 
375 /*
376  * return a malloc'd copy of "ctrl" (NULL if memory allocation fails)
377  */
378 static LDAPControl *
379 /* LDAP_CALL */		/* keep this routine internal for now */
380 ldap_control_dup( LDAPControl *ctrl )
381 {
382 	LDAPControl	*rctrl;
383 
384 	if (( rctrl = (LDAPControl *)NSLDAPI_MALLOC( sizeof( LDAPControl )))
385 	    == NULL ) {
386 		return( NULL );
387 	}
388 
389 	if ( ldap_control_copy_contents( rctrl, ctrl ) != LDAP_SUCCESS ) {
390 		NSLDAPI_FREE( rctrl );
391 		return( NULL );
392 	}
393 
394 	return( rctrl );
395 }
396 
397 
398 /*
399  * duplicate the contents of "ctrl_src" and place in "ctrl_dst"
400  */
401 static int
402 /* LDAP_CALL */		/* keep this routine internal for now */
403 ldap_control_copy_contents( LDAPControl *ctrl_dst, LDAPControl *ctrl_src )
404 {
405 	size_t	len;
406 
407 	if ( NULL == ctrl_dst || NULL == ctrl_src ) {
408 		return( LDAP_PARAM_ERROR );
409 	}
410 
411 	ctrl_dst->ldctl_iscritical = ctrl_src->ldctl_iscritical;
412 
413 	/* fill in the fields of this new control */
414 	if (( ctrl_dst->ldctl_oid = nsldapi_strdup( ctrl_src->ldctl_oid ))
415 	    == NULL ) {
416 		return( LDAP_NO_MEMORY );
417 	}
418 
419 	len = (size_t)(ctrl_src->ldctl_value).bv_len;
420 	if ( ctrl_src->ldctl_value.bv_val == NULL || len <= 0 ) {
421 		ctrl_dst->ldctl_value.bv_len = 0;
422 		ctrl_dst->ldctl_value.bv_val = NULL;
423 	} else {
424 		ctrl_dst->ldctl_value.bv_len = len;
425 		if (( ctrl_dst->ldctl_value.bv_val = NSLDAPI_MALLOC( len ))
426 		    == NULL ) {
427 			NSLDAPI_FREE( ctrl_dst->ldctl_oid );
428 			return( LDAP_NO_MEMORY );
429 		}
430 		SAFEMEMCPY( ctrl_dst->ldctl_value.bv_val,
431 		    ctrl_src->ldctl_value.bv_val, len );
432 	}
433 
434 	return ( LDAP_SUCCESS );
435 }
436 
437 
438 
439 /*
440  * build an allocated LDAPv3 control.  Returns an LDAP error code.
441  */
442 int
443 nsldapi_build_control( char *oid, BerElement *ber, int freeber, char iscritical,
444     LDAPControl **ctrlp )
445 {
446 	int		rc;
447 	struct berval	*bvp;
448 
449 	if ( ber == NULL ) {
450 		bvp = NULL;
451 	} else {
452 		/* allocate struct berval with contents of the BER encoding */
453 		rc = ber_flatten( ber, &bvp );
454 		if ( freeber ) {
455 			ber_free( ber, 1 );
456 		}
457 		if ( rc == -1 ) {
458 			return( LDAP_NO_MEMORY );
459 		}
460 	}
461 
462 	/* allocate the new control structure */
463 	if (( *ctrlp = (LDAPControl *)NSLDAPI_MALLOC( sizeof(LDAPControl)))
464 	    == NULL ) {
465 		if ( bvp != NULL ) {
466 			ber_bvfree( bvp );
467 		}
468 		return( LDAP_NO_MEMORY );
469 	}
470 
471 	/* fill in the fields of this new control */
472 	(*ctrlp)->ldctl_iscritical = iscritical;
473 	if (( (*ctrlp)->ldctl_oid = nsldapi_strdup( oid )) == NULL ) {
474 		NSLDAPI_FREE( *ctrlp );
475 		if ( bvp != NULL ) {
476 			ber_bvfree( bvp );
477 		}
478 		return( LDAP_NO_MEMORY );
479 	}
480 
481 	if ( bvp == NULL ) {
482 		(*ctrlp)->ldctl_value.bv_len = 0;
483 		(*ctrlp)->ldctl_value.bv_val = NULL;
484 	} else {
485 		(*ctrlp)->ldctl_value = *bvp;	/* struct copy */
486 		NSLDAPI_FREE( bvp );	/* free container, not contents! */
487 	}
488 
489 	return( LDAP_SUCCESS );
490 }
491