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