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/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
16 *
17 * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
18 * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
19 * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
20 * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
21 * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
22 * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
23 * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
24 * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
25 */
26/* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
27 * can be found in the file "build/LICENSE-2.0.1" in this distribution
28 * of OpenLDAP Software.
29 */
30
31#include "portable.h"
32
33#include <stdio.h>
34#include <ac/stdlib.h>
35#include <ac/string.h>
36#include <ac/time.h>
37
38#include "ldap-int.h"
39
40#define LDAP_MATCHRULE_IDENTIFIER      0x80L
41#define LDAP_REVERSEORDER_IDENTIFIER   0x81L
42#define LDAP_ATTRTYPES_IDENTIFIER      0x80L
43
44
45
46/* ---------------------------------------------------------------------------
47   countKeys
48
49   Internal function to determine the number of keys in the string.
50
51   keyString  (IN) String of items separated by whitespace.
52   ---------------------------------------------------------------------------*/
53
54static int countKeys(char *keyString)
55{
56	char *p = keyString;
57	int count = 0;
58
59	for (;;)
60	{
61		while (LDAP_SPACE(*p))		 /* Skip leading whitespace */
62			p++;
63
64		if (*p == '\0')			/* End of string? */
65			return count;
66
67		count++;				/* Found start of a key */
68
69		while (!LDAP_SPACE(*p))	/* Skip till next space or end of string. */
70			if (*p++ == '\0')
71				return count;
72	}
73}
74
75
76/* ---------------------------------------------------------------------------
77   readNextKey
78
79   Internal function to parse the next sort key in the string.
80   Allocate an LDAPSortKey structure and initialize it with
81   attribute name, reverse flag, and matching rule OID.
82
83   Each sort key in the string has the format:
84	  [whitespace][-]attribute[:[OID]]
85
86   pNextKey    (IN/OUT) Points to the next key in the sortkey string to parse.
87						The pointer is updated to point to the next character
88						after the sortkey being parsed.
89
90   key         (OUT)    Points to the address of an LDAPSortKey stucture
91						which has been allocated by this routine and
92						initialized with information from the next sortkey.
93   ---------------------------------------------------------------------------*/
94
95static int readNextKey( char **pNextKey, LDAPSortKey **key)
96{
97	char *p = *pNextKey;
98	int rev = 0;
99	char *attrStart;
100	int attrLen;
101	char *oidStart = NULL;
102	int oidLen = 0;
103
104	/* Skip leading white space. */
105	while (LDAP_SPACE(*p))
106		p++;
107
108	if (*p == '-')		 /* Check if the reverse flag is present. */
109	{
110		rev=1;
111		p++;
112	}
113
114	/* We're now positioned at the start of the attribute. */
115	attrStart = p;
116
117	/* Get the length of the attribute until the next whitespace or ":". */
118	attrLen = strcspn(p, " \t:");
119	p += attrLen;
120
121	if (attrLen == 0)	 /* If no attribute name was present, quit. */
122		return LDAP_PARAM_ERROR;
123
124	if (*p == ':')
125	{
126		oidStart = ++p;				 /* Start of the OID, after the colon */
127		oidLen = strcspn(p, " \t");	 /* Get length of OID till next whitespace */
128		p += oidLen;
129	}
130
131	*pNextKey = p;		 /* Update argument to point to next key */
132
133	/* Allocate an LDAPSortKey structure */
134	*key = LDAP_MALLOC(sizeof(LDAPSortKey));
135	if (*key == NULL) return LDAP_NO_MEMORY;
136
137	/* Allocate memory for the attribute and copy to it. */
138	(*key)->attributeType = LDAP_MALLOC(attrLen+1);
139	if ((*key)->attributeType == NULL) {
140		LDAP_FREE(*key);
141		return LDAP_NO_MEMORY;
142	}
143
144	strncpy((*key)->attributeType, attrStart, attrLen);
145	(*key)->attributeType[attrLen] = 0;
146
147	/* If present, allocate memory for the OID and copy to it. */
148	if (oidLen) {
149		(*key)->orderingRule = LDAP_MALLOC(oidLen+1);
150		if ((*key)->orderingRule == NULL) {
151			LDAP_FREE((*key)->attributeType);
152			LDAP_FREE(*key);
153			return LDAP_NO_MEMORY;
154		}
155		strncpy((*key)->orderingRule, oidStart, oidLen);
156		(*key)->orderingRule[oidLen] = 0;
157
158	} else {
159		(*key)->orderingRule = NULL;
160	}
161
162	(*key)->reverseOrder = rev;
163
164	return LDAP_SUCCESS;
165}
166
167
168/* ---------------------------------------------------------------------------
169   ldap_create_sort_keylist
170
171   Create an array of pointers to LDAPSortKey structures, containing the
172   information specified by the string representation of one or more
173   sort keys.
174
175   sortKeyList    (OUT) Points to a null-terminated array of pointers to
176						LDAPSortKey structures allocated by this routine.
177						This memory SHOULD be freed by the calling program
178						using ldap_free_sort_keylist().
179
180   keyString      (IN)  Points to a string of one or more sort keys.
181
182   ---------------------------------------------------------------------------*/
183
184int
185ldap_create_sort_keylist ( LDAPSortKey ***sortKeyList, char *keyString )
186{
187	int         numKeys, rc, i;
188	char        *nextKey;
189	LDAPSortKey **keyList = NULL;
190
191	assert( sortKeyList != NULL );
192	assert( keyString != NULL );
193
194	*sortKeyList = NULL;
195
196	/* Determine the number of sort keys so we can allocate memory. */
197	if (( numKeys = countKeys(keyString)) == 0) {
198		return LDAP_PARAM_ERROR;
199	}
200
201	/* Allocate the array of pointers.  Initialize to NULL. */
202	keyList=(LDAPSortKey**)LBER_CALLOC(numKeys+1, sizeof(LDAPSortKey*));
203	if ( keyList == NULL) return LDAP_NO_MEMORY;
204
205	/* For each sort key in the string, create an LDAPSortKey structure
206	   and add it to the list.
207	*/
208	nextKey = keyString;		  /* Points to the next key in the string */
209	for (i=0; i < numKeys; i++) {
210		rc = readNextKey(&nextKey, &keyList[i]);
211
212		if (rc != LDAP_SUCCESS) {
213			ldap_free_sort_keylist(keyList);
214			return rc;
215		}
216	}
217
218	*sortKeyList = keyList;
219	return LDAP_SUCCESS;
220}
221
222
223/* ---------------------------------------------------------------------------
224   ldap_free_sort_keylist
225
226   Frees the sort key structures created by ldap_create_sort_keylist().
227   Frees the memory referenced by the LDAPSortKey structures,
228   the LDAPSortKey structures themselves, and the array of pointers
229   to the structures.
230
231   keyList     (IN) Points to an array of pointers to LDAPSortKey structures.
232   ---------------------------------------------------------------------------*/
233
234void
235ldap_free_sort_keylist ( LDAPSortKey **keyList )
236{
237	int i;
238	LDAPSortKey *nextKeyp;
239
240	if (keyList == NULL) return;
241
242	i=0;
243	while ( 0 != (nextKeyp = keyList[i++]) ) {
244		if (nextKeyp->attributeType) {
245			LBER_FREE(nextKeyp->attributeType);
246		}
247
248		if (nextKeyp->orderingRule != NULL) {
249			LBER_FREE(nextKeyp->orderingRule);
250		}
251
252		LBER_FREE(nextKeyp);
253	}
254
255	LBER_FREE(keyList);
256}
257
258
259/* ---------------------------------------------------------------------------
260   ldap_create_sort_control_value
261
262   Create and encode the value of the server-side sort control.
263
264   ld          (IN) An LDAP session handle, as obtained from a call to
265					ldap_init().
266
267   keyList     (IN) Points to a null-terminated array of pointers to
268					LDAPSortKey structures, containing a description of
269					each of the sort keys to be used.  The description
270					consists of an attribute name, ascending/descending flag,
271					and an optional matching rule (OID) to use.
272
273   value      (OUT) Contains the control value; the bv_val member of the berval structure
274					SHOULD be freed by calling ldap_memfree() when done.
275
276
277   Ber encoding
278
279   SortKeyList ::= SEQUENCE OF SEQUENCE {
280		   attributeType   AttributeDescription,
281		   orderingRule    [0] MatchingRuleId OPTIONAL,
282		   reverseOrder    [1] BOOLEAN DEFAULT FALSE }
283
284   ---------------------------------------------------------------------------*/
285
286int
287ldap_create_sort_control_value(
288	LDAP *ld,
289	LDAPSortKey **keyList,
290	struct berval *value )
291{
292	int		i;
293	BerElement	*ber = NULL;
294	ber_tag_t	tag;
295
296	assert( ld != NULL );
297	assert( LDAP_VALID( ld ) );
298
299	if ( ld == NULL ) return LDAP_PARAM_ERROR;
300	if ( keyList == NULL || value == NULL ) {
301		ld->ld_errno = LDAP_PARAM_ERROR;
302		return LDAP_PARAM_ERROR;
303	}
304
305	value->bv_val = NULL;
306	value->bv_len = 0;
307	ld->ld_errno = LDAP_SUCCESS;
308
309	ber = ldap_alloc_ber_with_options( ld );
310	if ( ber == NULL) {
311		ld->ld_errno = LDAP_NO_MEMORY;
312		return ld->ld_errno;
313	}
314
315	tag = ber_printf( ber, "{" /*}*/ );
316	if ( tag == LBER_ERROR ) {
317		goto error_return;
318	}
319
320	for ( i = 0; keyList[i] != NULL; i++ ) {
321		tag = ber_printf( ber, "{s" /*}*/, keyList[i]->attributeType );
322		if ( tag == LBER_ERROR ) {
323			goto error_return;
324		}
325
326		if ( keyList[i]->orderingRule != NULL ) {
327			tag = ber_printf( ber, "ts",
328				LDAP_MATCHRULE_IDENTIFIER,
329				keyList[i]->orderingRule );
330
331			if ( tag == LBER_ERROR ) {
332				goto error_return;
333			}
334		}
335
336		if ( keyList[i]->reverseOrder ) {
337			tag = ber_printf( ber, "tb",
338				LDAP_REVERSEORDER_IDENTIFIER,
339				keyList[i]->reverseOrder );
340
341			if ( tag == LBER_ERROR ) {
342				goto error_return;
343			}
344		}
345
346		tag = ber_printf( ber, /*{*/ "N}" );
347		if ( tag == LBER_ERROR ) {
348			goto error_return;
349		}
350	}
351
352	tag = ber_printf( ber, /*{*/ "N}" );
353	if ( tag == LBER_ERROR ) {
354		goto error_return;
355	}
356
357	if ( ber_flatten2( ber, value, 1 ) == -1 ) {
358		ld->ld_errno = LDAP_NO_MEMORY;
359	}
360
361	if ( 0 ) {
362error_return:;
363		ld->ld_errno =  LDAP_ENCODING_ERROR;
364	}
365
366	if ( ber != NULL ) {
367		ber_free( ber, 1 );
368	}
369
370	return ld->ld_errno;
371}
372
373
374/* ---------------------------------------------------------------------------
375   ldap_create_sort_control
376
377   Create and encode the server-side sort control.
378
379   ld          (IN) An LDAP session handle, as obtained from a call to
380					ldap_init().
381
382   keyList     (IN) Points to a null-terminated array of pointers to
383					LDAPSortKey structures, containing a description of
384					each of the sort keys to be used.  The description
385					consists of an attribute name, ascending/descending flag,
386					and an optional matching rule (OID) to use.
387
388   isCritical  (IN) 0 - Indicates the control is not critical to the operation.
389					non-zero - The control is critical to the operation.
390
391   ctrlp      (OUT) Returns a pointer to the LDAPControl created.  This control
392					SHOULD be freed by calling ldap_control_free() when done.
393
394
395   Ber encoding
396
397   SortKeyList ::= SEQUENCE OF SEQUENCE {
398		   attributeType   AttributeDescription,
399		   orderingRule    [0] MatchingRuleId OPTIONAL,
400		   reverseOrder    [1] BOOLEAN DEFAULT FALSE }
401
402   ---------------------------------------------------------------------------*/
403
404int
405ldap_create_sort_control(
406	LDAP *ld,
407	LDAPSortKey **keyList,
408	int isCritical,
409	LDAPControl **ctrlp )
410{
411	struct berval	value;
412
413	assert( ld != NULL );
414	assert( LDAP_VALID( ld ) );
415
416	if ( ld == NULL ) {
417		return LDAP_PARAM_ERROR;
418	}
419
420	if ( ctrlp == NULL ) {
421		ld->ld_errno = LDAP_PARAM_ERROR;
422		return ld->ld_errno;
423	}
424
425	ld->ld_errno = ldap_create_sort_control_value( ld, keyList, &value );
426	if ( ld->ld_errno == LDAP_SUCCESS ) {
427		ld->ld_errno = ldap_control_create( LDAP_CONTROL_SORTREQUEST,
428			isCritical, &value, 0, ctrlp );
429		if ( ld->ld_errno != LDAP_SUCCESS ) {
430			LDAP_FREE( value.bv_val );
431		}
432	}
433
434	return ld->ld_errno;
435}
436
437
438/* ---------------------------------------------------------------------------
439	 apple specific code
440	 ldap_parse_sort_control - APPLE - WRAPPER method added during upgrade from 2.3.27 to 2.4.11
441	 to support older clients
442
443	 Decode the server-side sort control return information.
444
445	 ld          (IN) An LDAP session handle, as obtained from a call to
446	 ldap_init().
447
448	 ctrls       (IN) The address of a NULL-terminated array of LDAPControl
449	 structures, typically obtained by a call to
450	 ldap_parse_result().
451
452	 returnCode (OUT) This result parameter is filled in with the sort control
453	 result code.  This parameter MUST not be NULL.
454
455	 attribute  (OUT) If an error occured the server may return a string
456	 indicating the first attribute in the sortkey list
457	 that was in error.  If a string is returned, the memory
458	 should be freed with ldap_memfree.  If this parameter is
459	 NULL, no string is returned.
460
461
462	 Ber encoding for sort control
463
464		 SortResult ::= SEQUENCE {
465			sortResult  ENUMERATED {
466				 success                   (0), -- results are sorted
467				 operationsError           (1), -- server internal failure
468				 timeLimitExceeded         (3), -- timelimit reached before
469				 -- sorting was completed
470				 strongAuthRequired        (8), -- refused to return sorted
471				 -- results via insecure
472				 -- protocol
473				 adminLimitExceeded       (11), -- too many matching entries
474				 -- for the server to sort
475				 noSuchAttribute          (16), -- unrecognized attribute
476				 -- type in sort key
477				 inappropriateMatching    (18), -- unrecognized or inappro-
478				 -- priate matching rule in
479				 -- sort key
480				 insufficientAccessRights (50), -- refused to return sorted
481				 -- results to this client
482				 busy                     (51), -- too busy to process
483				 unwillingToPerform       (53), -- unable to sort
484				 other                    (80)
485			},
486			attributeType [0] AttributeDescription OPTIONAL }
487 ---------------------------------------------------------------------------*/
488
489int
490ldap_parse_sort_control(
491						LDAP           *ld,
492						LDAPControl    **ctrls,
493						unsigned long  *returnCode,
494						char           **attribute )
495{
496	LDAPControl *pControl;
497	int i;
498
499	if (ld == NULL) {
500		ld->ld_errno = LDAP_PARAM_ERROR;
501		return(ld->ld_errno);
502	}
503
504	if (ctrls == NULL) {
505		ld->ld_errno =  LDAP_CONTROL_NOT_FOUND;
506		return(ld->ld_errno);
507	}
508
509	if (attribute) {
510		*attribute = NULL;
511	}
512
513	/* Search the list of control responses for a sort control. */
514	for (i=0; ctrls[i]; i++) {
515		pControl = ctrls[i];
516		if (!strcmp(LDAP_CONTROL_SORTRESPONSE, pControl->ldctl_oid))
517			return ldap_parse_sortresponse_control(ld,pControl,(ber_int_t *)returnCode, attribute);
518	}
519
520	/* No sort control was found. */
521	ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
522	return(ld->ld_errno);
523
524}
525
526/* ---------------------------------------------------------------------------
527   ldap_parse_sortresponse_control
528
529   Decode the server-side sort control return information.
530
531   ld          (IN) An LDAP session handle, as obtained from a call to
532					ldap_init().
533
534   ctrl        (IN) The address of the LDAP Control Structure.
535
536   returnCode (OUT) This result parameter is filled in with the sort control
537					result code.  This parameter MUST not be NULL.
538
539   attribute  (OUT) If an error occured the server may return a string
540					indicating the first attribute in the sortkey list
541					that was in error.  If a string is returned, the memory
542					should be freed with ldap_memfree.  If this parameter is
543					NULL, no string is returned.
544
545
546   Ber encoding for sort control
547
548	 SortResult ::= SEQUENCE {
549		sortResult  ENUMERATED {
550			success                   (0), -- results are sorted
551			operationsError           (1), -- server internal failure
552			timeLimitExceeded         (3), -- timelimit reached before
553										   -- sorting was completed
554			strongAuthRequired        (8), -- refused to return sorted
555										   -- results via insecure
556										   -- protocol
557			adminLimitExceeded       (11), -- too many matching entries
558										   -- for the server to sort
559			noSuchAttribute          (16), -- unrecognized attribute
560										   -- type in sort key
561			inappropriateMatching    (18), -- unrecognized or inappro-
562										   -- priate matching rule in
563										   -- sort key
564			insufficientAccessRights (50), -- refused to return sorted
565										   -- results to this client
566			busy                     (51), -- too busy to process
567			unwillingToPerform       (53), -- unable to sort
568			other                    (80)
569			},
570	  attributeType [0] AttributeDescription OPTIONAL }
571   ---------------------------------------------------------------------------*/
572
573int
574ldap_parse_sortresponse_control(
575	LDAP *ld,
576	LDAPControl *ctrl,
577	ber_int_t *returnCode,
578	char **attribute )
579{
580	BerElement *ber;
581	ber_tag_t tag, berTag;
582	ber_len_t berLen;
583
584	assert( ld != NULL );
585	assert( LDAP_VALID( ld ) );
586
587	if (ld == NULL) {
588		return LDAP_PARAM_ERROR;
589	}
590
591	if (ctrl == NULL) {
592		ld->ld_errno =  LDAP_PARAM_ERROR;
593		return(ld->ld_errno);
594	}
595
596	if (attribute) {
597		*attribute = NULL;
598	}
599
600	if ( strcmp(LDAP_CONTROL_SORTRESPONSE, ctrl->ldctl_oid) != 0 ) {
601		/* Not sort result control */
602		ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
603		return(ld->ld_errno);
604	}
605
606	/* Create a BerElement from the berval returned in the control. */
607	ber = ber_init(&ctrl->ldctl_value);
608
609	if (ber == NULL) {
610		ld->ld_errno = LDAP_NO_MEMORY;
611		return(ld->ld_errno);
612	}
613
614	/* Extract the result code from the control. */
615	tag = ber_scanf(ber, "{e" /*}*/, returnCode);
616
617	if( tag == LBER_ERROR ) {
618		ber_free(ber, 1);
619		ld->ld_errno = LDAP_DECODING_ERROR;
620		return(ld->ld_errno);
621	}
622
623	/* If caller wants the attribute name, and if it's present in the control,
624	   extract the attribute name which caused the error. */
625	if (attribute && (LDAP_ATTRTYPES_IDENTIFIER == ber_peek_tag(ber, &berLen)))
626	{
627		tag = ber_scanf(ber, "ta", &berTag, attribute);
628
629		if (tag == LBER_ERROR ) {
630			ber_free(ber, 1);
631			ld->ld_errno = LDAP_DECODING_ERROR;
632			return(ld->ld_errno);
633		}
634	}
635
636	ber_free(ber,1);
637
638	ld->ld_errno = LDAP_SUCCESS;
639	return(ld->ld_errno);
640}
641