xref: /openbsd/usr.bin/ldap/aldap.c (revision 0ce1f88d)
1*0ce1f88dSmartijn /*	$OpenBSD: aldap.c,v 1.10 2022/03/31 09:03:48 martijn Exp $ */
29107066aSreyk 
39107066aSreyk /*
49107066aSreyk  * Copyright (c) 2008 Alexander Schrijver <aschrijver@openbsd.org>
59107066aSreyk  * Copyright (c) 2006, 2007 Marc Balmer <mbalmer@openbsd.org>
69107066aSreyk  *
79107066aSreyk  * Permission to use, copy, modify, and distribute this software for any
89107066aSreyk  * purpose with or without fee is hereby granted, provided that the above
99107066aSreyk  * copyright notice and this permission notice appear in all copies.
109107066aSreyk  *
119107066aSreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
129107066aSreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
139107066aSreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
149107066aSreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
159107066aSreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
169107066aSreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
179107066aSreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
189107066aSreyk  */
199107066aSreyk 
209107066aSreyk #include <arpa/inet.h>
219107066aSreyk #include <ctype.h>
229107066aSreyk #include <errno.h>
239107066aSreyk #include <inttypes.h>
249107066aSreyk #include <string.h>
259107066aSreyk #include <stdlib.h>
269107066aSreyk #include <unistd.h>
279107066aSreyk 
289107066aSreyk #include <event.h>
299107066aSreyk 
309107066aSreyk #include "aldap.h"
319107066aSreyk 
329107066aSreyk #if 0
339107066aSreyk #define DEBUG
349107066aSreyk #endif
359107066aSreyk #define VERSION 3
369107066aSreyk 
379107066aSreyk static struct ber_element	*ldap_parse_search_filter(struct ber_element *,
389107066aSreyk 				    char *);
399107066aSreyk static struct ber_element	*ldap_do_parse_search_filter(
409107066aSreyk 				    struct ber_element *, char **);
412f59fca6Smartijn struct aldap_stringset		*aldap_get_stringset(struct ber_element *);
429107066aSreyk char				*utoa(char *);
439107066aSreyk static int			 isu8cont(unsigned char);
449107066aSreyk char				*parseval(char *, size_t);
459107066aSreyk int				aldap_create_page_control(struct ber_element *,
469107066aSreyk 				    int, struct aldap_page_control *);
479107066aSreyk int				aldap_send(struct aldap *,
489107066aSreyk 				    struct ber_element *);
4951523eb3Sclaudio unsigned int			aldap_application(struct ber_element *);
509107066aSreyk 
519107066aSreyk #ifdef DEBUG
529107066aSreyk void			 ldap_debug_elements(struct ber_element *);
539107066aSreyk #endif
549107066aSreyk 
559107066aSreyk #ifdef DEBUG
569107066aSreyk #define DPRINTF(x...)	printf(x)
579107066aSreyk #define LDAP_DEBUG(x, y)	do { fprintf(stderr, "*** " x "\n"); ldap_debug_elements(y); } while (0)
589107066aSreyk #else
599107066aSreyk #define DPRINTF(x...)	do { } while (0)
609107066aSreyk #define LDAP_DEBUG(x, y)	do { } while (0)
619107066aSreyk #endif
629107066aSreyk 
6351523eb3Sclaudio unsigned int
aldap_application(struct ber_element * elm)649107066aSreyk aldap_application(struct ber_element *elm)
659107066aSreyk {
669107066aSreyk 	return BER_TYPE_OCTETSTRING;
679107066aSreyk }
689107066aSreyk 
699107066aSreyk int
aldap_close(struct aldap * al)709107066aSreyk aldap_close(struct aldap *al)
719107066aSreyk {
729107066aSreyk 	if (al->tls != NULL) {
739107066aSreyk 		tls_close(al->tls);
749107066aSreyk 		tls_free(al->tls);
759107066aSreyk 	}
769107066aSreyk 	close(al->fd);
77696b5899Stb 	ober_free(&al->ber);
789107066aSreyk 	evbuffer_free(al->buf);
799107066aSreyk 	free(al);
809107066aSreyk 
819107066aSreyk 	return (0);
829107066aSreyk }
839107066aSreyk 
849107066aSreyk struct aldap *
aldap_init(int fd)859107066aSreyk aldap_init(int fd)
869107066aSreyk {
879107066aSreyk 	struct aldap *a;
889107066aSreyk 
899107066aSreyk 	if ((a = calloc(1, sizeof(*a))) == NULL)
909107066aSreyk 		return NULL;
919107066aSreyk 	a->buf = evbuffer_new();
929107066aSreyk 	a->fd = fd;
93696b5899Stb 	ober_set_application(&a->ber, aldap_application);
949107066aSreyk 
959107066aSreyk 	return a;
969107066aSreyk }
979107066aSreyk 
989107066aSreyk int
aldap_tls(struct aldap * ldap,struct tls_config * cfg,const char * name)999107066aSreyk aldap_tls(struct aldap *ldap, struct tls_config *cfg, const char *name)
1009107066aSreyk {
1019107066aSreyk 	ldap->tls = tls_client();
1029107066aSreyk 	if (ldap->tls == NULL) {
1039107066aSreyk 		ldap->err = ALDAP_ERR_OPERATION_FAILED;
1049107066aSreyk 		return (-1);
1059107066aSreyk 	}
1069107066aSreyk 
1079107066aSreyk 	if (tls_configure(ldap->tls, cfg) == -1) {
1089107066aSreyk 		ldap->err = ALDAP_ERR_TLS_ERROR;
1099107066aSreyk 		return (-1);
1109107066aSreyk 	}
1119107066aSreyk 
1129107066aSreyk 	if (tls_connect_socket(ldap->tls, ldap->fd, name) == -1) {
1139107066aSreyk 		ldap->err = ALDAP_ERR_TLS_ERROR;
1149107066aSreyk 		return (-1);
1159107066aSreyk 	}
1169107066aSreyk 
1179107066aSreyk 	if (tls_handshake(ldap->tls) == -1) {
1189107066aSreyk 		ldap->err = ALDAP_ERR_TLS_ERROR;
1199107066aSreyk 		return (-1);
1209107066aSreyk 	}
1219107066aSreyk 
1229107066aSreyk 	return (0);
1239107066aSreyk }
1249107066aSreyk 
1259107066aSreyk int
aldap_send(struct aldap * ldap,struct ber_element * root)1269107066aSreyk aldap_send(struct aldap *ldap, struct ber_element *root)
1279107066aSreyk {
1289107066aSreyk 	void *ptr;
1299107066aSreyk 	char *data;
1309107066aSreyk 	size_t len, done;
13173774627Srob 	ssize_t error, wrote;
1329107066aSreyk 
133696b5899Stb 	len = ober_calc_len(root);
134696b5899Stb 	error = ober_write_elements(&ldap->ber, root);
135696b5899Stb 	ober_free_elements(root);
1369107066aSreyk 	if (error == -1)
1379107066aSreyk 		return -1;
1389107066aSreyk 
139696b5899Stb 	ober_get_writebuf(&ldap->ber, &ptr);
1409107066aSreyk 	done = 0;
1419107066aSreyk 	data = ptr;
1429107066aSreyk 	while (len > 0) {
1439107066aSreyk 		if (ldap->tls != NULL) {
1449107066aSreyk 			wrote = tls_write(ldap->tls, data + done, len);
1459107066aSreyk 			if (wrote == TLS_WANT_POLLIN ||
1469107066aSreyk 			    wrote == TLS_WANT_POLLOUT)
1479107066aSreyk 				continue;
1489107066aSreyk 		} else
1499107066aSreyk 			wrote = write(ldap->fd, data + done, len);
1509107066aSreyk 
1519107066aSreyk 		if (wrote == -1)
1529107066aSreyk 			return -1;
1539107066aSreyk 
1549107066aSreyk 		len -= wrote;
1559107066aSreyk 		done += wrote;
1569107066aSreyk 	}
1579107066aSreyk 
1589107066aSreyk 	return 0;
1599107066aSreyk }
1609107066aSreyk 
1619107066aSreyk int
aldap_req_starttls(struct aldap * ldap)1629107066aSreyk aldap_req_starttls(struct aldap *ldap)
1639107066aSreyk {
1649107066aSreyk 	struct ber_element *root = NULL, *ber;
1659107066aSreyk 
166696b5899Stb 	if ((root = ober_add_sequence(NULL)) == NULL)
1679107066aSreyk 		goto fail;
1689107066aSreyk 
169696b5899Stb 	ber = ober_printf_elements(root, "d{tst", ++ldap->msgid, BER_CLASS_APP,
17051523eb3Sclaudio 	    LDAP_REQ_EXTENDED, LDAP_STARTTLS_OID, BER_CLASS_CONTEXT, 0);
1719107066aSreyk 	if (ber == NULL) {
1729107066aSreyk 		ldap->err = ALDAP_ERR_OPERATION_FAILED;
1739107066aSreyk 		goto fail;
1749107066aSreyk 	}
1759107066aSreyk 
1769107066aSreyk 	if (aldap_send(ldap, root) == -1)
1779107066aSreyk 		goto fail;
1789107066aSreyk 
1799107066aSreyk 	return (ldap->msgid);
1809107066aSreyk fail:
1819107066aSreyk 	if (root != NULL)
182696b5899Stb 		ober_free_elements(root);
1839107066aSreyk 
1849107066aSreyk 	ldap->err = ALDAP_ERR_OPERATION_FAILED;
1859107066aSreyk 	return (-1);
1869107066aSreyk }
1879107066aSreyk 
1889107066aSreyk int
aldap_bind(struct aldap * ldap,char * binddn,char * bindcred)1899107066aSreyk aldap_bind(struct aldap *ldap, char *binddn, char *bindcred)
1909107066aSreyk {
1919107066aSreyk 	struct ber_element *root = NULL, *elm;
1929107066aSreyk 
1939107066aSreyk 	if (binddn == NULL)
1949107066aSreyk 		binddn = "";
1959107066aSreyk 	if (bindcred == NULL)
1969107066aSreyk 		bindcred = "";
1979107066aSreyk 
198696b5899Stb 	if ((root = ober_add_sequence(NULL)) == NULL)
1999107066aSreyk 		goto fail;
2009107066aSreyk 
201696b5899Stb 	elm = ober_printf_elements(root, "d{tdsst", ++ldap->msgid, BER_CLASS_APP,
20251523eb3Sclaudio 	    LDAP_REQ_BIND, VERSION, binddn, bindcred, BER_CLASS_CONTEXT,
20351523eb3Sclaudio 	    LDAP_AUTH_SIMPLE);
2049107066aSreyk 	if (elm == NULL)
2059107066aSreyk 		goto fail;
2069107066aSreyk 
2079107066aSreyk 	LDAP_DEBUG("aldap_bind", root);
2089107066aSreyk 
2099107066aSreyk 	if (aldap_send(ldap, root) == -1) {
2109107066aSreyk 		root = NULL;
2119107066aSreyk 		goto fail;
2129107066aSreyk 	}
2139107066aSreyk 	return (ldap->msgid);
2149107066aSreyk fail:
2159107066aSreyk 	if (root != NULL)
216696b5899Stb 		ober_free_elements(root);
2179107066aSreyk 
2189107066aSreyk 	ldap->err = ALDAP_ERR_OPERATION_FAILED;
2199107066aSreyk 	return (-1);
2209107066aSreyk }
2219107066aSreyk 
2229107066aSreyk int
aldap_unbind(struct aldap * ldap)2239107066aSreyk aldap_unbind(struct aldap *ldap)
2249107066aSreyk {
2259107066aSreyk 	struct ber_element *root = NULL, *elm;
2269107066aSreyk 
227696b5899Stb 	if ((root = ober_add_sequence(NULL)) == NULL)
2289107066aSreyk 		goto fail;
229696b5899Stb 	elm = ober_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP,
2309107066aSreyk 	    LDAP_REQ_UNBIND_30);
2319107066aSreyk 	if (elm == NULL)
2329107066aSreyk 		goto fail;
2339107066aSreyk 
2349107066aSreyk 	LDAP_DEBUG("aldap_unbind", root);
2359107066aSreyk 
2369107066aSreyk 	if (aldap_send(ldap, root) == -1) {
2379107066aSreyk 		root = NULL;
2389107066aSreyk 		goto fail;
2399107066aSreyk 	}
2409107066aSreyk 	return (ldap->msgid);
2419107066aSreyk fail:
2429107066aSreyk 	if (root != NULL)
243696b5899Stb 		ober_free_elements(root);
2449107066aSreyk 
2459107066aSreyk 	ldap->err = ALDAP_ERR_OPERATION_FAILED;
2469107066aSreyk 
2479107066aSreyk 	return (-1);
2489107066aSreyk }
2499107066aSreyk 
2509107066aSreyk int
aldap_search(struct aldap * ldap,char * basedn,enum scope scope,char * filter,char ** attrs,int typesonly,int sizelimit,int timelimit,struct aldap_page_control * page)2519107066aSreyk aldap_search(struct aldap *ldap, char *basedn, enum scope scope, char *filter,
2529107066aSreyk     char **attrs, int typesonly, int sizelimit, int timelimit,
2539107066aSreyk     struct aldap_page_control *page)
2549107066aSreyk {
2559107066aSreyk 	struct ber_element *root = NULL, *ber, *c;
2569107066aSreyk 	int i;
2579107066aSreyk 
258696b5899Stb 	if ((root = ober_add_sequence(NULL)) == NULL)
2599107066aSreyk 		goto fail;
2609107066aSreyk 
261696b5899Stb 	ber = ober_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP,
26251523eb3Sclaudio 	    LDAP_REQ_SEARCH);
2639107066aSreyk 	if (ber == NULL) {
2649107066aSreyk 		ldap->err = ALDAP_ERR_OPERATION_FAILED;
2659107066aSreyk 		goto fail;
2669107066aSreyk 	}
2679107066aSreyk 
2689107066aSreyk 	c = ber;
269696b5899Stb 	ber = ober_printf_elements(ber, "sEEddb", basedn, (long long)scope,
2709107066aSreyk 	                         (long long)LDAP_DEREF_NEVER, sizelimit,
2719107066aSreyk 				 timelimit, typesonly);
2729107066aSreyk 	if (ber == NULL) {
2739107066aSreyk 		ldap->err = ALDAP_ERR_OPERATION_FAILED;
2749107066aSreyk 		goto fail;
2759107066aSreyk 	}
2769107066aSreyk 
2779107066aSreyk 	if ((ber = ldap_parse_search_filter(ber, filter)) == NULL) {
2789107066aSreyk 		ldap->err = ALDAP_ERR_PARSER_ERROR;
2799107066aSreyk 		goto fail;
2809107066aSreyk 	}
2819107066aSreyk 
282696b5899Stb 	if ((ber = ober_add_sequence(ber)) == NULL)
2839107066aSreyk 		goto fail;
2849107066aSreyk 	if (attrs != NULL)
2859107066aSreyk 		for (i = 0; attrs[i] != NULL; i++) {
286696b5899Stb 			if ((ber = ober_add_string(ber, attrs[i])) == NULL)
2879107066aSreyk 				goto fail;
2889107066aSreyk 		}
2899107066aSreyk 
2909107066aSreyk 	aldap_create_page_control(c, 100, page);
2919107066aSreyk 
2929107066aSreyk 	LDAP_DEBUG("aldap_search", root);
2939107066aSreyk 
2949107066aSreyk 	if (aldap_send(ldap, root) == -1) {
2959107066aSreyk 		root = NULL;
2969107066aSreyk 		ldap->err = ALDAP_ERR_OPERATION_FAILED;
2979107066aSreyk 		goto fail;
2989107066aSreyk 	}
2999107066aSreyk 
3009107066aSreyk 	return (ldap->msgid);
3019107066aSreyk 
3029107066aSreyk fail:
3039107066aSreyk 	if (root != NULL)
304696b5899Stb 		ober_free_elements(root);
3059107066aSreyk 
3069107066aSreyk 	return (-1);
3079107066aSreyk }
3089107066aSreyk 
3099107066aSreyk int
aldap_create_page_control(struct ber_element * elm,int size,struct aldap_page_control * page)3109107066aSreyk aldap_create_page_control(struct ber_element *elm, int size,
3119107066aSreyk     struct aldap_page_control *page)
3129107066aSreyk {
31373774627Srob 	ssize_t len;
3149107066aSreyk 	struct ber c;
3159107066aSreyk 	struct ber_element *ber = NULL;
3169107066aSreyk 
3179107066aSreyk 	c.br_wbuf = NULL;
3189107066aSreyk 
319696b5899Stb 	ber = ober_add_sequence(NULL);
3209107066aSreyk 
3219107066aSreyk 	if (page == NULL) {
322696b5899Stb 		if (ober_printf_elements(ber, "ds", 50, "") == NULL)
3239107066aSreyk 			goto fail;
3249107066aSreyk 	} else {
325696b5899Stb 		if (ober_printf_elements(ber, "dx", 50, page->cookie,
3269107066aSreyk 			    page->cookie_len) == NULL)
3279107066aSreyk 			goto fail;
3289107066aSreyk 	}
3299107066aSreyk 
330696b5899Stb 	if ((len = ober_write_elements(&c, ber)) < 1)
3319107066aSreyk 		goto fail;
332696b5899Stb 	if (ober_printf_elements(elm, "{t{sx", 2, 0, LDAP_PAGED_OID,
3339107066aSreyk 		                c.br_wbuf, (size_t)len) == NULL)
3349107066aSreyk 		goto fail;
3359107066aSreyk 
336696b5899Stb 	ober_free_elements(ber);
337696b5899Stb 	ober_free(&c);
3389107066aSreyk 	return len;
3399107066aSreyk fail:
3409107066aSreyk 	if (ber != NULL)
341696b5899Stb 		ober_free_elements(ber);
342696b5899Stb 	ober_free(&c);
3439107066aSreyk 
3449107066aSreyk 	return (-1);
3459107066aSreyk }
3469107066aSreyk 
3479107066aSreyk struct aldap_message *
aldap_parse(struct aldap * ldap)3489107066aSreyk aldap_parse(struct aldap *ldap)
3499107066aSreyk {
3509107066aSreyk 	int			 class;
35151523eb3Sclaudio 	unsigned int		 type;
3529107066aSreyk 	long long		 msgid = 0;
3539107066aSreyk 	struct aldap_message	*m;
3549107066aSreyk 	struct ber_element	*a = NULL, *ep;
3559107066aSreyk 	char			 rbuf[512];
3569107066aSreyk 	int			 ret, retry;
3579107066aSreyk 
3589107066aSreyk 	if ((m = calloc(1, sizeof(struct aldap_message))) == NULL)
3599107066aSreyk 		return NULL;
3609107066aSreyk 
3619107066aSreyk 	retry = 0;
3629107066aSreyk 	while (m->msg == NULL) {
3639107066aSreyk 		if (retry || EVBUFFER_LENGTH(ldap->buf) == 0) {
3649107066aSreyk 			if (ldap->tls) {
3659107066aSreyk 				ret = tls_read(ldap->tls, rbuf, sizeof(rbuf));
3669107066aSreyk 				if (ret == TLS_WANT_POLLIN ||
3679107066aSreyk 				    ret == TLS_WANT_POLLOUT)
3689107066aSreyk 					continue;
3699107066aSreyk 			} else
3709107066aSreyk 				ret = read(ldap->fd, rbuf, sizeof(rbuf));
3719107066aSreyk 
3729107066aSreyk 			if (ret == -1) {
3739107066aSreyk 				goto parsefail;
3749107066aSreyk 			}
3759107066aSreyk 
3769107066aSreyk 			evbuffer_add(ldap->buf, rbuf, ret);
3779107066aSreyk 		}
3789107066aSreyk 
3799107066aSreyk 		if (EVBUFFER_LENGTH(ldap->buf) > 0) {
380696b5899Stb 			ober_set_readbuf(&ldap->ber, EVBUFFER_DATA(ldap->buf),
3819107066aSreyk 			    EVBUFFER_LENGTH(ldap->buf));
3829107066aSreyk 			errno = 0;
383696b5899Stb 			m->msg = ober_read_elements(&ldap->ber, NULL);
3849107066aSreyk 			if (errno != 0 && errno != ECANCELED) {
3859107066aSreyk 				goto parsefail;
3869107066aSreyk 			}
3879107066aSreyk 
3889107066aSreyk 			retry = 1;
3899107066aSreyk 		}
3909107066aSreyk 	}
3919107066aSreyk 
3929107066aSreyk 	evbuffer_drain(ldap->buf, ldap->ber.br_rptr - ldap->ber.br_rbuf);
3939107066aSreyk 
3949107066aSreyk 	LDAP_DEBUG("message", m->msg);
3959107066aSreyk 
396696b5899Stb 	if (ober_scanf_elements(m->msg, "{ite", &msgid, &class, &type, &a) != 0)
3979107066aSreyk 		goto parsefail;
3989107066aSreyk 	m->msgid = msgid;
3999107066aSreyk 	m->message_type = type;
4009107066aSreyk 	m->protocol_op = a;
4019107066aSreyk 
4029107066aSreyk 	switch (m->message_type) {
4039107066aSreyk 	case LDAP_RES_BIND:
4049107066aSreyk 	case LDAP_RES_MODIFY:
4059107066aSreyk 	case LDAP_RES_ADD:
4069107066aSreyk 	case LDAP_RES_DELETE:
4079107066aSreyk 	case LDAP_RES_MODRDN:
4089107066aSreyk 	case LDAP_RES_COMPARE:
4099107066aSreyk 	case LDAP_RES_SEARCH_RESULT:
410696b5899Stb 		if (ober_scanf_elements(m->protocol_op, "{EeSe",
41192748799Smartijn 		    &m->body.res.rescode, &m->dn, &m->body.res.diagmsg) != 0)
4129107066aSreyk 			goto parsefail;
41392748799Smartijn 		if (m->body.res.rescode == LDAP_REFERRAL) {
41492748799Smartijn 			a = m->body.res.diagmsg->be_next;
415696b5899Stb 			if (ober_scanf_elements(a, "{e", &m->references) != 0)
4169107066aSreyk 				goto parsefail;
41792748799Smartijn 		}
4189107066aSreyk 		if (m->msg->be_sub) {
4199107066aSreyk 			for (ep = m->msg->be_sub; ep != NULL; ep = ep->be_next) {
420696b5899Stb 				ober_scanf_elements(ep, "t", &class, &type);
4219107066aSreyk 				if (class == 2 && type == 0)
4229107066aSreyk 					m->page = aldap_parse_page_control(ep->be_sub->be_sub,
4239107066aSreyk 					    ep->be_sub->be_sub->be_len);
4249107066aSreyk 			}
4259107066aSreyk 		} else
4269107066aSreyk 			m->page = NULL;
4279107066aSreyk 		break;
4289107066aSreyk 	case LDAP_RES_SEARCH_ENTRY:
429696b5899Stb 		if (ober_scanf_elements(m->protocol_op, "{eS{e", &m->dn,
4309107066aSreyk 		    &m->body.search.attrs) != 0)
4319107066aSreyk 			goto parsefail;
4329107066aSreyk 		break;
4339107066aSreyk 	case LDAP_RES_SEARCH_REFERENCE:
434696b5899Stb 		if (ober_scanf_elements(m->protocol_op, "{e", &m->references) != 0)
4359107066aSreyk 			goto parsefail;
4369107066aSreyk 		break;
4379107066aSreyk 	case LDAP_RES_EXTENDED:
438696b5899Stb 		if (ober_scanf_elements(m->protocol_op, "{E",
4399107066aSreyk 		    &m->body.res.rescode) != 0) {
4409107066aSreyk 			goto parsefail;
4419107066aSreyk 		}
4429107066aSreyk 		break;
4439107066aSreyk 	}
4449107066aSreyk 
4459107066aSreyk 	return m;
4469107066aSreyk parsefail:
4479107066aSreyk 	evbuffer_drain(ldap->buf, EVBUFFER_LENGTH(ldap->buf));
4489107066aSreyk 	ldap->err = ALDAP_ERR_PARSER_ERROR;
4499107066aSreyk 	aldap_freemsg(m);
4509107066aSreyk 	return NULL;
4519107066aSreyk }
4529107066aSreyk 
4539107066aSreyk struct aldap_page_control *
aldap_parse_page_control(struct ber_element * control,size_t len)4549107066aSreyk aldap_parse_page_control(struct ber_element *control, size_t len)
4559107066aSreyk {
4569107066aSreyk 	char *oid, *s;
4579107066aSreyk 	char *encoded;
4589107066aSreyk 	struct ber b;
4599107066aSreyk 	struct ber_element *elm;
4609107066aSreyk 	struct aldap_page_control *page;
4619107066aSreyk 
4629107066aSreyk 	b.br_wbuf = NULL;
463696b5899Stb 	ober_scanf_elements(control, "ss", &oid, &encoded);
464696b5899Stb 	ober_set_readbuf(&b, encoded, control->be_next->be_len);
465696b5899Stb 	elm = ober_read_elements(&b, NULL);
4669107066aSreyk 
4679107066aSreyk 	if ((page = malloc(sizeof(struct aldap_page_control))) == NULL) {
4689107066aSreyk 		if (elm != NULL)
469696b5899Stb 			ober_free_elements(elm);
470696b5899Stb 		ober_free(&b);
4719107066aSreyk 		return NULL;
4729107066aSreyk 	}
4739107066aSreyk 
474696b5899Stb 	ober_scanf_elements(elm->be_sub, "is", &page->size, &s);
4759107066aSreyk 	page->cookie_len = elm->be_sub->be_next->be_len;
4769107066aSreyk 
4779107066aSreyk 	if ((page->cookie = malloc(page->cookie_len)) == NULL) {
4789107066aSreyk 		if (elm != NULL)
479696b5899Stb 			ober_free_elements(elm);
480696b5899Stb 		ober_free(&b);
4819107066aSreyk 		free(page);
4829107066aSreyk 		return NULL;
4839107066aSreyk 	}
4849107066aSreyk 	memcpy(page->cookie, s, page->cookie_len);
4859107066aSreyk 
486696b5899Stb 	ober_free_elements(elm);
487696b5899Stb 	ober_free(&b);
4889107066aSreyk 	return page;
4899107066aSreyk }
4909107066aSreyk 
4919107066aSreyk void
aldap_freepage(struct aldap_page_control * page)4929107066aSreyk aldap_freepage(struct aldap_page_control *page)
4939107066aSreyk {
4949107066aSreyk 	free(page->cookie);
4959107066aSreyk 	free(page);
4969107066aSreyk }
4979107066aSreyk 
4989107066aSreyk void
aldap_freemsg(struct aldap_message * msg)4999107066aSreyk aldap_freemsg(struct aldap_message *msg)
5009107066aSreyk {
5019107066aSreyk 	if (msg->msg)
502696b5899Stb 		ober_free_elements(msg->msg);
5039107066aSreyk 	free(msg);
5049107066aSreyk }
5059107066aSreyk 
5069107066aSreyk int
aldap_get_resultcode(struct aldap_message * msg)5079107066aSreyk aldap_get_resultcode(struct aldap_message *msg)
5089107066aSreyk {
5099107066aSreyk 	return msg->body.res.rescode;
5109107066aSreyk }
5119107066aSreyk 
5129107066aSreyk char *
aldap_get_dn(struct aldap_message * msg)5139107066aSreyk aldap_get_dn(struct aldap_message *msg)
5149107066aSreyk {
5159107066aSreyk 	char *dn;
5169107066aSreyk 
5179107066aSreyk 	if (msg->dn == NULL)
5189107066aSreyk 		return NULL;
5199107066aSreyk 
520696b5899Stb 	if (ober_get_string(msg->dn, &dn) == -1)
5219107066aSreyk 		return NULL;
5229107066aSreyk 
5239107066aSreyk 	return utoa(dn);
5249107066aSreyk }
5259107066aSreyk 
5262f59fca6Smartijn struct aldap_stringset *
aldap_get_references(struct aldap_message * msg)5279107066aSreyk aldap_get_references(struct aldap_message *msg)
5289107066aSreyk {
5299107066aSreyk 	if (msg->references == NULL)
5309107066aSreyk 		return NULL;
5319107066aSreyk 	return aldap_get_stringset(msg->references);
5329107066aSreyk }
5339107066aSreyk 
5349107066aSreyk void
aldap_free_references(char ** values)5359107066aSreyk aldap_free_references(char **values)
5369107066aSreyk {
5379107066aSreyk 	int i;
5389107066aSreyk 
5399107066aSreyk 	if (values == NULL)
5409107066aSreyk 		return;
5419107066aSreyk 
5429107066aSreyk 	for (i = 0; values[i] != NULL; i++)
5439107066aSreyk 		free(values[i]);
5449107066aSreyk 
5459107066aSreyk 	free(values);
5469107066aSreyk }
5479107066aSreyk 
5489107066aSreyk char *
aldap_get_diagmsg(struct aldap_message * msg)5499107066aSreyk aldap_get_diagmsg(struct aldap_message *msg)
5509107066aSreyk {
5519107066aSreyk 	char *s;
5529107066aSreyk 
5539107066aSreyk 	if (msg->body.res.diagmsg == NULL)
5549107066aSreyk 		return NULL;
5559107066aSreyk 
556696b5899Stb 	if (ober_get_string(msg->body.res.diagmsg, &s) == -1)
5579107066aSreyk 		return NULL;
5589107066aSreyk 
5599107066aSreyk 	return utoa(s);
5609107066aSreyk }
5619107066aSreyk 
5629107066aSreyk int
aldap_count_attrs(struct aldap_message * msg)5639107066aSreyk aldap_count_attrs(struct aldap_message *msg)
5649107066aSreyk {
5659107066aSreyk 	int i;
5669107066aSreyk 	struct ber_element *a;
5679107066aSreyk 
5689107066aSreyk 	if (msg->body.search.attrs == NULL)
5699107066aSreyk 		return (-1);
5709107066aSreyk 
5719107066aSreyk 	for (i = 0, a = msg->body.search.attrs;
572696b5899Stb 	    a != NULL && ober_get_eoc(a) != 0;
5739107066aSreyk 	    i++, a = a->be_next)
5749107066aSreyk 		;
5759107066aSreyk 
5769107066aSreyk 	return i;
5779107066aSreyk }
5789107066aSreyk 
5799107066aSreyk int
aldap_first_attr(struct aldap_message * msg,char ** outkey,struct aldap_stringset ** outvalues)5802f59fca6Smartijn aldap_first_attr(struct aldap_message *msg, char **outkey,
5812f59fca6Smartijn     struct aldap_stringset **outvalues)
5829107066aSreyk {
583*0ce1f88dSmartijn 	struct ber_element *b;
5849107066aSreyk 	char *key;
5852f59fca6Smartijn 	struct aldap_stringset *ret;
5869107066aSreyk 
5879107066aSreyk 	if (msg->body.search.attrs == NULL)
5889107066aSreyk 		goto fail;
5899107066aSreyk 
590*0ce1f88dSmartijn 	if (ober_scanf_elements(msg->body.search.attrs, "{s(e)}",
591*0ce1f88dSmartijn 	    &key, &b) != 0)
5929107066aSreyk 		goto fail;
5939107066aSreyk 
5949107066aSreyk 	msg->body.search.iter = msg->body.search.attrs->be_next;
5959107066aSreyk 
5969107066aSreyk 	if ((ret = aldap_get_stringset(b)) == NULL)
5979107066aSreyk 		goto fail;
5989107066aSreyk 
5999107066aSreyk 	(*outvalues) = ret;
6009107066aSreyk 	(*outkey) = utoa(key);
6019107066aSreyk 
6029107066aSreyk 	return (1);
6039107066aSreyk fail:
6049107066aSreyk 	(*outkey) = NULL;
6059107066aSreyk 	(*outvalues) = NULL;
6069107066aSreyk 	return (-1);
6079107066aSreyk }
6089107066aSreyk 
6099107066aSreyk int
aldap_next_attr(struct aldap_message * msg,char ** outkey,struct aldap_stringset ** outvalues)6102f59fca6Smartijn aldap_next_attr(struct aldap_message *msg, char **outkey,
6112f59fca6Smartijn     struct aldap_stringset **outvalues)
6129107066aSreyk {
613*0ce1f88dSmartijn 	struct ber_element *a;
6149107066aSreyk 	char *key;
6152f59fca6Smartijn 	struct aldap_stringset *ret;
6169107066aSreyk 
6179107066aSreyk 	if (msg->body.search.iter == NULL)
6189107066aSreyk 		goto notfound;
6199107066aSreyk 
6209107066aSreyk 	LDAP_DEBUG("attr", msg->body.search.iter);
6219107066aSreyk 
622696b5899Stb 	if (ober_get_eoc(msg->body.search.iter) == 0)
6239107066aSreyk 		goto notfound;
6249107066aSreyk 
625*0ce1f88dSmartijn 	if (ober_scanf_elements(msg->body.search.iter, "{s(e)}", &key, &a) != 0)
6269107066aSreyk 		goto fail;
6279107066aSreyk 
6289107066aSreyk 	msg->body.search.iter = msg->body.search.iter->be_next;
6299107066aSreyk 
6309107066aSreyk 	if ((ret = aldap_get_stringset(a)) == NULL)
6319107066aSreyk 		goto fail;
6329107066aSreyk 
6339107066aSreyk 	(*outvalues) = ret;
6349107066aSreyk 	(*outkey) = utoa(key);
6359107066aSreyk 
6369107066aSreyk 	return (1);
6379107066aSreyk fail:
6389107066aSreyk notfound:
6399107066aSreyk 	(*outkey) = NULL;
6409107066aSreyk 	(*outvalues) = NULL;
6419107066aSreyk 	return (-1);
6429107066aSreyk }
6439107066aSreyk 
6449107066aSreyk int
aldap_match_attr(struct aldap_message * msg,char * inkey,struct aldap_stringset ** outvalues)6452f59fca6Smartijn aldap_match_attr(struct aldap_message *msg, char *inkey,
6462f59fca6Smartijn     struct aldap_stringset **outvalues)
6479107066aSreyk {
6489107066aSreyk 	struct ber_element *a, *b;
6499107066aSreyk 	char *descr = NULL;
6502f59fca6Smartijn 	struct aldap_stringset *ret;
6519107066aSreyk 
6529107066aSreyk 	if (msg->body.search.attrs == NULL)
6539107066aSreyk 		goto fail;
6549107066aSreyk 
6559107066aSreyk 	LDAP_DEBUG("attr", msg->body.search.attrs);
6569107066aSreyk 
6579107066aSreyk 	for (a = msg->body.search.attrs;;) {
6589107066aSreyk 		if (a == NULL)
6599107066aSreyk 			goto notfound;
660696b5899Stb 		if (ober_get_eoc(a) == 0)
6619107066aSreyk 			goto notfound;
662696b5899Stb 		if (ober_scanf_elements(a, "{s(e", &descr, &b) != 0)
6639107066aSreyk 			goto fail;
6649107066aSreyk 		if (strcasecmp(descr, inkey) == 0)
6659107066aSreyk 			goto attrfound;
6669107066aSreyk 		a = a->be_next;
6679107066aSreyk 	}
6689107066aSreyk 
6699107066aSreyk attrfound:
6709107066aSreyk 	if ((ret = aldap_get_stringset(b)) == NULL)
6719107066aSreyk 		goto fail;
6729107066aSreyk 
6739107066aSreyk 	(*outvalues) = ret;
6749107066aSreyk 
6759107066aSreyk 	return (1);
6769107066aSreyk fail:
6779107066aSreyk notfound:
6789107066aSreyk 	(*outvalues) = NULL;
6799107066aSreyk 	return (-1);
6809107066aSreyk }
6819107066aSreyk 
6829107066aSreyk int
aldap_free_attr(struct aldap_stringset * values)6832f59fca6Smartijn aldap_free_attr(struct aldap_stringset *values)
6849107066aSreyk {
6859107066aSreyk 	if (values == NULL)
6869107066aSreyk 		return -1;
6879107066aSreyk 
6882f59fca6Smartijn 	free(values->str);
6899107066aSreyk 	free(values);
6909107066aSreyk 
6919107066aSreyk 	return (1);
6929107066aSreyk }
6939107066aSreyk 
6949107066aSreyk void
aldap_free_url(struct aldap_url * lu)6959107066aSreyk aldap_free_url(struct aldap_url *lu)
6969107066aSreyk {
6979107066aSreyk 	free(lu->buffer);
6989107066aSreyk }
6999107066aSreyk 
7009107066aSreyk int
aldap_parse_url(const char * url,struct aldap_url * lu)7019107066aSreyk aldap_parse_url(const char *url, struct aldap_url *lu)
7029107066aSreyk {
7039107066aSreyk 	char		*p, *forward, *forward2;
7049107066aSreyk 	const char	*errstr = NULL;
7059107066aSreyk 	int		 i;
7069107066aSreyk 
7079107066aSreyk 	if ((lu->buffer = p = strdup(url)) == NULL)
7089107066aSreyk 		return (-1);
7099107066aSreyk 
7109107066aSreyk 	/* protocol */
7119107066aSreyk 	if (strncasecmp(LDAP_URL, p, strlen(LDAP_URL)) == 0) {
7129107066aSreyk 		lu->protocol = LDAP;
7139107066aSreyk 		p += strlen(LDAP_URL);
7149107066aSreyk 	} else if (strncasecmp(LDAPS_URL, p, strlen(LDAPS_URL)) == 0) {
7159107066aSreyk 		lu->protocol = LDAPS;
7169107066aSreyk 		p += strlen(LDAPS_URL);
7179107066aSreyk 	} else if (strncasecmp(LDAPTLS_URL, p, strlen(LDAPTLS_URL)) == 0) {
7189107066aSreyk 		lu->protocol = LDAPTLS;
7199107066aSreyk 		p += strlen(LDAPTLS_URL);
7209107066aSreyk 	} else if (strncasecmp(LDAPI_URL, p, strlen(LDAPI_URL)) == 0) {
7219107066aSreyk 		lu->protocol = LDAPI;
7229107066aSreyk 		p += strlen(LDAPI_URL);
7239107066aSreyk 	} else
7249107066aSreyk 		lu->protocol = -1;
7259107066aSreyk 
7269107066aSreyk 	/* host and optional port */
7279107066aSreyk 	if ((forward = strchr(p, '/')) != NULL)
7289107066aSreyk 		*forward = '\0';
7299107066aSreyk 	/* find the optional port */
7309107066aSreyk 	if ((forward2 = strchr(p, ':')) != NULL) {
7319107066aSreyk 		*forward2 = '\0';
7329107066aSreyk 		/* if a port is given */
7339107066aSreyk 		if (*(forward2+1) != '\0') {
7349107066aSreyk #define PORT_MAX UINT16_MAX
7359107066aSreyk 			lu->port = strtonum(++forward2, 0, PORT_MAX, &errstr);
7369107066aSreyk 			if (errstr)
7379107066aSreyk 				goto fail;
7389107066aSreyk 		}
7399107066aSreyk 	}
7409107066aSreyk 	/* fail if no host is given */
7419107066aSreyk 	if (strlen(p) == 0)
7429107066aSreyk 		goto fail;
7439107066aSreyk 	lu->host = p;
7449107066aSreyk 	if (forward == NULL)
7459107066aSreyk 		goto done;
7469107066aSreyk 	/* p is assigned either a pointer to a character or to '\0' */
7479107066aSreyk 	p = ++forward;
7489107066aSreyk 	if (strlen(p) == 0)
7499107066aSreyk 		goto done;
7509107066aSreyk 
7519107066aSreyk 	/* dn */
7529107066aSreyk 	if ((forward = strchr(p, '?')) != NULL)
7539107066aSreyk 		*forward = '\0';
7549107066aSreyk 	lu->dn = p;
7559107066aSreyk 	if (forward == NULL)
7569107066aSreyk 		goto done;
7579107066aSreyk 	/* p is assigned either a pointer to a character or to '\0' */
7589107066aSreyk 	p = ++forward;
7599107066aSreyk 	if (strlen(p) == 0)
7609107066aSreyk 		goto done;
7619107066aSreyk 
7629107066aSreyk 	/* attributes */
7639107066aSreyk 	if ((forward = strchr(p, '?')) != NULL)
7649107066aSreyk 		*forward = '\0';
7659107066aSreyk 	for (i = 0; i < MAXATTR; i++) {
7669107066aSreyk 		if ((forward2 = strchr(p, ',')) == NULL) {
7679107066aSreyk 			if (strlen(p) == 0)
7689107066aSreyk 				break;
7699107066aSreyk 			lu->attributes[i] = p;
7709107066aSreyk 			break;
7719107066aSreyk 		}
7729107066aSreyk 		*forward2 = '\0';
7739107066aSreyk 		lu->attributes[i] = p;
7749107066aSreyk 		p = ++forward2;
7759107066aSreyk 	}
7769107066aSreyk 	if (forward == NULL)
7779107066aSreyk 		goto done;
7789107066aSreyk 	/* p is assigned either a pointer to a character or to '\0' */
7799107066aSreyk 	p = ++forward;
7809107066aSreyk 	if (strlen(p) == 0)
7819107066aSreyk 		goto done;
7829107066aSreyk 
7839107066aSreyk 	/* scope */
7849107066aSreyk 	if ((forward = strchr(p, '?')) != NULL)
7859107066aSreyk 		*forward = '\0';
7869107066aSreyk 	if (strcmp(p, "base") == 0)
7879107066aSreyk 		lu->scope = LDAP_SCOPE_BASE;
7889107066aSreyk 	else if (strcmp(p, "one") == 0)
7899107066aSreyk 		lu->scope = LDAP_SCOPE_ONELEVEL;
7909107066aSreyk 	else if (strcmp(p, "sub") == 0)
7919107066aSreyk 		lu->scope = LDAP_SCOPE_SUBTREE;
7929107066aSreyk 	else
7939107066aSreyk 		goto fail;
7949107066aSreyk 	if (forward == NULL)
7959107066aSreyk 		goto done;
7969107066aSreyk 	p = ++forward;
7979107066aSreyk 	if (strlen(p) == 0)
7989107066aSreyk 		goto done;
7999107066aSreyk 
8009107066aSreyk 	/* filter */
8019107066aSreyk 	if (p)
8029107066aSreyk 		lu->filter = p;
8039107066aSreyk done:
8049107066aSreyk 	return (1);
8059107066aSreyk fail:
8069107066aSreyk 	free(lu->buffer);
8079107066aSreyk 	lu->buffer = NULL;
8089107066aSreyk 	return (-1);
8099107066aSreyk }
8109107066aSreyk 
8119107066aSreyk int
aldap_search_url(struct aldap * ldap,char * url,int typesonly,int sizelimit,int timelimit,struct aldap_page_control * page)8129107066aSreyk aldap_search_url(struct aldap *ldap, char *url, int typesonly, int sizelimit,
8139107066aSreyk     int timelimit, struct aldap_page_control *page)
8149107066aSreyk {
8159107066aSreyk 	struct aldap_url *lu;
8169107066aSreyk 
8179107066aSreyk 	if ((lu = calloc(1, sizeof(*lu))) == NULL)
8189107066aSreyk 		return (-1);
8199107066aSreyk 
8209107066aSreyk 	if (aldap_parse_url(url, lu))
8219107066aSreyk 		goto fail;
8229107066aSreyk 
8239107066aSreyk 	if (aldap_search(ldap, lu->dn, lu->scope, lu->filter, lu->attributes,
8249107066aSreyk 	    typesonly, sizelimit, timelimit, page) == -1)
8259107066aSreyk 		goto fail;
8269107066aSreyk 
8279107066aSreyk 	aldap_free_url(lu);
8289107066aSreyk 	return (ldap->msgid);
8299107066aSreyk fail:
8309107066aSreyk 	aldap_free_url(lu);
8319107066aSreyk 	return (-1);
8329107066aSreyk }
8339107066aSreyk 
8349107066aSreyk /*
8359107066aSreyk  * internal functions
8369107066aSreyk  */
8379107066aSreyk 
8382f59fca6Smartijn struct aldap_stringset *
aldap_get_stringset(struct ber_element * elm)8399107066aSreyk aldap_get_stringset(struct ber_element *elm)
8409107066aSreyk {
8419107066aSreyk 	struct ber_element *a;
8429107066aSreyk 	int i;
8432f59fca6Smartijn 	struct aldap_stringset *ret;
8449107066aSreyk 
8459107066aSreyk 	if (elm->be_type != BER_TYPE_OCTETSTRING)
8469107066aSreyk 		return NULL;
8479107066aSreyk 
8482f59fca6Smartijn 	if ((ret = malloc(sizeof(*ret))) == NULL)
8492f59fca6Smartijn 		return NULL;
8502f59fca6Smartijn 	for (a = elm, ret->len = 0; a != NULL && a->be_type ==
8512f59fca6Smartijn 	    BER_TYPE_OCTETSTRING; a = a->be_next, ret->len++)
8529107066aSreyk 		;
8532f59fca6Smartijn 	if (ret->len == 0) {
8542f59fca6Smartijn 		free(ret);
8559107066aSreyk 		return NULL;
8562f59fca6Smartijn 	}
8579107066aSreyk 
8582f59fca6Smartijn 	if ((ret->str = reallocarray(NULL, ret->len,
8592f59fca6Smartijn 	    sizeof(*(ret->str)))) == NULL) {
8602f59fca6Smartijn 		free(ret);
8619107066aSreyk 		return NULL;
8622f59fca6Smartijn 	}
8639107066aSreyk 
8649107066aSreyk 	for (a = elm, i = 0; a != NULL && a->be_type == BER_TYPE_OCTETSTRING;
8652f59fca6Smartijn 	    a = a->be_next, i++)
866696b5899Stb 		(void) ober_get_ostring(a, &(ret->str[i]));
8679107066aSreyk 
8689107066aSreyk 	return ret;
8699107066aSreyk }
8709107066aSreyk 
8719107066aSreyk /*
8729107066aSreyk  * Base case for ldap_do_parse_search_filter
8739107066aSreyk  *
8749107066aSreyk  * returns:
8759107066aSreyk  *	struct ber_element *, ber_element tree
8769107066aSreyk  *	NULL, parse failed
8779107066aSreyk  */
8789107066aSreyk static struct ber_element *
ldap_parse_search_filter(struct ber_element * ber,char * filter)8799107066aSreyk ldap_parse_search_filter(struct ber_element *ber, char *filter)
8809107066aSreyk {
8819107066aSreyk 	struct ber_element *elm;
8829107066aSreyk 	char *cp;
8839107066aSreyk 
8849107066aSreyk 	cp = filter;
8859107066aSreyk 
8869107066aSreyk 	if (cp == NULL || *cp == '\0') {
8879107066aSreyk 		errno = EINVAL;
8889107066aSreyk 		return (NULL);
8899107066aSreyk 	}
8909107066aSreyk 
8919107066aSreyk 	if ((elm = ldap_do_parse_search_filter(ber, &cp)) == NULL)
8929107066aSreyk 		return (NULL);
8939107066aSreyk 
8949107066aSreyk 	if (*cp != '\0') {
895696b5899Stb 		ober_free_elements(elm);
896696b5899Stb 		ober_link_elements(ber, NULL);
8979107066aSreyk 		errno = EINVAL;
8989107066aSreyk 		return (NULL);
8999107066aSreyk 	}
9009107066aSreyk 
9019107066aSreyk 	return (elm);
9029107066aSreyk }
9039107066aSreyk 
9049107066aSreyk /*
9059107066aSreyk  * Translate RFC4515 search filter string into ber_element tree
9069107066aSreyk  *
9079107066aSreyk  * returns:
9089107066aSreyk  *	struct ber_element *, ber_element tree
9099107066aSreyk  *	NULL, parse failed
9109107066aSreyk  *
9119107066aSreyk  * notes:
9129107066aSreyk  *	when cp is passed to a recursive invocation, it is updated
9139107066aSreyk  *	    to point one character beyond the filter that was passed
9149107066aSreyk  *	    i.e., cp jumps to "(filter)" upon return
9159107066aSreyk  *	                               ^
9169107066aSreyk  *	goto's used to discriminate error-handling based on error type
9179107066aSreyk  *	doesn't handle extended filters (yet)
9189107066aSreyk  *
9199107066aSreyk  */
9209107066aSreyk static struct ber_element *
ldap_do_parse_search_filter(struct ber_element * prev,char ** cpp)9219107066aSreyk ldap_do_parse_search_filter(struct ber_element *prev, char **cpp)
9229107066aSreyk {
9239107066aSreyk 	struct ber_element *elm, *root = NULL;
9249107066aSreyk 	char *attr_desc, *attr_val, *parsed_val, *cp;
9259107066aSreyk 	size_t len;
9269107066aSreyk 	unsigned long type;
9279107066aSreyk 
9289107066aSreyk 	root = NULL;
9299107066aSreyk 
9309107066aSreyk 	/* cpp should pass in pointer to opening parenthesis of "(filter)" */
9319107066aSreyk 	cp = *cpp;
9329107066aSreyk 	if (*cp != '(')
9339107066aSreyk 		goto syntaxfail;
9349107066aSreyk 
9359107066aSreyk 	switch (*++cp) {
9369107066aSreyk 	case '&':		/* AND */
9379107066aSreyk 	case '|':		/* OR */
9389107066aSreyk 		if (*cp == '&')
9399107066aSreyk 			type = LDAP_FILT_AND;
9409107066aSreyk 		else
9419107066aSreyk 			type = LDAP_FILT_OR;
9429107066aSreyk 
943696b5899Stb 		if ((elm = ober_add_set(prev)) == NULL)
9449107066aSreyk 			goto callfail;
9459107066aSreyk 		root = elm;
946696b5899Stb 		ober_set_header(elm, BER_CLASS_CONTEXT, type);
9479107066aSreyk 
9489107066aSreyk 		if (*++cp != '(')		/* opening `(` of filter */
9499107066aSreyk 			goto syntaxfail;
9509107066aSreyk 
9519107066aSreyk 		while (*cp == '(') {
9529107066aSreyk 			if ((elm =
9539107066aSreyk 			    ldap_do_parse_search_filter(elm, &cp)) == NULL)
9549107066aSreyk 				goto bad;
9559107066aSreyk 		}
9569107066aSreyk 
9579107066aSreyk 		if (*cp != ')')			/* trailing `)` of filter */
9589107066aSreyk 			goto syntaxfail;
9599107066aSreyk 		break;
9609107066aSreyk 
9619107066aSreyk 	case '!':		/* NOT */
962696b5899Stb 		if ((root = ober_add_sequence(prev)) == NULL)
9639107066aSreyk 			goto callfail;
964696b5899Stb 		ober_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_NOT);
9659107066aSreyk 
9669107066aSreyk 		cp++;				/* now points to sub-filter */
9679107066aSreyk 		if ((elm = ldap_do_parse_search_filter(root, &cp)) == NULL)
9689107066aSreyk 			goto bad;
9699107066aSreyk 
9709107066aSreyk 		if (*cp != ')')			/* trailing `)` of filter */
9719107066aSreyk 			goto syntaxfail;
9729107066aSreyk 		break;
9739107066aSreyk 
9749107066aSreyk 	default:	/* SIMPLE || PRESENCE */
9759107066aSreyk 		attr_desc = cp;
9769107066aSreyk 
9779107066aSreyk 		len = strcspn(cp, "()<>~=");
9789107066aSreyk 		cp += len;
9799107066aSreyk 		switch (*cp) {
9809107066aSreyk 		case '~':
9819107066aSreyk 			type = LDAP_FILT_APPR;
9829107066aSreyk 			cp++;
9839107066aSreyk 			break;
9849107066aSreyk 		case '<':
9859107066aSreyk 			type = LDAP_FILT_LE;
9869107066aSreyk 			cp++;
9879107066aSreyk 			break;
9889107066aSreyk 		case '>':
9899107066aSreyk 			type = LDAP_FILT_GE;
9909107066aSreyk 			cp++;
9919107066aSreyk 			break;
9929107066aSreyk 		case '=':
9939107066aSreyk 			type = LDAP_FILT_EQ;	/* assume EQ until disproven */
9949107066aSreyk 			break;
9959107066aSreyk 		case '(':
9969107066aSreyk 		case ')':
9979107066aSreyk 		default:
9989107066aSreyk 			goto syntaxfail;
9999107066aSreyk 		}
10009107066aSreyk 		attr_val = ++cp;
10019107066aSreyk 
10029107066aSreyk 		/* presence filter */
10039107066aSreyk 		if (strncmp(attr_val, "*)", 2) == 0) {
10049107066aSreyk 			cp++;			/* point to trailing `)` */
10059107066aSreyk 			if ((root =
1006696b5899Stb 			    ober_add_nstring(prev, attr_desc, len)) == NULL)
10079107066aSreyk 				goto bad;
10089107066aSreyk 
1009696b5899Stb 			ober_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_PRES);
10109107066aSreyk 			break;
10119107066aSreyk 		}
10129107066aSreyk 
1013696b5899Stb 		if ((root = ober_add_sequence(prev)) == NULL)
10149107066aSreyk 			goto callfail;
1015696b5899Stb 		ober_set_header(root, BER_CLASS_CONTEXT, type);
10169107066aSreyk 
1017696b5899Stb 		if ((elm = ober_add_nstring(root, attr_desc, len)) == NULL)
10189107066aSreyk 			goto callfail;
10199107066aSreyk 
10209107066aSreyk 		len = strcspn(attr_val, "*)");
10219107066aSreyk 		if (len == 0 && *cp != '*')
10229107066aSreyk 			goto syntaxfail;
10239107066aSreyk 		cp += len;
10249107066aSreyk 		if (*cp == '\0')
10259107066aSreyk 			goto syntaxfail;
10269107066aSreyk 
10279107066aSreyk 		if (*cp == '*') {	/* substring filter */
10289107066aSreyk 			int initial;
10299107066aSreyk 
10309107066aSreyk 			cp = attr_val;
10319107066aSreyk 
1032696b5899Stb 			ober_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_SUBS);
10339107066aSreyk 
1034696b5899Stb 			if ((elm = ober_add_sequence(elm)) == NULL)
10359107066aSreyk 				goto callfail;
10369107066aSreyk 
10379107066aSreyk 			for (initial = 1;; cp++, initial = 0) {
10389107066aSreyk 				attr_val = cp;
10399107066aSreyk 
10409107066aSreyk 				len = strcspn(attr_val, "*)");
10419107066aSreyk 				if (len == 0) {
10429107066aSreyk 					if (*cp == ')')
10439107066aSreyk 						break;
10449107066aSreyk 					else
10459107066aSreyk 						continue;
10469107066aSreyk 				}
10479107066aSreyk 				cp += len;
10489107066aSreyk 				if (*cp == '\0')
10499107066aSreyk 					goto syntaxfail;
10509107066aSreyk 
10519107066aSreyk 				if (initial)
10529107066aSreyk 					type = LDAP_FILT_SUBS_INIT;
10539107066aSreyk 				else if (*cp == ')')
10549107066aSreyk 					type = LDAP_FILT_SUBS_FIN;
10559107066aSreyk 				else
10569107066aSreyk 					type = LDAP_FILT_SUBS_ANY;
10579107066aSreyk 
10589107066aSreyk 				if ((parsed_val = parseval(attr_val, len)) ==
10599107066aSreyk 				    NULL)
10609107066aSreyk 					goto callfail;
1061696b5899Stb 				elm = ober_add_nstring(elm, parsed_val,
10629107066aSreyk 				    strlen(parsed_val));
10639107066aSreyk 				free(parsed_val);
10649107066aSreyk 				if (elm == NULL)
10659107066aSreyk 					goto callfail;
1066696b5899Stb 				ober_set_header(elm, BER_CLASS_CONTEXT, type);
10679107066aSreyk 				if (type == LDAP_FILT_SUBS_FIN)
10689107066aSreyk 					break;
10699107066aSreyk 			}
10709107066aSreyk 			break;
10719107066aSreyk 		}
10729107066aSreyk 
10739107066aSreyk 		if ((parsed_val = parseval(attr_val, len)) == NULL)
10749107066aSreyk 			goto callfail;
1075696b5899Stb 		elm = ober_add_nstring(elm, parsed_val, strlen(parsed_val));
10769107066aSreyk 		free(parsed_val);
10779107066aSreyk 		if (elm == NULL)
10789107066aSreyk 			goto callfail;
10799107066aSreyk 		break;
10809107066aSreyk 	}
10819107066aSreyk 
10829107066aSreyk 	cp++;		/* now points one char beyond the trailing `)` */
10839107066aSreyk 
10849107066aSreyk 	*cpp = cp;
10859107066aSreyk 	return (root);
10869107066aSreyk 
10879107066aSreyk syntaxfail:		/* XXX -- error reporting */
10889107066aSreyk callfail:
10899107066aSreyk bad:
10909107066aSreyk 	if (root != NULL)
1091696b5899Stb 		ober_free_elements(root);
1092696b5899Stb 	ober_link_elements(prev, NULL);
10939107066aSreyk 	return (NULL);
10949107066aSreyk }
10959107066aSreyk 
10969107066aSreyk #ifdef DEBUG
10979107066aSreyk /*
10989107066aSreyk  * Display a list of ber elements.
10999107066aSreyk  *
11009107066aSreyk  */
11019107066aSreyk void
ldap_debug_elements(struct ber_element * root)11029107066aSreyk ldap_debug_elements(struct ber_element *root)
11039107066aSreyk {
11049107066aSreyk 	static int	 indent = 0;
11059107066aSreyk 	long long	 v;
11069107066aSreyk 	int		 d;
11079107066aSreyk 	char		*buf;
11089107066aSreyk 	size_t		 len;
11099107066aSreyk 	u_int		 i;
11109107066aSreyk 	int		 constructed;
11119107066aSreyk 	struct ber_oid	 o;
11129107066aSreyk 
11139107066aSreyk 	/* calculate lengths */
1114696b5899Stb 	ober_calc_len(root);
11159107066aSreyk 
11169107066aSreyk 	switch (root->be_encoding) {
11179107066aSreyk 	case BER_TYPE_SEQUENCE:
11189107066aSreyk 	case BER_TYPE_SET:
11199107066aSreyk 		constructed = root->be_encoding;
11209107066aSreyk 		break;
11219107066aSreyk 	default:
11229107066aSreyk 		constructed = 0;
11239107066aSreyk 		break;
11249107066aSreyk 	}
11259107066aSreyk 
11269107066aSreyk 	fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
11279107066aSreyk 	switch (root->be_class) {
11289107066aSreyk 	case BER_CLASS_UNIVERSAL:
11299107066aSreyk 		fprintf(stderr, "class: universal(%u) type: ", root->be_class);
11309107066aSreyk 		switch (root->be_type) {
11319107066aSreyk 		case BER_TYPE_EOC:
11329107066aSreyk 			fprintf(stderr, "end-of-content");
11339107066aSreyk 			break;
11349107066aSreyk 		case BER_TYPE_BOOLEAN:
11359107066aSreyk 			fprintf(stderr, "boolean");
11369107066aSreyk 			break;
11379107066aSreyk 		case BER_TYPE_INTEGER:
11389107066aSreyk 			fprintf(stderr, "integer");
11399107066aSreyk 			break;
11409107066aSreyk 		case BER_TYPE_BITSTRING:
11419107066aSreyk 			fprintf(stderr, "bit-string");
11429107066aSreyk 			break;
11439107066aSreyk 		case BER_TYPE_OCTETSTRING:
11449107066aSreyk 			fprintf(stderr, "octet-string");
11459107066aSreyk 			break;
11469107066aSreyk 		case BER_TYPE_NULL:
11479107066aSreyk 			fprintf(stderr, "null");
11489107066aSreyk 			break;
11499107066aSreyk 		case BER_TYPE_OBJECT:
11509107066aSreyk 			fprintf(stderr, "object");
11519107066aSreyk 			break;
11529107066aSreyk 		case BER_TYPE_ENUMERATED:
11539107066aSreyk 			fprintf(stderr, "enumerated");
11549107066aSreyk 			break;
11559107066aSreyk 		case BER_TYPE_SEQUENCE:
11569107066aSreyk 			fprintf(stderr, "sequence");
11579107066aSreyk 			break;
11589107066aSreyk 		case BER_TYPE_SET:
11599107066aSreyk 			fprintf(stderr, "set");
11609107066aSreyk 			break;
11619107066aSreyk 		}
11629107066aSreyk 		break;
11639107066aSreyk 	case BER_CLASS_APPLICATION:
11649107066aSreyk 		fprintf(stderr, "class: application(%u) type: ",
11659107066aSreyk 		    root->be_class);
11669107066aSreyk 		switch (root->be_type) {
11679107066aSreyk 		case LDAP_REQ_BIND:
11689107066aSreyk 			fprintf(stderr, "bind");
11699107066aSreyk 			break;
11709107066aSreyk 		case LDAP_RES_BIND:
11719107066aSreyk 			fprintf(stderr, "bind");
11729107066aSreyk 			break;
11739107066aSreyk 		case LDAP_REQ_UNBIND_30:
11749107066aSreyk 			break;
11759107066aSreyk 		case LDAP_REQ_SEARCH:
11769107066aSreyk 			fprintf(stderr, "search");
11779107066aSreyk 			break;
11789107066aSreyk 		case LDAP_RES_SEARCH_ENTRY:
11799107066aSreyk 			fprintf(stderr, "search_entry");
11809107066aSreyk 			break;
11819107066aSreyk 		case LDAP_RES_SEARCH_RESULT:
11829107066aSreyk 			fprintf(stderr, "search_result");
11839107066aSreyk 			break;
11849107066aSreyk 		case LDAP_REQ_MODIFY:
11859107066aSreyk 			fprintf(stderr, "modify");
11869107066aSreyk 			break;
11879107066aSreyk 		case LDAP_RES_MODIFY:
11889107066aSreyk 			fprintf(stderr, "modify");
11899107066aSreyk 			break;
11909107066aSreyk 		case LDAP_REQ_ADD:
11919107066aSreyk 			fprintf(stderr, "add");
11929107066aSreyk 			break;
11939107066aSreyk 		case LDAP_RES_ADD:
11949107066aSreyk 			fprintf(stderr, "add");
11959107066aSreyk 			break;
11969107066aSreyk 		case LDAP_REQ_DELETE_30:
11979107066aSreyk 			fprintf(stderr, "delete");
11989107066aSreyk 			break;
11999107066aSreyk 		case LDAP_RES_DELETE:
12009107066aSreyk 			fprintf(stderr, "delete");
12019107066aSreyk 			break;
12029107066aSreyk 		case LDAP_REQ_MODRDN:
12039107066aSreyk 			fprintf(stderr, "modrdn");
12049107066aSreyk 			break;
12059107066aSreyk 		case LDAP_RES_MODRDN:
12069107066aSreyk 			fprintf(stderr, "modrdn");
12079107066aSreyk 			break;
12089107066aSreyk 		case LDAP_REQ_COMPARE:
12099107066aSreyk 			fprintf(stderr, "compare");
12109107066aSreyk 			break;
12119107066aSreyk 		case LDAP_RES_COMPARE:
12129107066aSreyk 			fprintf(stderr, "compare");
12139107066aSreyk 			break;
12149107066aSreyk 		case LDAP_REQ_ABANDON_30:
12159107066aSreyk 			fprintf(stderr, "abandon");
12169107066aSreyk 			break;
12179107066aSreyk 		}
12189107066aSreyk 		break;
12199107066aSreyk 	case BER_CLASS_PRIVATE:
12209107066aSreyk 		fprintf(stderr, "class: private(%u) type: ", root->be_class);
122179c62c2aSrob 		fprintf(stderr, "encoding (%u) type: ", root->be_encoding);
12229107066aSreyk 		break;
12239107066aSreyk 	case BER_CLASS_CONTEXT:
12249107066aSreyk 		/* XXX: this is not correct */
12259107066aSreyk 		fprintf(stderr, "class: context(%u) type: ", root->be_class);
12269107066aSreyk 		switch(root->be_type) {
12279107066aSreyk 		case LDAP_AUTH_SIMPLE:
12289107066aSreyk 			fprintf(stderr, "auth simple");
12299107066aSreyk 			break;
12309107066aSreyk 		}
12319107066aSreyk 		break;
12329107066aSreyk 	default:
12339107066aSreyk 		fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
12349107066aSreyk 		break;
12359107066aSreyk 	}
123679c62c2aSrob 	fprintf(stderr, "(%u) encoding %u ",
12379107066aSreyk 	    root->be_type, root->be_encoding);
12389107066aSreyk 
12399107066aSreyk 	if (constructed)
12409107066aSreyk 		root->be_encoding = constructed;
12419107066aSreyk 
12429107066aSreyk 	switch (root->be_encoding) {
12439107066aSreyk 	case BER_TYPE_BOOLEAN:
1244696b5899Stb 		if (ober_get_boolean(root, &d) == -1) {
12459107066aSreyk 			fprintf(stderr, "<INVALID>\n");
12469107066aSreyk 			break;
12479107066aSreyk 		}
12489107066aSreyk 		fprintf(stderr, "%s(%d)\n", d ? "true" : "false", d);
12499107066aSreyk 		break;
12509107066aSreyk 	case BER_TYPE_INTEGER:
1251696b5899Stb 		if (ober_get_integer(root, &v) == -1) {
12529107066aSreyk 			fprintf(stderr, "<INVALID>\n");
12539107066aSreyk 			break;
12549107066aSreyk 		}
12559107066aSreyk 		fprintf(stderr, "value %lld\n", v);
12569107066aSreyk 		break;
12579107066aSreyk 	case BER_TYPE_ENUMERATED:
1258696b5899Stb 		if (ober_get_enumerated(root, &v) == -1) {
12599107066aSreyk 			fprintf(stderr, "<INVALID>\n");
12609107066aSreyk 			break;
12619107066aSreyk 		}
12629107066aSreyk 		fprintf(stderr, "value %lld\n", v);
12639107066aSreyk 		break;
12649107066aSreyk 	case BER_TYPE_BITSTRING:
1265696b5899Stb 		if (ober_get_bitstring(root, (void *)&buf, &len) == -1) {
12669107066aSreyk 			fprintf(stderr, "<INVALID>\n");
12679107066aSreyk 			break;
12689107066aSreyk 		}
12699107066aSreyk 		fprintf(stderr, "hexdump ");
12709107066aSreyk 		for (i = 0; i < len; i++)
12719107066aSreyk 			fprintf(stderr, "%02x", buf[i]);
12729107066aSreyk 		fprintf(stderr, "\n");
12739107066aSreyk 		break;
12749107066aSreyk 	case BER_TYPE_OBJECT:
1275696b5899Stb 		if (ober_get_oid(root, &o) == -1) {
12769107066aSreyk 			fprintf(stderr, "<INVALID>\n");
12779107066aSreyk 			break;
12789107066aSreyk 		}
12799107066aSreyk 		fprintf(stderr, "\n");
12809107066aSreyk 		break;
12819107066aSreyk 	case BER_TYPE_OCTETSTRING:
1282696b5899Stb 		if (ober_get_nstring(root, (void *)&buf, &len) == -1) {
12839107066aSreyk 			fprintf(stderr, "<INVALID>\n");
12849107066aSreyk 			break;
12859107066aSreyk 		}
1286f530ba9fSreyk 		fprintf(stderr, "string \"%.*s\"\n",  (int)len, buf);
12879107066aSreyk 		break;
12889107066aSreyk 	case BER_TYPE_NULL:	/* no payload */
12899107066aSreyk 	case BER_TYPE_EOC:
12909107066aSreyk 	case BER_TYPE_SEQUENCE:
12919107066aSreyk 	case BER_TYPE_SET:
12929107066aSreyk 	default:
12939107066aSreyk 		fprintf(stderr, "\n");
12949107066aSreyk 		break;
12959107066aSreyk 	}
12969107066aSreyk 
12979107066aSreyk 	if (constructed && root->be_sub) {
12989107066aSreyk 		indent += 2;
12999107066aSreyk 		ldap_debug_elements(root->be_sub);
13009107066aSreyk 		indent -= 2;
13019107066aSreyk 	}
13029107066aSreyk 	if (root->be_next)
13039107066aSreyk 		ldap_debug_elements(root->be_next);
13049107066aSreyk }
13059107066aSreyk #endif
13069107066aSreyk 
13079107066aSreyk /*
13089107066aSreyk  * Strip UTF-8 down to ASCII without validation.
13099107066aSreyk  * notes:
13109107066aSreyk  *	non-ASCII characters are displayed as '?'
13119107066aSreyk  *	the argument u should be a NULL terminated sequence of UTF-8 bytes.
13129107066aSreyk  */
13139107066aSreyk char *
utoa(char * u)13149107066aSreyk utoa(char *u)
13159107066aSreyk {
13169107066aSreyk 	int	 len, i, j;
13179107066aSreyk 	char	*str;
13189107066aSreyk 
13199107066aSreyk 	/* calculate the length to allocate */
13209107066aSreyk 	for (len = 0, i = 0; u[i] != '\0'; i++)
13219107066aSreyk 		if (!isu8cont(u[i]))
13229107066aSreyk 			len++;
13239107066aSreyk 
13249107066aSreyk 	if ((str = calloc(len + 1, sizeof(char))) == NULL)
13259107066aSreyk 		return NULL;
13269107066aSreyk 
13279107066aSreyk 	/* copy the ASCII characters to the newly allocated string */
13289107066aSreyk 	for (i = 0, j = 0; u[i] != '\0'; i++)
13299107066aSreyk 		if (!isu8cont(u[i]))
13309107066aSreyk 			str[j++] = isascii((unsigned char)u[i]) ? u[i] : '?';
13319107066aSreyk 
13329107066aSreyk 	return str;
13339107066aSreyk }
13349107066aSreyk 
13359107066aSreyk static int
isu8cont(unsigned char c)13369107066aSreyk isu8cont(unsigned char c)
13379107066aSreyk {
13389107066aSreyk 	return (c & (0x80 | 0x40)) == 0x80;
13399107066aSreyk }
13409107066aSreyk 
13419107066aSreyk /*
13429107066aSreyk  * Parse a LDAP value
13439107066aSreyk  * notes:
13449107066aSreyk  *	the argument p should be a NUL-terminated sequence of ASCII bytes
13459107066aSreyk  */
13469107066aSreyk char *
parseval(char * p,size_t len)13479107066aSreyk parseval(char *p, size_t len)
13489107066aSreyk {
13499107066aSreyk 	char	 hex[3];
13509107066aSreyk 	char	*buffer;
13519107066aSreyk 	size_t	 i, j;
13529107066aSreyk 
13539107066aSreyk 	if ((buffer = calloc(1, len + 1)) == NULL)
13549107066aSreyk 		return NULL;
13559107066aSreyk 
13569107066aSreyk 	for (i = j = 0; j < len; i++) {
13579107066aSreyk 		if (p[j] == '\\') {
13589107066aSreyk 			strlcpy(hex, p + j + 1, sizeof(hex));
13599107066aSreyk 			buffer[i] = (char)strtoumax(hex, NULL, 16);
13609107066aSreyk 			j += 3;
13619107066aSreyk 		} else {
13629107066aSreyk 			buffer[i] = p[j];
13639107066aSreyk 			j++;
13649107066aSreyk 		}
13659107066aSreyk 	}
13669107066aSreyk 
13679107066aSreyk 	return buffer;
13689107066aSreyk }
13699107066aSreyk 
13709107066aSreyk int
aldap_get_errno(struct aldap * a,const char ** estr)13719107066aSreyk aldap_get_errno(struct aldap *a, const char **estr)
13729107066aSreyk {
13739107066aSreyk 	switch (a->err) {
13749107066aSreyk 	case ALDAP_ERR_SUCCESS:
13759107066aSreyk 		*estr = "success";
13769107066aSreyk 		break;
13779107066aSreyk 	case ALDAP_ERR_PARSER_ERROR:
13789107066aSreyk 		*estr = "parser failed";
13799107066aSreyk 		break;
13809107066aSreyk 	case ALDAP_ERR_INVALID_FILTER:
13819107066aSreyk 		*estr = "invalid filter";
13829107066aSreyk 		break;
13839107066aSreyk 	case ALDAP_ERR_OPERATION_FAILED:
13849107066aSreyk 		*estr = "operation failed";
13859107066aSreyk 		break;
13869107066aSreyk 	case ALDAP_ERR_TLS_ERROR:
13879107066aSreyk 		*estr = tls_error(a->tls);
13889107066aSreyk 		break;
13899107066aSreyk 	default:
13909107066aSreyk 		*estr = "unknown";
13919107066aSreyk 		break;
13929107066aSreyk 	}
13939107066aSreyk 	return (a->err);
13949107066aSreyk }
1395