aldap.c revision 1.1
1/*	$OpenBSD: aldap.c,v 1.1 2020/09/12 15:06:12 martijn Exp $ */
2
3/*
4 * Copyright (c) 2008 Alexander Schrijver <aschrijver@openbsd.org>
5 * Copyright (c) 2006, 2007 Marc Balmer <mbalmer@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <arpa/inet.h>
21#include <ctype.h>
22#include <errno.h>
23#include <inttypes.h>
24#include <string.h>
25#include <stdlib.h>
26#include <unistd.h>
27
28#include <event.h>
29
30#include "aldap.h"
31
32#if 0
33#define DEBUG
34#endif
35#define VERSION 3
36
37static struct ber_element	*ldap_parse_search_filter(struct ber_element *,
38				    char *);
39static struct ber_element	*ldap_do_parse_search_filter(
40				    struct ber_element *, char **);
41struct aldap_stringset		*aldap_get_stringset(struct ber_element *);
42char				*utoa(char *);
43static int			 isu8cont(unsigned char);
44char				*parseval(char *, size_t);
45int				aldap_create_page_control(struct ber_element *,
46				    int, struct aldap_page_control *);
47int				aldap_send(struct aldap *,
48				    struct ber_element *);
49unsigned int			aldap_application(struct ber_element *);
50
51#ifdef DEBUG
52void			 ldap_debug_elements(struct ber_element *);
53#endif
54
55#ifdef DEBUG
56#define DPRINTF(x...)	printf(x)
57#define LDAP_DEBUG(x, y)	do { fprintf(stderr, "*** " x "\n"); ldap_debug_elements(y); } while (0)
58#else
59#define DPRINTF(x...)	do { } while (0)
60#define LDAP_DEBUG(x, y)	do { } while (0)
61#endif
62
63unsigned int
64aldap_application(struct ber_element *elm)
65{
66	return BER_TYPE_OCTETSTRING;
67}
68
69int
70aldap_close(struct aldap *al)
71{
72	if (al->tls != NULL) {
73		tls_close(al->tls);
74		tls_free(al->tls);
75	}
76	close(al->fd);
77	ober_free(&al->ber);
78	evbuffer_free(al->buf);
79	free(al);
80
81	return (0);
82}
83
84struct aldap *
85aldap_init(int fd)
86{
87	struct aldap *a;
88
89	if ((a = calloc(1, sizeof(*a))) == NULL)
90		return NULL;
91	a->buf = evbuffer_new();
92	a->fd = fd;
93	ober_set_application(&a->ber, aldap_application);
94
95	return a;
96}
97
98int
99aldap_tls(struct aldap *ldap, struct tls_config *cfg, const char *name)
100{
101	ldap->tls = tls_client();
102	if (ldap->tls == NULL) {
103		ldap->err = ALDAP_ERR_OPERATION_FAILED;
104		return (-1);
105	}
106
107	if (tls_configure(ldap->tls, cfg) == -1) {
108		ldap->err = ALDAP_ERR_TLS_ERROR;
109		return (-1);
110	}
111
112	if (tls_connect_socket(ldap->tls, ldap->fd, name) == -1) {
113		ldap->err = ALDAP_ERR_TLS_ERROR;
114		return (-1);
115	}
116
117	if (tls_handshake(ldap->tls) == -1) {
118		ldap->err = ALDAP_ERR_TLS_ERROR;
119		return (-1);
120	}
121
122	return (0);
123}
124
125int
126aldap_send(struct aldap *ldap, struct ber_element *root)
127{
128	void *ptr;
129	char *data;
130	size_t len, done;
131	ssize_t error, wrote;
132
133	len = ober_calc_len(root);
134	error = ober_write_elements(&ldap->ber, root);
135	ober_free_elements(root);
136	if (error == -1)
137		return -1;
138
139	ober_get_writebuf(&ldap->ber, &ptr);
140	done = 0;
141	data = ptr;
142	while (len > 0) {
143		if (ldap->tls != NULL) {
144			wrote = tls_write(ldap->tls, data + done, len);
145			if (wrote == TLS_WANT_POLLIN ||
146			    wrote == TLS_WANT_POLLOUT)
147				continue;
148		} else
149			wrote = write(ldap->fd, data + done, len);
150
151		if (wrote == -1)
152			return -1;
153
154		len -= wrote;
155		done += wrote;
156	}
157
158	return 0;
159}
160
161int
162aldap_req_starttls(struct aldap *ldap)
163{
164	struct ber_element *root = NULL, *ber;
165
166	if ((root = ober_add_sequence(NULL)) == NULL)
167		goto fail;
168
169	ber = ober_printf_elements(root, "d{tst", ++ldap->msgid, BER_CLASS_APP,
170	    LDAP_REQ_EXTENDED, LDAP_STARTTLS_OID, BER_CLASS_CONTEXT, 0);
171	if (ber == NULL) {
172		ldap->err = ALDAP_ERR_OPERATION_FAILED;
173		goto fail;
174	}
175
176	if (aldap_send(ldap, root) == -1)
177		goto fail;
178
179	return (ldap->msgid);
180fail:
181	if (root != NULL)
182		ober_free_elements(root);
183
184	ldap->err = ALDAP_ERR_OPERATION_FAILED;
185	return (-1);
186}
187
188int
189aldap_bind(struct aldap *ldap, char *binddn, char *bindcred)
190{
191	struct ber_element *root = NULL, *elm;
192
193	if (binddn == NULL)
194		binddn = "";
195	if (bindcred == NULL)
196		bindcred = "";
197
198	if ((root = ober_add_sequence(NULL)) == NULL)
199		goto fail;
200
201	elm = ober_printf_elements(root, "d{tdsst", ++ldap->msgid, BER_CLASS_APP,
202	    LDAP_REQ_BIND, VERSION, binddn, bindcred, BER_CLASS_CONTEXT,
203	    LDAP_AUTH_SIMPLE);
204	if (elm == NULL)
205		goto fail;
206
207	LDAP_DEBUG("aldap_bind", root);
208
209	if (aldap_send(ldap, root) == -1) {
210		root = NULL;
211		goto fail;
212	}
213	return (ldap->msgid);
214fail:
215	if (root != NULL)
216		ober_free_elements(root);
217
218	ldap->err = ALDAP_ERR_OPERATION_FAILED;
219	return (-1);
220}
221
222int
223aldap_unbind(struct aldap *ldap)
224{
225	struct ber_element *root = NULL, *elm;
226
227	if ((root = ober_add_sequence(NULL)) == NULL)
228		goto fail;
229	elm = ober_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP,
230	    LDAP_REQ_UNBIND_30);
231	if (elm == NULL)
232		goto fail;
233
234	LDAP_DEBUG("aldap_unbind", root);
235
236	if (aldap_send(ldap, root) == -1) {
237		root = NULL;
238		goto fail;
239	}
240	return (ldap->msgid);
241fail:
242	if (root != NULL)
243		ober_free_elements(root);
244
245	ldap->err = ALDAP_ERR_OPERATION_FAILED;
246
247	return (-1);
248}
249
250int
251aldap_search(struct aldap *ldap, char *basedn, enum scope scope, char *filter,
252    char **attrs, int typesonly, int sizelimit, int timelimit,
253    struct aldap_page_control *page)
254{
255	struct ber_element *root = NULL, *ber, *c;
256	int i;
257
258	if ((root = ober_add_sequence(NULL)) == NULL)
259		goto fail;
260
261	ber = ober_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP,
262	    LDAP_REQ_SEARCH);
263	if (ber == NULL) {
264		ldap->err = ALDAP_ERR_OPERATION_FAILED;
265		goto fail;
266	}
267
268	c = ber;
269	ber = ober_printf_elements(ber, "sEEddb", basedn, (long long)scope,
270	                         (long long)LDAP_DEREF_NEVER, sizelimit,
271				 timelimit, typesonly);
272	if (ber == NULL) {
273		ldap->err = ALDAP_ERR_OPERATION_FAILED;
274		goto fail;
275	}
276
277	if ((ber = ldap_parse_search_filter(ber, filter)) == NULL) {
278		ldap->err = ALDAP_ERR_PARSER_ERROR;
279		goto fail;
280	}
281
282	if ((ber = ober_add_sequence(ber)) == NULL)
283		goto fail;
284	if (attrs != NULL)
285		for (i = 0; attrs[i] != NULL; i++) {
286			if ((ber = ober_add_string(ber, attrs[i])) == NULL)
287				goto fail;
288		}
289
290	aldap_create_page_control(c, 100, page);
291
292	LDAP_DEBUG("aldap_search", root);
293
294	if (aldap_send(ldap, root) == -1) {
295		root = NULL;
296		ldap->err = ALDAP_ERR_OPERATION_FAILED;
297		goto fail;
298	}
299
300	return (ldap->msgid);
301
302fail:
303	if (root != NULL)
304		ober_free_elements(root);
305
306	return (-1);
307}
308
309int
310aldap_create_page_control(struct ber_element *elm, int size,
311    struct aldap_page_control *page)
312{
313	ssize_t len;
314	struct ber c;
315	struct ber_element *ber = NULL;
316
317	c.br_wbuf = NULL;
318
319	ber = ober_add_sequence(NULL);
320
321	if (page == NULL) {
322		if (ober_printf_elements(ber, "ds", 50, "") == NULL)
323			goto fail;
324	} else {
325		if (ober_printf_elements(ber, "dx", 50, page->cookie,
326			    page->cookie_len) == NULL)
327			goto fail;
328	}
329
330	if ((len = ober_write_elements(&c, ber)) < 1)
331		goto fail;
332	if (ober_printf_elements(elm, "{t{sx", 2, 0, LDAP_PAGED_OID,
333		                c.br_wbuf, (size_t)len) == NULL)
334		goto fail;
335
336	ober_free_elements(ber);
337	ober_free(&c);
338	return len;
339fail:
340	if (ber != NULL)
341		ober_free_elements(ber);
342	ober_free(&c);
343
344	return (-1);
345}
346
347struct aldap_message *
348aldap_parse(struct aldap *ldap)
349{
350	int			 class;
351	unsigned int		 type;
352	long long		 msgid = 0;
353	struct aldap_message	*m;
354	struct ber_element	*a = NULL, *ep;
355	char			 rbuf[512];
356	int			 ret, retry;
357
358	if ((m = calloc(1, sizeof(struct aldap_message))) == NULL)
359		return NULL;
360
361	retry = 0;
362	while (m->msg == NULL) {
363		if (retry || EVBUFFER_LENGTH(ldap->buf) == 0) {
364			if (ldap->tls) {
365				ret = tls_read(ldap->tls, rbuf, sizeof(rbuf));
366				if (ret == TLS_WANT_POLLIN ||
367				    ret == TLS_WANT_POLLOUT)
368					continue;
369			} else
370				ret = read(ldap->fd, rbuf, sizeof(rbuf));
371
372			if (ret == -1) {
373				goto parsefail;
374			}
375
376			evbuffer_add(ldap->buf, rbuf, ret);
377		}
378
379		if (EVBUFFER_LENGTH(ldap->buf) > 0) {
380			ober_set_readbuf(&ldap->ber, EVBUFFER_DATA(ldap->buf),
381			    EVBUFFER_LENGTH(ldap->buf));
382			errno = 0;
383			m->msg = ober_read_elements(&ldap->ber, NULL);
384			if (errno != 0 && errno != ECANCELED) {
385				goto parsefail;
386			}
387
388			retry = 1;
389		}
390	}
391
392	evbuffer_drain(ldap->buf, ldap->ber.br_rptr - ldap->ber.br_rbuf);
393
394	LDAP_DEBUG("message", m->msg);
395
396	if (ober_scanf_elements(m->msg, "{ite", &msgid, &class, &type, &a) != 0)
397		goto parsefail;
398	m->msgid = msgid;
399	m->message_type = type;
400	m->protocol_op = a;
401
402	switch (m->message_type) {
403	case LDAP_RES_BIND:
404	case LDAP_RES_MODIFY:
405	case LDAP_RES_ADD:
406	case LDAP_RES_DELETE:
407	case LDAP_RES_MODRDN:
408	case LDAP_RES_COMPARE:
409	case LDAP_RES_SEARCH_RESULT:
410		if (ober_scanf_elements(m->protocol_op, "{EeSe",
411		    &m->body.res.rescode, &m->dn, &m->body.res.diagmsg) != 0)
412			goto parsefail;
413		if (m->body.res.rescode == LDAP_REFERRAL) {
414			a = m->body.res.diagmsg->be_next;
415			if (ober_scanf_elements(a, "{e", &m->references) != 0)
416				goto parsefail;
417		}
418		if (m->msg->be_sub) {
419			for (ep = m->msg->be_sub; ep != NULL; ep = ep->be_next) {
420				ober_scanf_elements(ep, "t", &class, &type);
421				if (class == 2 && type == 0)
422					m->page = aldap_parse_page_control(ep->be_sub->be_sub,
423					    ep->be_sub->be_sub->be_len);
424			}
425		} else
426			m->page = NULL;
427		break;
428	case LDAP_RES_SEARCH_ENTRY:
429		if (ober_scanf_elements(m->protocol_op, "{eS{e", &m->dn,
430		    &m->body.search.attrs) != 0)
431			goto parsefail;
432		break;
433	case LDAP_RES_SEARCH_REFERENCE:
434		if (ober_scanf_elements(m->protocol_op, "{e", &m->references) != 0)
435			goto parsefail;
436		break;
437	case LDAP_RES_EXTENDED:
438		if (ober_scanf_elements(m->protocol_op, "{E",
439		    &m->body.res.rescode) != 0) {
440			goto parsefail;
441		}
442		break;
443	}
444
445	return m;
446parsefail:
447	evbuffer_drain(ldap->buf, EVBUFFER_LENGTH(ldap->buf));
448	ldap->err = ALDAP_ERR_PARSER_ERROR;
449	aldap_freemsg(m);
450	return NULL;
451}
452
453struct aldap_page_control *
454aldap_parse_page_control(struct ber_element *control, size_t len)
455{
456	char *oid, *s;
457	char *encoded;
458	struct ber b;
459	struct ber_element *elm;
460	struct aldap_page_control *page;
461
462	b.br_wbuf = NULL;
463	ober_scanf_elements(control, "ss", &oid, &encoded);
464	ober_set_readbuf(&b, encoded, control->be_next->be_len);
465	elm = ober_read_elements(&b, NULL);
466
467	if ((page = malloc(sizeof(struct aldap_page_control))) == NULL) {
468		if (elm != NULL)
469			ober_free_elements(elm);
470		ober_free(&b);
471		return NULL;
472	}
473
474	ober_scanf_elements(elm->be_sub, "is", &page->size, &s);
475	page->cookie_len = elm->be_sub->be_next->be_len;
476
477	if ((page->cookie = malloc(page->cookie_len)) == NULL) {
478		if (elm != NULL)
479			ober_free_elements(elm);
480		ober_free(&b);
481		free(page);
482		return NULL;
483	}
484	memcpy(page->cookie, s, page->cookie_len);
485
486	ober_free_elements(elm);
487	ober_free(&b);
488	return page;
489}
490
491void
492aldap_freepage(struct aldap_page_control *page)
493{
494	free(page->cookie);
495	free(page);
496}
497
498void
499aldap_freemsg(struct aldap_message *msg)
500{
501	if (msg->msg)
502		ober_free_elements(msg->msg);
503	free(msg);
504}
505
506int
507aldap_get_resultcode(struct aldap_message *msg)
508{
509	return msg->body.res.rescode;
510}
511
512char *
513aldap_get_dn(struct aldap_message *msg)
514{
515	char *dn;
516
517	if (msg->dn == NULL)
518		return NULL;
519
520	if (ober_get_string(msg->dn, &dn) == -1)
521		return NULL;
522
523	return utoa(dn);
524}
525
526struct aldap_stringset *
527aldap_get_references(struct aldap_message *msg)
528{
529	if (msg->references == NULL)
530		return NULL;
531	return aldap_get_stringset(msg->references);
532}
533
534void
535aldap_free_references(char **values)
536{
537	int i;
538
539	if (values == NULL)
540		return;
541
542	for (i = 0; values[i] != NULL; i++)
543		free(values[i]);
544
545	free(values);
546}
547
548char *
549aldap_get_diagmsg(struct aldap_message *msg)
550{
551	char *s;
552
553	if (msg->body.res.diagmsg == NULL)
554		return NULL;
555
556	if (ober_get_string(msg->body.res.diagmsg, &s) == -1)
557		return NULL;
558
559	return utoa(s);
560}
561
562int
563aldap_count_attrs(struct aldap_message *msg)
564{
565	int i;
566	struct ber_element *a;
567
568	if (msg->body.search.attrs == NULL)
569		return (-1);
570
571	for (i = 0, a = msg->body.search.attrs;
572	    a != NULL && ober_get_eoc(a) != 0;
573	    i++, a = a->be_next)
574		;
575
576	return i;
577}
578
579int
580aldap_first_attr(struct aldap_message *msg, char **outkey,
581    struct aldap_stringset **outvalues)
582{
583	struct ber_element *b, *c;
584	char *key;
585	struct aldap_stringset *ret;
586
587	if (msg->body.search.attrs == NULL)
588		goto fail;
589
590	if (ober_scanf_elements(msg->body.search.attrs, "{s(e)}e",
591	    &key, &b, &c) != 0)
592		goto fail;
593
594	msg->body.search.iter = msg->body.search.attrs->be_next;
595
596	if ((ret = aldap_get_stringset(b)) == NULL)
597		goto fail;
598
599	(*outvalues) = ret;
600	(*outkey) = utoa(key);
601
602	return (1);
603fail:
604	(*outkey) = NULL;
605	(*outvalues) = NULL;
606	return (-1);
607}
608
609int
610aldap_next_attr(struct aldap_message *msg, char **outkey,
611    struct aldap_stringset **outvalues)
612{
613	struct ber_element *a, *b;
614	char *key;
615	struct aldap_stringset *ret;
616
617	if (msg->body.search.iter == NULL)
618		goto notfound;
619
620	LDAP_DEBUG("attr", msg->body.search.iter);
621
622	if (ober_get_eoc(msg->body.search.iter) == 0)
623		goto notfound;
624
625	if (ober_scanf_elements(msg->body.search.iter, "{s(e)}e", &key, &a, &b)
626	    != 0)
627		goto fail;
628
629	msg->body.search.iter = msg->body.search.iter->be_next;
630
631	if ((ret = aldap_get_stringset(a)) == NULL)
632		goto fail;
633
634	(*outvalues) = ret;
635	(*outkey) = utoa(key);
636
637	return (1);
638fail:
639notfound:
640	(*outkey) = NULL;
641	(*outvalues) = NULL;
642	return (-1);
643}
644
645int
646aldap_match_attr(struct aldap_message *msg, char *inkey,
647    struct aldap_stringset **outvalues)
648{
649	struct ber_element *a, *b;
650	char *descr = NULL;
651	struct aldap_stringset *ret;
652
653	if (msg->body.search.attrs == NULL)
654		goto fail;
655
656	LDAP_DEBUG("attr", msg->body.search.attrs);
657
658	for (a = msg->body.search.attrs;;) {
659		if (a == NULL)
660			goto notfound;
661		if (ober_get_eoc(a) == 0)
662			goto notfound;
663		if (ober_scanf_elements(a, "{s(e", &descr, &b) != 0)
664			goto fail;
665		if (strcasecmp(descr, inkey) == 0)
666			goto attrfound;
667		a = a->be_next;
668	}
669
670attrfound:
671	if ((ret = aldap_get_stringset(b)) == NULL)
672		goto fail;
673
674	(*outvalues) = ret;
675
676	return (1);
677fail:
678notfound:
679	(*outvalues) = NULL;
680	return (-1);
681}
682
683int
684aldap_free_attr(struct aldap_stringset *values)
685{
686	if (values == NULL)
687		return -1;
688
689	free(values->str);
690	free(values);
691
692	return (1);
693}
694
695void
696aldap_free_url(struct aldap_url *lu)
697{
698	free(lu->buffer);
699}
700
701int
702aldap_parse_url(const char *url, struct aldap_url *lu)
703{
704	char		*p, *forward, *forward2;
705	const char	*errstr = NULL;
706	int		 i;
707
708	if ((lu->buffer = p = strdup(url)) == NULL)
709		return (-1);
710
711	/* protocol */
712	if (strncasecmp(LDAP_URL, p, strlen(LDAP_URL)) == 0) {
713		lu->protocol = LDAP;
714		p += strlen(LDAP_URL);
715	} else if (strncasecmp(LDAPS_URL, p, strlen(LDAPS_URL)) == 0) {
716		lu->protocol = LDAPS;
717		p += strlen(LDAPS_URL);
718	} else if (strncasecmp(LDAPTLS_URL, p, strlen(LDAPTLS_URL)) == 0) {
719		lu->protocol = LDAPTLS;
720		p += strlen(LDAPTLS_URL);
721	} else if (strncasecmp(LDAPI_URL, p, strlen(LDAPI_URL)) == 0) {
722		lu->protocol = LDAPI;
723		p += strlen(LDAPI_URL);
724	} else
725		lu->protocol = -1;
726
727	/* host and optional port */
728	if ((forward = strchr(p, '/')) != NULL)
729		*forward = '\0';
730	/* find the optional port */
731	if ((forward2 = strchr(p, ':')) != NULL) {
732		*forward2 = '\0';
733		/* if a port is given */
734		if (*(forward2+1) != '\0') {
735#define PORT_MAX UINT16_MAX
736			lu->port = strtonum(++forward2, 0, PORT_MAX, &errstr);
737			if (errstr)
738				goto fail;
739		}
740	}
741	/* fail if no host is given */
742	if (strlen(p) == 0)
743		goto fail;
744	lu->host = p;
745	if (forward == NULL)
746		goto done;
747	/* p is assigned either a pointer to a character or to '\0' */
748	p = ++forward;
749	if (strlen(p) == 0)
750		goto done;
751
752	/* dn */
753	if ((forward = strchr(p, '?')) != NULL)
754		*forward = '\0';
755	lu->dn = p;
756	if (forward == NULL)
757		goto done;
758	/* p is assigned either a pointer to a character or to '\0' */
759	p = ++forward;
760	if (strlen(p) == 0)
761		goto done;
762
763	/* attributes */
764	if ((forward = strchr(p, '?')) != NULL)
765		*forward = '\0';
766	for (i = 0; i < MAXATTR; i++) {
767		if ((forward2 = strchr(p, ',')) == NULL) {
768			if (strlen(p) == 0)
769				break;
770			lu->attributes[i] = p;
771			break;
772		}
773		*forward2 = '\0';
774		lu->attributes[i] = p;
775		p = ++forward2;
776	}
777	if (forward == NULL)
778		goto done;
779	/* p is assigned either a pointer to a character or to '\0' */
780	p = ++forward;
781	if (strlen(p) == 0)
782		goto done;
783
784	/* scope */
785	if ((forward = strchr(p, '?')) != NULL)
786		*forward = '\0';
787	if (strcmp(p, "base") == 0)
788		lu->scope = LDAP_SCOPE_BASE;
789	else if (strcmp(p, "one") == 0)
790		lu->scope = LDAP_SCOPE_ONELEVEL;
791	else if (strcmp(p, "sub") == 0)
792		lu->scope = LDAP_SCOPE_SUBTREE;
793	else
794		goto fail;
795	if (forward == NULL)
796		goto done;
797	p = ++forward;
798	if (strlen(p) == 0)
799		goto done;
800
801	/* filter */
802	if (p)
803		lu->filter = p;
804done:
805	return (1);
806fail:
807	free(lu->buffer);
808	lu->buffer = NULL;
809	return (-1);
810}
811
812int
813aldap_search_url(struct aldap *ldap, char *url, int typesonly, int sizelimit,
814    int timelimit, struct aldap_page_control *page)
815{
816	struct aldap_url *lu;
817
818	if ((lu = calloc(1, sizeof(*lu))) == NULL)
819		return (-1);
820
821	if (aldap_parse_url(url, lu))
822		goto fail;
823
824	if (aldap_search(ldap, lu->dn, lu->scope, lu->filter, lu->attributes,
825	    typesonly, sizelimit, timelimit, page) == -1)
826		goto fail;
827
828	aldap_free_url(lu);
829	return (ldap->msgid);
830fail:
831	aldap_free_url(lu);
832	return (-1);
833}
834
835/*
836 * internal functions
837 */
838
839struct aldap_stringset *
840aldap_get_stringset(struct ber_element *elm)
841{
842	struct ber_element *a;
843	int i;
844	struct aldap_stringset *ret;
845
846	if (elm->be_type != BER_TYPE_OCTETSTRING)
847		return NULL;
848
849	if ((ret = malloc(sizeof(*ret))) == NULL)
850		return NULL;
851	for (a = elm, ret->len = 0; a != NULL && a->be_type ==
852	    BER_TYPE_OCTETSTRING; a = a->be_next, ret->len++)
853		;
854	if (ret->len == 0) {
855		free(ret);
856		return NULL;
857	}
858
859	if ((ret->str = reallocarray(NULL, ret->len,
860	    sizeof(*(ret->str)))) == NULL) {
861		free(ret);
862		return NULL;
863	}
864
865	for (a = elm, i = 0; a != NULL && a->be_type == BER_TYPE_OCTETSTRING;
866	    a = a->be_next, i++)
867		(void) ober_get_ostring(a, &(ret->str[i]));
868
869	return ret;
870}
871
872/*
873 * Base case for ldap_do_parse_search_filter
874 *
875 * returns:
876 *	struct ber_element *, ber_element tree
877 *	NULL, parse failed
878 */
879static struct ber_element *
880ldap_parse_search_filter(struct ber_element *ber, char *filter)
881{
882	struct ber_element *elm;
883	char *cp;
884
885	cp = filter;
886
887	if (cp == NULL || *cp == '\0') {
888		errno = EINVAL;
889		return (NULL);
890	}
891
892	if ((elm = ldap_do_parse_search_filter(ber, &cp)) == NULL)
893		return (NULL);
894
895	if (*cp != '\0') {
896		ober_free_elements(elm);
897		ober_link_elements(ber, NULL);
898		errno = EINVAL;
899		return (NULL);
900	}
901
902	return (elm);
903}
904
905/*
906 * Translate RFC4515 search filter string into ber_element tree
907 *
908 * returns:
909 *	struct ber_element *, ber_element tree
910 *	NULL, parse failed
911 *
912 * notes:
913 *	when cp is passed to a recursive invocation, it is updated
914 *	    to point one character beyond the filter that was passed
915 *	    i.e., cp jumps to "(filter)" upon return
916 *	                               ^
917 *	goto's used to discriminate error-handling based on error type
918 *	doesn't handle extended filters (yet)
919 *
920 */
921static struct ber_element *
922ldap_do_parse_search_filter(struct ber_element *prev, char **cpp)
923{
924	struct ber_element *elm, *root = NULL;
925	char *attr_desc, *attr_val, *parsed_val, *cp;
926	size_t len;
927	unsigned long type;
928
929	root = NULL;
930
931	/* cpp should pass in pointer to opening parenthesis of "(filter)" */
932	cp = *cpp;
933	if (*cp != '(')
934		goto syntaxfail;
935
936	switch (*++cp) {
937	case '&':		/* AND */
938	case '|':		/* OR */
939		if (*cp == '&')
940			type = LDAP_FILT_AND;
941		else
942			type = LDAP_FILT_OR;
943
944		if ((elm = ober_add_set(prev)) == NULL)
945			goto callfail;
946		root = elm;
947		ober_set_header(elm, BER_CLASS_CONTEXT, type);
948
949		if (*++cp != '(')		/* opening `(` of filter */
950			goto syntaxfail;
951
952		while (*cp == '(') {
953			if ((elm =
954			    ldap_do_parse_search_filter(elm, &cp)) == NULL)
955				goto bad;
956		}
957
958		if (*cp != ')')			/* trailing `)` of filter */
959			goto syntaxfail;
960		break;
961
962	case '!':		/* NOT */
963		if ((root = ober_add_sequence(prev)) == NULL)
964			goto callfail;
965		ober_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_NOT);
966
967		cp++;				/* now points to sub-filter */
968		if ((elm = ldap_do_parse_search_filter(root, &cp)) == NULL)
969			goto bad;
970
971		if (*cp != ')')			/* trailing `)` of filter */
972			goto syntaxfail;
973		break;
974
975	default:	/* SIMPLE || PRESENCE */
976		attr_desc = cp;
977
978		len = strcspn(cp, "()<>~=");
979		cp += len;
980		switch (*cp) {
981		case '~':
982			type = LDAP_FILT_APPR;
983			cp++;
984			break;
985		case '<':
986			type = LDAP_FILT_LE;
987			cp++;
988			break;
989		case '>':
990			type = LDAP_FILT_GE;
991			cp++;
992			break;
993		case '=':
994			type = LDAP_FILT_EQ;	/* assume EQ until disproven */
995			break;
996		case '(':
997		case ')':
998		default:
999			goto syntaxfail;
1000		}
1001		attr_val = ++cp;
1002
1003		/* presence filter */
1004		if (strncmp(attr_val, "*)", 2) == 0) {
1005			cp++;			/* point to trailing `)` */
1006			if ((root =
1007			    ober_add_nstring(prev, attr_desc, len)) == NULL)
1008				goto bad;
1009
1010			ober_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_PRES);
1011			break;
1012		}
1013
1014		if ((root = ober_add_sequence(prev)) == NULL)
1015			goto callfail;
1016		ober_set_header(root, BER_CLASS_CONTEXT, type);
1017
1018		if ((elm = ober_add_nstring(root, attr_desc, len)) == NULL)
1019			goto callfail;
1020
1021		len = strcspn(attr_val, "*)");
1022		if (len == 0 && *cp != '*')
1023			goto syntaxfail;
1024		cp += len;
1025		if (*cp == '\0')
1026			goto syntaxfail;
1027
1028		if (*cp == '*') {	/* substring filter */
1029			int initial;
1030
1031			cp = attr_val;
1032
1033			ober_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_SUBS);
1034
1035			if ((elm = ober_add_sequence(elm)) == NULL)
1036				goto callfail;
1037
1038			for (initial = 1;; cp++, initial = 0) {
1039				attr_val = cp;
1040
1041				len = strcspn(attr_val, "*)");
1042				if (len == 0) {
1043					if (*cp == ')')
1044						break;
1045					else
1046						continue;
1047				}
1048				cp += len;
1049				if (*cp == '\0')
1050					goto syntaxfail;
1051
1052				if (initial)
1053					type = LDAP_FILT_SUBS_INIT;
1054				else if (*cp == ')')
1055					type = LDAP_FILT_SUBS_FIN;
1056				else
1057					type = LDAP_FILT_SUBS_ANY;
1058
1059				if ((parsed_val = parseval(attr_val, len)) ==
1060				    NULL)
1061					goto callfail;
1062				elm = ober_add_nstring(elm, parsed_val,
1063				    strlen(parsed_val));
1064				free(parsed_val);
1065				if (elm == NULL)
1066					goto callfail;
1067				ober_set_header(elm, BER_CLASS_CONTEXT, type);
1068				if (type == LDAP_FILT_SUBS_FIN)
1069					break;
1070			}
1071			break;
1072		}
1073
1074		if ((parsed_val = parseval(attr_val, len)) == NULL)
1075			goto callfail;
1076		elm = ober_add_nstring(elm, parsed_val, strlen(parsed_val));
1077		free(parsed_val);
1078		if (elm == NULL)
1079			goto callfail;
1080		break;
1081	}
1082
1083	cp++;		/* now points one char beyond the trailing `)` */
1084
1085	*cpp = cp;
1086	return (root);
1087
1088syntaxfail:		/* XXX -- error reporting */
1089callfail:
1090bad:
1091	if (root != NULL)
1092		ober_free_elements(root);
1093	ober_link_elements(prev, NULL);
1094	return (NULL);
1095}
1096
1097#ifdef DEBUG
1098/*
1099 * Display a list of ber elements.
1100 *
1101 */
1102void
1103ldap_debug_elements(struct ber_element *root)
1104{
1105	static int	 indent = 0;
1106	long long	 v;
1107	int		 d;
1108	char		*buf;
1109	size_t		 len;
1110	u_int		 i;
1111	int		 constructed;
1112	struct ber_oid	 o;
1113
1114	/* calculate lengths */
1115	ober_calc_len(root);
1116
1117	switch (root->be_encoding) {
1118	case BER_TYPE_SEQUENCE:
1119	case BER_TYPE_SET:
1120		constructed = root->be_encoding;
1121		break;
1122	default:
1123		constructed = 0;
1124		break;
1125	}
1126
1127	fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
1128	switch (root->be_class) {
1129	case BER_CLASS_UNIVERSAL:
1130		fprintf(stderr, "class: universal(%u) type: ", root->be_class);
1131		switch (root->be_type) {
1132		case BER_TYPE_EOC:
1133			fprintf(stderr, "end-of-content");
1134			break;
1135		case BER_TYPE_BOOLEAN:
1136			fprintf(stderr, "boolean");
1137			break;
1138		case BER_TYPE_INTEGER:
1139			fprintf(stderr, "integer");
1140			break;
1141		case BER_TYPE_BITSTRING:
1142			fprintf(stderr, "bit-string");
1143			break;
1144		case BER_TYPE_OCTETSTRING:
1145			fprintf(stderr, "octet-string");
1146			break;
1147		case BER_TYPE_NULL:
1148			fprintf(stderr, "null");
1149			break;
1150		case BER_TYPE_OBJECT:
1151			fprintf(stderr, "object");
1152			break;
1153		case BER_TYPE_ENUMERATED:
1154			fprintf(stderr, "enumerated");
1155			break;
1156		case BER_TYPE_SEQUENCE:
1157			fprintf(stderr, "sequence");
1158			break;
1159		case BER_TYPE_SET:
1160			fprintf(stderr, "set");
1161			break;
1162		}
1163		break;
1164	case BER_CLASS_APPLICATION:
1165		fprintf(stderr, "class: application(%u) type: ",
1166		    root->be_class);
1167		switch (root->be_type) {
1168		case LDAP_REQ_BIND:
1169			fprintf(stderr, "bind");
1170			break;
1171		case LDAP_RES_BIND:
1172			fprintf(stderr, "bind");
1173			break;
1174		case LDAP_REQ_UNBIND_30:
1175			break;
1176		case LDAP_REQ_SEARCH:
1177			fprintf(stderr, "search");
1178			break;
1179		case LDAP_RES_SEARCH_ENTRY:
1180			fprintf(stderr, "search_entry");
1181			break;
1182		case LDAP_RES_SEARCH_RESULT:
1183			fprintf(stderr, "search_result");
1184			break;
1185		case LDAP_REQ_MODIFY:
1186			fprintf(stderr, "modify");
1187			break;
1188		case LDAP_RES_MODIFY:
1189			fprintf(stderr, "modify");
1190			break;
1191		case LDAP_REQ_ADD:
1192			fprintf(stderr, "add");
1193			break;
1194		case LDAP_RES_ADD:
1195			fprintf(stderr, "add");
1196			break;
1197		case LDAP_REQ_DELETE_30:
1198			fprintf(stderr, "delete");
1199			break;
1200		case LDAP_RES_DELETE:
1201			fprintf(stderr, "delete");
1202			break;
1203		case LDAP_REQ_MODRDN:
1204			fprintf(stderr, "modrdn");
1205			break;
1206		case LDAP_RES_MODRDN:
1207			fprintf(stderr, "modrdn");
1208			break;
1209		case LDAP_REQ_COMPARE:
1210			fprintf(stderr, "compare");
1211			break;
1212		case LDAP_RES_COMPARE:
1213			fprintf(stderr, "compare");
1214			break;
1215		case LDAP_REQ_ABANDON_30:
1216			fprintf(stderr, "abandon");
1217			break;
1218		}
1219		break;
1220	case BER_CLASS_PRIVATE:
1221		fprintf(stderr, "class: private(%u) type: ", root->be_class);
1222		fprintf(stderr, "encoding (%u) type: ", root->be_encoding);
1223		break;
1224	case BER_CLASS_CONTEXT:
1225		/* XXX: this is not correct */
1226		fprintf(stderr, "class: context(%u) type: ", root->be_class);
1227		switch(root->be_type) {
1228		case LDAP_AUTH_SIMPLE:
1229			fprintf(stderr, "auth simple");
1230			break;
1231		}
1232		break;
1233	default:
1234		fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
1235		break;
1236	}
1237	fprintf(stderr, "(%u) encoding %u ",
1238	    root->be_type, root->be_encoding);
1239
1240	if (constructed)
1241		root->be_encoding = constructed;
1242
1243	switch (root->be_encoding) {
1244	case BER_TYPE_BOOLEAN:
1245		if (ober_get_boolean(root, &d) == -1) {
1246			fprintf(stderr, "<INVALID>\n");
1247			break;
1248		}
1249		fprintf(stderr, "%s(%d)\n", d ? "true" : "false", d);
1250		break;
1251	case BER_TYPE_INTEGER:
1252		if (ober_get_integer(root, &v) == -1) {
1253			fprintf(stderr, "<INVALID>\n");
1254			break;
1255		}
1256		fprintf(stderr, "value %lld\n", v);
1257		break;
1258	case BER_TYPE_ENUMERATED:
1259		if (ober_get_enumerated(root, &v) == -1) {
1260			fprintf(stderr, "<INVALID>\n");
1261			break;
1262		}
1263		fprintf(stderr, "value %lld\n", v);
1264		break;
1265	case BER_TYPE_BITSTRING:
1266		if (ober_get_bitstring(root, (void *)&buf, &len) == -1) {
1267			fprintf(stderr, "<INVALID>\n");
1268			break;
1269		}
1270		fprintf(stderr, "hexdump ");
1271		for (i = 0; i < len; i++)
1272			fprintf(stderr, "%02x", buf[i]);
1273		fprintf(stderr, "\n");
1274		break;
1275	case BER_TYPE_OBJECT:
1276		if (ober_get_oid(root, &o) == -1) {
1277			fprintf(stderr, "<INVALID>\n");
1278			break;
1279		}
1280		fprintf(stderr, "\n");
1281		break;
1282	case BER_TYPE_OCTETSTRING:
1283		if (ober_get_nstring(root, (void *)&buf, &len) == -1) {
1284			fprintf(stderr, "<INVALID>\n");
1285			break;
1286		}
1287		fprintf(stderr, "string \"%.*s\"\n",  (int)len, buf);
1288		break;
1289	case BER_TYPE_NULL:	/* no payload */
1290	case BER_TYPE_EOC:
1291	case BER_TYPE_SEQUENCE:
1292	case BER_TYPE_SET:
1293	default:
1294		fprintf(stderr, "\n");
1295		break;
1296	}
1297
1298	if (constructed && root->be_sub) {
1299		indent += 2;
1300		ldap_debug_elements(root->be_sub);
1301		indent -= 2;
1302	}
1303	if (root->be_next)
1304		ldap_debug_elements(root->be_next);
1305}
1306#endif
1307
1308/*
1309 * Strip UTF-8 down to ASCII without validation.
1310 * notes:
1311 *	non-ASCII characters are displayed as '?'
1312 *	the argument u should be a NULL terminated sequence of UTF-8 bytes.
1313 */
1314char *
1315utoa(char *u)
1316{
1317	int	 len, i, j;
1318	char	*str;
1319
1320	/* calculate the length to allocate */
1321	for (len = 0, i = 0; u[i] != '\0'; i++)
1322		if (!isu8cont(u[i]))
1323			len++;
1324
1325	if ((str = calloc(len + 1, sizeof(char))) == NULL)
1326		return NULL;
1327
1328	/* copy the ASCII characters to the newly allocated string */
1329	for (i = 0, j = 0; u[i] != '\0'; i++)
1330		if (!isu8cont(u[i]))
1331			str[j++] = isascii((unsigned char)u[i]) ? u[i] : '?';
1332
1333	return str;
1334}
1335
1336static int
1337isu8cont(unsigned char c)
1338{
1339	return (c & (0x80 | 0x40)) == 0x80;
1340}
1341
1342/*
1343 * Parse a LDAP value
1344 * notes:
1345 *	the argument p should be a NUL-terminated sequence of ASCII bytes
1346 */
1347char *
1348parseval(char *p, size_t len)
1349{
1350	char	 hex[3];
1351	char	*buffer;
1352	size_t	 i, j;
1353
1354	if ((buffer = calloc(1, len + 1)) == NULL)
1355		return NULL;
1356
1357	for (i = j = 0; j < len; i++) {
1358		if (p[j] == '\\') {
1359			strlcpy(hex, p + j + 1, sizeof(hex));
1360			buffer[i] = (char)strtoumax(hex, NULL, 16);
1361			j += 3;
1362		} else {
1363			buffer[i] = p[j];
1364			j++;
1365		}
1366	}
1367
1368	return buffer;
1369}
1370
1371int
1372aldap_get_errno(struct aldap *a, const char **estr)
1373{
1374	switch (a->err) {
1375	case ALDAP_ERR_SUCCESS:
1376		*estr = "success";
1377		break;
1378	case ALDAP_ERR_PARSER_ERROR:
1379		*estr = "parser failed";
1380		break;
1381	case ALDAP_ERR_INVALID_FILTER:
1382		*estr = "invalid filter";
1383		break;
1384	case ALDAP_ERR_OPERATION_FAILED:
1385		*estr = "operation failed";
1386		break;
1387	case ALDAP_ERR_TLS_ERROR:
1388		*estr = tls_error(a->tls);
1389		break;
1390	default:
1391		*estr = "unknown";
1392		break;
1393	}
1394	return (a->err);
1395}
1396