Hi

Omar has sugested to update ber. I have implemented this and rebased
my patches to this.

The "update ber and aldap" patch is acually not correct, because the
aldap_match_attr() has a changed API. The result might not used after
the message was freed.

The updated aldap also adds the posibility for ldap+tls (starttls) and
ldapi. This is not yet implemented.

Philipp
From 68d93d9ca2dd3601d314403c3a035d758f168401 Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Mon, 12 Feb 2024 08:38:42 +0100
Subject: [PATCH 1/4] table-ldap update ber and aldap

---
 configure.ac                          |   2 +-
 extras/tables/table-ldap/aldap.c      | 609 +++++++++++--------
 extras/tables/table-ldap/aldap.h      |  69 ++-
 extras/tables/table-ldap/ber.c        | 816 ++++++++++++++++----------
 extras/tables/table-ldap/ber.h        | 141 +++--
 extras/tables/table-ldap/table_ldap.c |  57 +-
 6 files changed, 1059 insertions(+), 635 deletions(-)

diff --git a/configure.ac b/configure.ac
index 7ecc50d..7c4561c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -579,7 +579,7 @@ AC_ARG_WITH([libssl],
 	]
 )
 ## XXX chl -lssl manually added
-LIBS="-lcrypto -lssl $LIBS"
+LIBS="-lcrypto -lssl -ltls $LIBS"
 AC_TRY_LINK_FUNC([RAND_add], [AC_DEFINE([HAVE_OPENSSL], [1],
 	[Define if your ssl headers are included
 	with #include <openssl/header.h>])],
diff --git a/extras/tables/table-ldap/aldap.c b/extras/tables/table-ldap/aldap.c
index e5b99fa..fca8abc 100644
--- a/extras/tables/table-ldap/aldap.c
+++ b/extras/tables/table-ldap/aldap.c
@@ -1,3 +1,5 @@
+/*	$OpenBSD: aldap.c,v 1.10 2022/03/31 09:03:48 martijn Exp $ */
+
 /*
  * Copyright (c) 2008 Alexander Schrijver <aschrij...@openbsd.org>
  * Copyright (c) 2006, 2007 Marc Balmer <mbal...@openbsd.org>
@@ -17,12 +19,16 @@
 
 #include "includes.h"
 
+#include <arpa/inet.h>
+#include <ctype.h>
 #include <errno.h>
 #include <inttypes.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <event.h>
+
 #include "aldap.h"
 
 #if 0
@@ -34,11 +40,15 @@ static struct ber_element	*ldap_parse_search_filter(struct ber_element *,
 				    char *, const char *);
 static struct ber_element	*ldap_do_parse_search_filter(
 				    struct ber_element *, char **, const char *);
-char				**aldap_get_stringset(struct ber_element *);
+struct aldap_stringset		*aldap_get_stringset(struct ber_element *);
 char				*utoa(char *);
+static int			 isu8cont(unsigned char);
 char				*parseval(char *, size_t, const char *);
 int				aldap_create_page_control(struct ber_element *,
 				    int, struct aldap_page_control *);
+int				aldap_send(struct aldap *,
+				    struct ber_element *);
+unsigned int			aldap_application(struct ber_element *);
 
 #ifdef DEBUG
 void			 ldap_debug_elements(struct ber_element *);
@@ -52,13 +62,22 @@ void			 ldap_debug_elements(struct ber_element *);
 #define LDAP_DEBUG(x, y)	do { } while (0)
 #endif
 
+unsigned int
+aldap_application(struct ber_element *elm)
+{
+	return BER_TYPE_OCTETSTRING;
+}
+
 int
 aldap_close(struct aldap *al)
 {
-	if (close(al->ber.fd) == -1)
-		return (-1);
-
-	ber_free(&al->ber);
+	if (al->tls != NULL) {
+		tls_close(al->tls);
+		tls_free(al->tls);
+	}
+	close(al->fd);
+	ober_free(&al->ber);
+	evbuffer_free(al->buf);
 	free(al);
 
 	return (0);
@@ -71,43 +90,142 @@ aldap_init(int fd)
 
 	if ((a = calloc(1, sizeof(*a))) == NULL)
 		return NULL;
-	a->ber.fd = fd;
+	a->buf = evbuffer_new();
+	a->fd = fd;
+	ober_set_application(&a->ber, aldap_application);
 
 	return a;
 }
 
+static int
+tls_handshake_wrapper(struct tls *ctx)
+{
+	int ret;
+	do {
+		ret = tls_handshake(ctx);
+	} while (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT);
+	return ret;
+}
+
+int
+aldap_tls(struct aldap *ldap, struct tls_config *cfg, const char *name)
+{
+	ldap->tls = tls_client();
+	if (ldap->tls == NULL) {
+		ldap->err = ALDAP_ERR_OPERATION_FAILED;
+		return (-1);
+	}
+
+	if (tls_configure(ldap->tls, cfg) == -1) {
+		ldap->err = ALDAP_ERR_TLS_ERROR;
+		return (-1);
+	}
+
+	if (tls_connect_socket(ldap->tls, ldap->fd, name) == -1) {
+		ldap->err = ALDAP_ERR_TLS_ERROR;
+		return (-1);
+	}
+
+	if (tls_handshake_wrapper(ldap->tls) == -1) {
+		ldap->err = ALDAP_ERR_TLS_ERROR;
+		return (-1);
+	}
+
+	return (0);
+}
+
+int
+aldap_send(struct aldap *ldap, struct ber_element *root)
+{
+	void *ptr;
+	char *data;
+	size_t len, done;
+	ssize_t error, wrote;
+
+	len = ober_calc_len(root);
+	error = ober_write_elements(&ldap->ber, root);
+	ober_free_elements(root);
+	if (error == -1)
+		return -1;
+
+	ober_get_writebuf(&ldap->ber, &ptr);
+	done = 0;
+	data = ptr;
+	while (len > 0) {
+		if (ldap->tls != NULL) {
+			wrote = tls_write(ldap->tls, data + done, len);
+			if (wrote == TLS_WANT_POLLIN ||
+			    wrote == TLS_WANT_POLLOUT)
+				continue;
+		} else
+			wrote = write(ldap->fd, data + done, len);
+
+		if (wrote == -1)
+			return -1;
+
+		len -= wrote;
+		done += wrote;
+	}
+
+	return 0;
+}
+
+int
+aldap_req_starttls(struct aldap *ldap)
+{
+	struct ber_element *root = NULL, *ber;
+
+	if ((root = ober_add_sequence(NULL)) == NULL)
+		goto fail;
+
+	ber = ober_printf_elements(root, "d{tst", ++ldap->msgid, BER_CLASS_APP,
+	    LDAP_REQ_EXTENDED, LDAP_STARTTLS_OID, BER_CLASS_CONTEXT, 0);
+	if (ber == NULL) {
+		ldap->err = ALDAP_ERR_OPERATION_FAILED;
+		goto fail;
+	}
+
+	if (aldap_send(ldap, root) == -1)
+		goto fail;
+
+	return (ldap->msgid);
+fail:
+	if (root != NULL)
+		ober_free_elements(root);
+
+	ldap->err = ALDAP_ERR_OPERATION_FAILED;
+	return (-1);
+}
+
 int
 aldap_bind(struct aldap *ldap, char *binddn, char *bindcred)
 {
 	struct ber_element *root = NULL, *elm;
-	int error;
 
 	if (binddn == NULL)
 		binddn = "";
 	if (bindcred == NULL)
 		bindcred = "";
 
-	if ((root = ber_add_sequence(NULL)) == NULL)
+	if ((root = ober_add_sequence(NULL)) == NULL)
 		goto fail;
 
-	elm = ber_printf_elements(root, "d{tdsst", ++ldap->msgid, BER_CLASS_APP,
-	    (unsigned long)LDAP_REQ_BIND, ALDAP_VERSION, binddn, bindcred,
-	    BER_CLASS_CONTEXT, (unsigned long)LDAP_AUTH_SIMPLE);
+	elm = ober_printf_elements(root, "d{tdsst", ++ldap->msgid, BER_CLASS_APP,
+	    LDAP_REQ_BIND, ALDAP_VERSION, binddn, bindcred, BER_CLASS_CONTEXT,
+	    LDAP_AUTH_SIMPLE);
 	if (elm == NULL)
 		goto fail;
 
 	LDAP_DEBUG("aldap_bind", root);
 
-	error = ber_write_elements(&ldap->ber, root);
-	ber_free_elements(root);
-	root = NULL;
-	if (error == -1)
+	if (aldap_send(ldap, root) == -1) {
+		root = NULL;
 		goto fail;
-
+	}
 	return (ldap->msgid);
 fail:
 	if (root != NULL)
-		ber_free_elements(root);
+		ober_free_elements(root);
 
 	ldap->err = ALDAP_ERR_OPERATION_FAILED;
 	return (-1);
@@ -117,27 +235,24 @@ int
 aldap_unbind(struct aldap *ldap)
 {
 	struct ber_element *root = NULL, *elm;
-	int error;
 
-	if ((root = ber_add_sequence(NULL)) == NULL)
+	if ((root = ober_add_sequence(NULL)) == NULL)
 		goto fail;
-	elm = ber_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP,
+	elm = ober_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP,
 	    LDAP_REQ_UNBIND_30);
 	if (elm == NULL)
 		goto fail;
 
 	LDAP_DEBUG("aldap_unbind", root);
 
-	error = ber_write_elements(&ldap->ber, root);
-	ber_free_elements(root);
-	root = NULL;
-	if (error == -1)
+	if (aldap_send(ldap, root) == -1) {
+		root = NULL;
 		goto fail;
-
+	}
 	return (ldap->msgid);
 fail:
 	if (root != NULL)
-		ber_free_elements(root);
+		ober_free_elements(root);
 
 	ldap->err = ALDAP_ERR_OPERATION_FAILED;
 
@@ -150,21 +265,21 @@ aldap_search(struct aldap *ldap, char *basedn, enum scope scope, char *filter,
     struct aldap_page_control *page)
 {
 	struct ber_element *root = NULL, *ber, *c;
-	int i, error;
+	int i;
 
-	if ((root = ber_add_sequence(NULL)) == NULL)
+	if ((root = ober_add_sequence(NULL)) == NULL)
 		goto fail;
 
-	ber = ber_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP,
-	    (unsigned long) LDAP_REQ_SEARCH);
+	ber = ober_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP,
+	    LDAP_REQ_SEARCH);
 	if (ber == NULL) {
 		ldap->err = ALDAP_ERR_OPERATION_FAILED;
 		goto fail;
 	}
 
-	c = ber;
-	ber = ber_printf_elements(ber, "sEEddb", basedn, (long long)scope,
-	                         (long long)LDAP_DEREF_NEVER, sizelimit,
+	c = ber;	
+	ber = ober_printf_elements(ber, "sEEddb", basedn, (long long)scope,
+	                         (long long)LDAP_DEREF_NEVER, sizelimit, 
 				 timelimit, typesonly);
 	if (ber == NULL) {
 		ldap->err = ALDAP_ERR_OPERATION_FAILED;
@@ -176,11 +291,11 @@ aldap_search(struct aldap *ldap, char *basedn, enum scope scope, char *filter,
 		goto fail;
 	}
 
-	if ((ber = ber_add_sequence(ber)) == NULL)
+	if ((ber = ober_add_sequence(ber)) == NULL)
 		goto fail;
 	if (attrs != NULL)
 		for (i = 0; attrs[i] != NULL; i++) {
-			if ((ber = ber_add_string(ber, attrs[i])) == NULL)
+			if ((ber = ober_add_string(ber, attrs[i])) == NULL)
 				goto fail;
 		}
 
@@ -188,10 +303,8 @@ aldap_search(struct aldap *ldap, char *basedn, enum scope scope, char *filter,
 
 	LDAP_DEBUG("aldap_search", root);
 
-	error = ber_write_elements(&ldap->ber, root);
-	ber_free_elements(root);
-	root = NULL;
-	if (error == -1) {
+	if (aldap_send(ldap, root) == -1) {
+		root = NULL;
 		ldap->err = ALDAP_ERR_OPERATION_FAILED;
 		goto fail;
 	}
@@ -200,7 +313,7 @@ aldap_search(struct aldap *ldap, char *basedn, enum scope scope, char *filter,
 
 fail:
 	if (root != NULL)
-		ber_free_elements(root);
+		ober_free_elements(root);
 
 	return (-1);
 }
@@ -209,37 +322,36 @@ int
 aldap_create_page_control(struct ber_element *elm, int size,
     struct aldap_page_control *page)
 {
-	int len;
+	ssize_t len;
 	struct ber c;
 	struct ber_element *ber = NULL;
 
 	c.br_wbuf = NULL;
-	c.fd = -1;
 
-	ber = ber_add_sequence(NULL);
+	ber = ober_add_sequence(NULL);
 
 	if (page == NULL) {
-		if (ber_printf_elements(ber, "ds", 50, "") == NULL)
+		if (ober_printf_elements(ber, "ds", 50, "") == NULL)
 			goto fail;
 	} else {
-		if (ber_printf_elements(ber, "dx", 50, page->cookie,
+		if (ober_printf_elements(ber, "dx", 50, page->cookie,
 			    page->cookie_len) == NULL)
 			goto fail;
 	}
 
-	if ((len = ber_write_elements(&c, ber)) < 1)
+	if ((len = ober_write_elements(&c, ber)) < 1)
 		goto fail;
-	if (ber_printf_elements(elm, "{t{sx", 2, 0, LDAP_PAGED_OID,
+	if (ober_printf_elements(elm, "{t{sx", 2, 0, LDAP_PAGED_OID,
 		                c.br_wbuf, (size_t)len) == NULL)
 		goto fail;
 
-	ber_free_elements(ber);
-	ber_free(&c);
+	ober_free_elements(ber);
+	ober_free(&c);
 	return len;
 fail:
 	if (ber != NULL)
-		ber_free_elements(ber);
-	ber_free(&c);
+		ober_free_elements(ber);
+	ober_free(&c);	
 
 	return (-1);
 }
@@ -248,20 +360,52 @@ struct aldap_message *
 aldap_parse(struct aldap *ldap)
 {
 	int			 class;
-	unsigned long		 type;
+	unsigned int		 type;
 	long long		 msgid = 0;
 	struct aldap_message	*m;
 	struct ber_element	*a = NULL, *ep;
+	char			 rbuf[512];
+	int			 ret, retry;
 
 	if ((m = calloc(1, sizeof(struct aldap_message))) == NULL)
 		return NULL;
 
-	if ((m->msg = ber_read_elements(&ldap->ber, NULL)) == NULL)
-		goto parsefail;
+	retry = 0;
+	while (m->msg == NULL) {
+		if (retry || EVBUFFER_LENGTH(ldap->buf) == 0) {
+			if (ldap->tls) {
+				ret = tls_read(ldap->tls, rbuf, sizeof(rbuf));
+				if (ret == TLS_WANT_POLLIN ||
+				    ret == TLS_WANT_POLLOUT)
+					continue;
+			} else
+				ret = read(ldap->fd, rbuf, sizeof(rbuf));
+
+			if (ret <= 0) {
+				goto parsefail;
+			}
+
+			evbuffer_add(ldap->buf, rbuf, ret);
+		}
+
+		if (EVBUFFER_LENGTH(ldap->buf) > 0) {
+			ober_set_readbuf(&ldap->ber, EVBUFFER_DATA(ldap->buf),
+			    EVBUFFER_LENGTH(ldap->buf));
+			errno = 0;
+			m->msg = ober_read_elements(&ldap->ber, NULL);
+			if (errno != 0 && errno != ECANCELED) {
+				goto parsefail;
+			}
+
+			retry = 1;
+		}
+	}
+
+	evbuffer_drain(ldap->buf, ldap->ber.br_rptr - ldap->ber.br_rbuf);
 
 	LDAP_DEBUG("message", m->msg);
 
-	if (ber_scanf_elements(m->msg, "{ite", &msgid, &class, &type, &a) != 0)
+	if (ober_scanf_elements(m->msg, "{ite", &msgid, &class, &type, &a) != 0)
 		goto parsefail;
 	m->msgid = msgid;
 	m->message_type = type;
@@ -275,15 +419,17 @@ aldap_parse(struct aldap *ldap)
 	case LDAP_RES_MODRDN:
 	case LDAP_RES_COMPARE:
 	case LDAP_RES_SEARCH_RESULT:
-		if (ber_scanf_elements(m->protocol_op, "{EeSeSe",
-		    &m->body.res.rescode, &m->dn, &m->body.res.diagmsg, &a) != 0)
+		if (ober_scanf_elements(m->protocol_op, "{EeSe",
+		    &m->body.res.rescode, &m->dn, &m->body.res.diagmsg) != 0)
 			goto parsefail;
-		if (m->body.res.rescode == LDAP_REFERRAL)
-			if (ber_scanf_elements(a, "{e", &m->references) != 0)
+		if (m->body.res.rescode == LDAP_REFERRAL) {
+			a = m->body.res.diagmsg->be_next;
+			if (ober_scanf_elements(a, "{e", &m->references) != 0)
 				goto parsefail;
+		}
 		if (m->msg->be_sub) {
 			for (ep = m->msg->be_sub; ep != NULL; ep = ep->be_next) {
-				ber_scanf_elements(ep, "t", &class, &type);
+				ober_scanf_elements(ep, "t", &class, &type);
 				if (class == 2 && type == 0)
 					m->page = aldap_parse_page_control(ep->be_sub->be_sub,
 					    ep->be_sub->be_sub->be_len);
@@ -292,25 +438,32 @@ aldap_parse(struct aldap *ldap)
 			m->page = NULL;
 		break;
 	case LDAP_RES_SEARCH_ENTRY:
-		if (ber_scanf_elements(m->protocol_op, "{eS{e", &m->dn,
+		if (ober_scanf_elements(m->protocol_op, "{eS{e", &m->dn,
 		    &m->body.search.attrs) != 0)
 			goto parsefail;
 		break;
 	case LDAP_RES_SEARCH_REFERENCE:
-		if (ber_scanf_elements(m->protocol_op, "{e", &m->references) != 0)
+		if (ober_scanf_elements(m->protocol_op, "{e", &m->references) != 0)
+			goto parsefail;
+		break;
+	case LDAP_RES_EXTENDED:
+		if (ober_scanf_elements(m->protocol_op, "{E",
+		    &m->body.res.rescode) != 0) {
 			goto parsefail;
+		}
 		break;
 	}
 
 	return m;
 parsefail:
+	evbuffer_drain(ldap->buf, EVBUFFER_LENGTH(ldap->buf));
 	ldap->err = ALDAP_ERR_PARSER_ERROR;
 	aldap_freemsg(m);
 	return NULL;
 }
 
 struct aldap_page_control *
-aldap_parse_page_control(struct ber_element *control, size_t len)
+aldap_parse_page_control(struct ber_element *control, size_t len) 
 {
 	char *oid, *s;
 	char *encoded;
@@ -319,42 +472,38 @@ aldap_parse_page_control(struct ber_element *control, size_t len)
 	struct aldap_page_control *page;
 
 	b.br_wbuf = NULL;
-	b.fd = -1;
-	ber_scanf_elements(control, "ss", &oid, &encoded);
-	ber_set_readbuf(&b, encoded, control->be_next->be_len);
-	elm = ber_read_elements(&b, NULL);
-	if (elm == NULL) {
-		ber_free(&b);
-		return NULL;
-	}
+	ober_scanf_elements(control, "ss", &oid, &encoded);
+	ober_set_readbuf(&b, encoded, control->be_next->be_len);
+	elm = ober_read_elements(&b, NULL);
 
 	if ((page = malloc(sizeof(struct aldap_page_control))) == NULL) {
-		ber_free_elements(elm);
-		ber_free(&b);
+		if (elm != NULL)
+			ober_free_elements(elm);
+		ober_free(&b);
 		return NULL;
 	}
 
-	ber_scanf_elements(elm->be_sub, "is", &page->size, &s);
+	ober_scanf_elements(elm->be_sub, "is", &page->size, &s);
 	page->cookie_len = elm->be_sub->be_next->be_len;
 
 	if ((page->cookie = malloc(page->cookie_len)) == NULL) {
-		ber_free_elements(elm);
-		ber_free(&b);
+		if (elm != NULL)
+			ober_free_elements(elm);
+		ober_free(&b);
 		free(page);
 		return NULL;
 	}
 	memcpy(page->cookie, s, page->cookie_len);
 
-	ber_free_elements(elm);
-	ber_free(&b);
+	ober_free_elements(elm);
+	ober_free(&b);
 	return page;
 }
 
 void
 aldap_freepage(struct aldap_page_control *page)
 {
-	if (page->cookie)
-		free(page->cookie);
+	free(page->cookie);
 	free(page);
 }
 
@@ -362,7 +511,7 @@ void
 aldap_freemsg(struct aldap_message *msg)
 {
 	if (msg->msg)
-		ber_free_elements(msg->msg);
+		ober_free_elements(msg->msg);
 	free(msg);
 }
 
@@ -380,13 +529,13 @@ aldap_get_dn(struct aldap_message *msg)
 	if (msg->dn == NULL)
 		return NULL;
 
-	if (ber_get_string(msg->dn, &dn) == -1)
+	if (ober_get_string(msg->dn, &dn) == -1)
 		return NULL;
 
 	return utoa(dn);
 }
 
-char **
+struct aldap_stringset *
 aldap_get_references(struct aldap_message *msg)
 {
 	if (msg->references == NULL)
@@ -416,7 +565,7 @@ aldap_get_diagmsg(struct aldap_message *msg)
 	if (msg->body.res.diagmsg == NULL)
 		return NULL;
 
-	if (ber_get_string(msg->body.res.diagmsg, &s) == -1)
+	if (ober_get_string(msg->body.res.diagmsg, &s) == -1)
 		return NULL;
 
 	return utoa(s);
@@ -432,7 +581,7 @@ aldap_count_attrs(struct aldap_message *msg)
 		return (-1);
 
 	for (i = 0, a = msg->body.search.attrs;
-	    a != NULL && ber_get_eoc(a) != 0;
+	    a != NULL && ober_get_eoc(a) != 0;
 	    i++, a = a->be_next)
 		;
 
@@ -440,17 +589,18 @@ aldap_count_attrs(struct aldap_message *msg)
 }
 
 int
-aldap_first_attr(struct aldap_message *msg, char **outkey, char ***outvalues)
+aldap_first_attr(struct aldap_message *msg, char **outkey,
+    struct aldap_stringset **outvalues)
 {
-	struct ber_element *b, *c;
+	struct ber_element *b;
 	char *key;
-	char **ret;
+	struct aldap_stringset *ret;
 
 	if (msg->body.search.attrs == NULL)
 		goto fail;
 
-	if (ber_scanf_elements(msg->body.search.attrs, "{s(e)}e",
-	    &key, &b, &c) != 0)
+	if (ober_scanf_elements(msg->body.search.attrs, "{s(e)}",
+	    &key, &b) != 0)
 		goto fail;
 
 	msg->body.search.iter = msg->body.search.attrs->be_next;
@@ -469,22 +619,22 @@ fail:
 }
 
 int
-aldap_next_attr(struct aldap_message *msg, char **outkey, char ***outvalues)
+aldap_next_attr(struct aldap_message *msg, char **outkey,
+    struct aldap_stringset **outvalues)
 {
-	struct ber_element *a, *b;
+	struct ber_element *a;
 	char *key;
-	char **ret;
+	struct aldap_stringset *ret;
 
 	if (msg->body.search.iter == NULL)
 		goto notfound;
 
 	LDAP_DEBUG("attr", msg->body.search.iter);
 
-	if (ber_get_eoc(msg->body.search.iter) == 0)
+	if (ober_get_eoc(msg->body.search.iter) == 0)
 		goto notfound;
 
-	if (ber_scanf_elements(msg->body.search.iter, "{s(e)}e", &key, &a, &b)
-	    != 0)
+	if (ober_scanf_elements(msg->body.search.iter, "{s(e)}", &key, &a) != 0)
 		goto fail;
 
 	msg->body.search.iter = msg->body.search.iter->be_next;
@@ -504,11 +654,12 @@ notfound:
 }
 
 int
-aldap_match_attr(struct aldap_message *msg, char *inkey, char ***outvalues)
+aldap_match_attr(struct aldap_message *msg, char *inkey,
+    struct aldap_stringset **outvalues)
 {
 	struct ber_element *a, *b;
 	char *descr = NULL;
-	char **ret;
+	struct aldap_stringset *ret;
 
 	if (msg->body.search.attrs == NULL)
 		goto fail;
@@ -518,9 +669,9 @@ aldap_match_attr(struct aldap_message *msg, char *inkey, char ***outvalues)
 	for (a = msg->body.search.attrs;;) {
 		if (a == NULL)
 			goto notfound;
-		if (ber_get_eoc(a) == 0)
+		if (ober_get_eoc(a) == 0)
 			goto notfound;
-		if (ber_scanf_elements(a, "{s(e", &descr, &b) != 0)
+		if (ober_scanf_elements(a, "{s(e", &descr, &b) != 0)
 			goto fail;
 		if (strcasecmp(descr, inkey) == 0)
 			goto attrfound;
@@ -541,16 +692,12 @@ notfound:
 }
 
 int
-aldap_free_attr(char **values)
+aldap_free_attr(struct aldap_stringset *values)
 {
-	int i;
-
 	if (values == NULL)
 		return -1;
 
-	for (i = 0; values[i] != NULL; i++)
-		free(values[i]);
-
+	free(values->str);
 	free(values);
 
 	return (1);
@@ -569,15 +716,24 @@ aldap_parse_url(const char *url, struct aldap_url *lu)
 	const char	*errstr = NULL;
 	int		 i;
 
-	if ((lu->buffer = strdup(url)) == NULL)
+	if ((lu->buffer = p = strdup(url)) == NULL)
 		return (-1);
-	p = lu->buffer;
 
 	/* protocol */
-	if (strncasecmp(LDAP_URL, p, strlen(LDAP_URL)) != 0)
-		goto fail;
-	lu->protocol = LDAP;
-	p += strlen(LDAP_URL);
+	if (strncasecmp(LDAP_URL, p, strlen(LDAP_URL)) == 0) {
+		lu->protocol = LDAP;
+		p += strlen(LDAP_URL);
+	} else if (strncasecmp(LDAPS_URL, p, strlen(LDAPS_URL)) == 0) {
+		lu->protocol = LDAPS;
+		p += strlen(LDAPS_URL);
+	} else if (strncasecmp(LDAPTLS_URL, p, strlen(LDAPTLS_URL)) == 0) {
+		lu->protocol = LDAPTLS;
+		p += strlen(LDAPTLS_URL);
+	} else if (strncasecmp(LDAPI_URL, p, strlen(LDAPI_URL)) == 0) {
+		lu->protocol = LDAPI;
+		p += strlen(LDAPI_URL);
+	} else
+		lu->protocol = -1;
 
 	/* host and optional port */
 	if ((forward = strchr(p, '/')) != NULL)
@@ -595,6 +751,8 @@ aldap_parse_url(const char *url, struct aldap_url *lu)
 		}
 	} else {
 		lu->port = LDAP_PORT;
+		if (lu->protocol == LDAPS)
+			lu->port = LDAPS_PORT;
 	}
 	/* fail if no host is given */
 	if (strlen(p) == 0)
@@ -667,10 +825,9 @@ fail:
 	return (-1);
 }
 
-#if 0
 int
 aldap_search_url(struct aldap *ldap, char *url, int typesonly, int sizelimit,
-    int timelimit)
+    int timelimit, struct aldap_page_control *page)
 {
 	struct aldap_url *lu;
 
@@ -680,8 +837,8 @@ aldap_search_url(struct aldap *ldap, char *url, int typesonly, int sizelimit,
 	if (aldap_parse_url(url, lu))
 		goto fail;
 
-	if (aldap_search(ldap, lu->dn, lu->scope, lu->filter, lu->attributes,
-	    typesonly, sizelimit, timelimit) == -1)
+	if (aldap_search(ldap, lu->dn, lu->scope, lu->filter, NULL,
+            lu->attributes, typesonly, sizelimit, timelimit, page) == -1)
 		goto fail;
 
 	aldap_free_url(lu);
@@ -690,39 +847,40 @@ fail:
 	aldap_free_url(lu);
 	return (-1);
 }
-#endif
 
 /*
  * internal functions
  */
 
-char **
+struct aldap_stringset *
 aldap_get_stringset(struct ber_element *elm)
 {
 	struct ber_element *a;
 	int i;
-	char **ret;
-	char *s;
+	struct aldap_stringset *ret;
 
 	if (elm->be_type != BER_TYPE_OCTETSTRING)
 		return NULL;
 
-	for (a = elm, i = 1; i > 0 && a != NULL && a->be_type ==
-	    BER_TYPE_OCTETSTRING; a = a->be_next, i++)
+	if ((ret = malloc(sizeof(*ret))) == NULL)
+		return NULL;
+	for (a = elm, ret->len = 0; a != NULL && a->be_type ==
+	    BER_TYPE_OCTETSTRING; a = a->be_next, ret->len++)
 		;
-	if (i == 1)
+	if (ret->len == 0) {
+		free(ret);
 		return NULL;
+	}
 
-	if ((ret = calloc(i + 1, sizeof(char *))) == NULL)
+	if ((ret->str = reallocarray(NULL, ret->len,
+	    sizeof(*(ret->str)))) == NULL) {
+		free(ret);
 		return NULL;
+	}
 
 	for (a = elm, i = 0; a != NULL && a->be_type == BER_TYPE_OCTETSTRING;
-	    a = a->be_next, i++) {
-
-		ber_get_string(a, &s);
-		ret[i] = utoa(s);
-	}
-	ret[i + 1] = NULL;
+	    a = a->be_next, i++)
+		(void) ober_get_ostring(a, &(ret->str[i]));
 
 	return ret;
 }
@@ -751,8 +909,8 @@ ldap_parse_search_filter(struct ber_element *ber, char *filter, const char *key)
 		return (NULL);
 
 	if (*cp != '\0') {
-		ber_free_elements(elm);
-		ber_link_elements(ber, NULL);
+		ober_free_elements(elm);
+		ober_link_elements(ber, NULL);
 		errno = EINVAL;
 		return (NULL);
 	}
@@ -799,10 +957,10 @@ ldap_do_parse_search_filter(struct ber_element *prev, char **cpp, const char *ke
 		else
 			type = LDAP_FILT_OR;
 
-		if ((elm = ber_add_set(prev)) == NULL)
+		if ((elm = ober_add_set(prev)) == NULL)
 			goto callfail;
 		root = elm;
-		ber_set_header(elm, BER_CLASS_CONTEXT, type);
+		ober_set_header(elm, BER_CLASS_CONTEXT, type);
 
 		if (*++cp != '(')		/* opening `(` of filter */
 			goto syntaxfail;
@@ -818,12 +976,12 @@ ldap_do_parse_search_filter(struct ber_element *prev, char **cpp, const char *ke
 		break;
 
 	case '!':		/* NOT */
-		if ((root = ber_add_sequence(prev)) == NULL)
+		if ((root = ober_add_sequence(prev)) == NULL)
 			goto callfail;
-		ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_NOT);
+		ober_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_NOT);
 
 		cp++;				/* now points to sub-filter */
-		if (ldap_do_parse_search_filter(root, &cp, key) == NULL)
+		if ((elm = ldap_do_parse_search_filter(root, &cp, key)) == NULL)
 			goto bad;
 
 		if (*cp != ')')			/* trailing `)` of filter */
@@ -862,18 +1020,18 @@ ldap_do_parse_search_filter(struct ber_element *prev, char **cpp, const char *ke
 		if (strncmp(attr_val, "*)", 2) == 0) {
 			cp++;			/* point to trailing `)` */
 			if ((root =
-			    ber_add_nstring(prev, attr_desc, len)) == NULL)
+			    ober_add_nstring(prev, attr_desc, len)) == NULL)
 				goto bad;
 
-			ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_PRES);
+			ober_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_PRES);
 			break;
 		}
 
-		if ((root = ber_add_sequence(prev)) == NULL)
+		if ((root = ober_add_sequence(prev)) == NULL)
 			goto callfail;
-		ber_set_header(root, BER_CLASS_CONTEXT, type);
+		ober_set_header(root, BER_CLASS_CONTEXT, type);
 
-		if ((elm = ber_add_nstring(root, attr_desc, len)) == NULL)
+		if ((elm = ober_add_nstring(root, attr_desc, len)) == NULL)
 			goto callfail;
 
 		len = strcspn(attr_val, "*)");
@@ -888,9 +1046,9 @@ ldap_do_parse_search_filter(struct ber_element *prev, char **cpp, const char *ke
 
 			cp = attr_val;
 
-			ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_SUBS);
+			ober_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_SUBS);
 
-			if ((elm = ber_add_sequence(elm)) == NULL)
+			if ((elm = ober_add_sequence(elm)) == NULL)
 				goto callfail;
 
 			for (initial = 1;; cp++, initial = 0) {
@@ -917,12 +1075,12 @@ ldap_do_parse_search_filter(struct ber_element *prev, char **cpp, const char *ke
 				if ((parsed_val = parseval(attr_val, len, key)) ==
 				    NULL)
 					goto callfail;
-				elm = ber_add_nstring(elm, parsed_val,
+				elm = ober_add_nstring(elm, parsed_val,
 				    strlen(parsed_val));
 				free(parsed_val);
 				if (elm == NULL)
 					goto callfail;
-				ber_set_header(elm, BER_CLASS_CONTEXT, type);
+				ober_set_header(elm, BER_CLASS_CONTEXT, type);
 				if (type == LDAP_FILT_SUBS_FIN)
 					break;
 			}
@@ -931,7 +1089,7 @@ ldap_do_parse_search_filter(struct ber_element *prev, char **cpp, const char *ke
 
 		if ((parsed_val = parseval(attr_val, len, key)) == NULL)
 			goto callfail;
-		elm = ber_add_nstring(elm, parsed_val, strlen(parsed_val));
+		elm = ober_add_nstring(elm, parsed_val, strlen(parsed_val));
 		free(parsed_val);
 		if (elm == NULL)
 			goto callfail;
@@ -947,8 +1105,8 @@ syntaxfail:		/* XXX -- error reporting */
 callfail:
 bad:
 	if (root != NULL)
-		ber_free_elements(root);
-	ber_link_elements(prev, NULL);
+		ober_free_elements(root);
+	ober_link_elements(prev, NULL);
 	return (NULL);
 }
 
@@ -965,12 +1123,12 @@ ldap_debug_elements(struct ber_element *root)
 	int		 d;
 	char		*buf;
 	size_t		 len;
-	unsigned int	 i;
+	u_int		 i;
 	int		 constructed;
 	struct ber_oid	 o;
 
 	/* calculate lengths */
-	ber_calc_len(root);
+	ober_calc_len(root);
 
 	switch (root->be_encoding) {
 	case BER_TYPE_SEQUENCE:
@@ -1077,7 +1235,7 @@ ldap_debug_elements(struct ber_element *root)
 		break;
 	case BER_CLASS_PRIVATE:
 		fprintf(stderr, "class: private(%u) type: ", root->be_class);
-		fprintf(stderr, "encoding (%lu) type: ", root->be_encoding);
+		fprintf(stderr, "encoding (%u) type: ", root->be_encoding);
 		break;
 	case BER_CLASS_CONTEXT:
 		/* XXX: this is not correct */
@@ -1092,7 +1250,7 @@ ldap_debug_elements(struct ber_element *root)
 		fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
 		break;
 	}
-	fprintf(stderr, "(%lu) encoding %lu ",
+	fprintf(stderr, "(%u) encoding %u ",
 	    root->be_type, root->be_encoding);
 
 	if (constructed)
@@ -1100,28 +1258,28 @@ ldap_debug_elements(struct ber_element *root)
 
 	switch (root->be_encoding) {
 	case BER_TYPE_BOOLEAN:
-		if (ber_get_boolean(root, &d) == -1) {
+		if (ober_get_boolean(root, &d) == -1) {
 			fprintf(stderr, "<INVALID>\n");
 			break;
 		}
 		fprintf(stderr, "%s(%d)\n", d ? "true" : "false", d);
 		break;
 	case BER_TYPE_INTEGER:
-		if (ber_get_integer(root, &v) == -1) {
+		if (ober_get_integer(root, &v) == -1) {
 			fprintf(stderr, "<INVALID>\n");
 			break;
 		}
 		fprintf(stderr, "value %lld\n", v);
 		break;
 	case BER_TYPE_ENUMERATED:
-		if (ber_get_enumerated(root, &v) == -1) {
+		if (ober_get_enumerated(root, &v) == -1) {
 			fprintf(stderr, "<INVALID>\n");
 			break;
 		}
 		fprintf(stderr, "value %lld\n", v);
 		break;
 	case BER_TYPE_BITSTRING:
-		if (ber_get_bitstring(root, (void *)&buf, &len) == -1) {
+		if (ober_get_bitstring(root, (void *)&buf, &len) == -1) {
 			fprintf(stderr, "<INVALID>\n");
 			break;
 		}
@@ -1131,18 +1289,18 @@ ldap_debug_elements(struct ber_element *root)
 		fprintf(stderr, "\n");
 		break;
 	case BER_TYPE_OBJECT:
-		if (ber_get_oid(root, &o) == -1) {
+		if (ober_get_oid(root, &o) == -1) {
 			fprintf(stderr, "<INVALID>\n");
 			break;
 		}
 		fprintf(stderr, "\n");
 		break;
 	case BER_TYPE_OCTETSTRING:
-		if (ber_get_nstring(root, (void *)&buf, &len) == -1) {
+		if (ober_get_nstring(root, (void *)&buf, &len) == -1) {
 			fprintf(stderr, "<INVALID>\n");
 			break;
 		}
-		fprintf(stderr, "string \"%.*s\"\n",  len, buf);
+		fprintf(stderr, "string \"%.*s\"\n",  (int)len, buf);
 		break;
 	case BER_TYPE_NULL:	/* no payload */
 	case BER_TYPE_EOC:
@@ -1164,7 +1322,7 @@ ldap_debug_elements(struct ber_element *root)
 #endif
 
 /*
- * Convert UTF-8 to ASCII.
+ * Strip UTF-8 down to ASCII without validation.
  * notes:
  *	non-ASCII characters are displayed as '?'
  *	the argument u should be a NULL terminated sequence of UTF-8 bytes.
@@ -1176,106 +1334,82 @@ utoa(char *u)
 	char	*str;
 
 	/* calculate the length to allocate */
-	for (len = 0, i = 0; u[i] != '\0'; ) {
-		if ((u[i] & 0xF0) == 0xF0)
-			i += 4;
-		else if ((u[i] & 0xE0) == 0xE0)
-			i += 3;
-		else if ((u[i] & 0xC0) == 0xC0)
-			i += 2;
-		else
-			i += 1;
-		len++;
-	}
+	for (len = 0, i = 0; u[i] != '\0'; i++)
+		if (!isu8cont(u[i]))
+			len++;
 
 	if ((str = calloc(len + 1, sizeof(char))) == NULL)
 		return NULL;
 
 	/* copy the ASCII characters to the newly allocated string */
-	for (i = 0, j = 0; u[i] != '\0'; j++) {
-		if ((u[i] & 0xF0) == 0xF0) {
-			str[j] = '?';
-			i += 4;
-		} else if ((u[i] & 0xE0) == 0xE0) {
-			str[j] = '?';
-			i += 3;
-		} else if ((u[i] & 0xC0) == 0xC0) {
-			str[j] = '?';
-			i += 2;
-		} else {
-			str[j] =  u[i];
-			i += 1;
-		}
-	}
+	for (i = 0, j = 0; u[i] != '\0'; i++)
+		if (!isu8cont(u[i]))
+			str[j++] = isascii((unsigned char)u[i]) ? u[i] : '?';
 
 	return str;
 }
 
+static int
+isu8cont(unsigned char c)
+{
+	return (c & (0x80 | 0x40)) == 0x80;
+}
+
 /*
  * Parse a LDAP value
  * notes:
- *	the argument u should be a NULL terminated sequence of ASCII bytes.
+ *	the argument p should be a NUL-terminated sequence of ASCII bytes
  */
 char *
 parseval(char *p, size_t len, const char *key)
 {
 	char	 hex[3];
-	char	*cp = p, *buffer, *newbuffer;
-	size_t	 size, newsize, i, j, keylen;
-
+	char	*buffer, *newbuffer;
+	size_t	 i, j, keylen, size, newsize;
 	size = len + 1;
+
 	if ((buffer = calloc(1, size)) == NULL)
 		return NULL;
 
 	for (i = j = 0; j < len; i++) {
-		if (i >= size) {
-			newsize = size + 1024;
-			if ((newbuffer = realloc(buffer, newsize)) == NULL) {
-				free(buffer);
-				return (NULL);
-			}
-			buffer = newbuffer;
-			size = newsize;
-		}
-
-		if (cp[j] == '\\') {
-			(void)strlcpy(hex, cp + j + 1, sizeof(hex));
+		if (p[j] == '\\') {
+			strlcpy(hex, p + j + 1, sizeof(hex));
 			buffer[i] = (char)strtoumax(hex, NULL, 16);
 			j += 3;
-		} else if (cp[j] == '%') {
-			switch (cp[j + 1]) {
-			case '%':
-				buffer[i] = '%';
-				j += 2;
-				break;
-			case 's':
-				if (!key) {
-					free(buffer);
-					return NULL;
-				}
-				keylen = strlen(key);
-				if (!keylen) {
-					j += 2;
-					break;
-				}
-				newsize = size + keylen;
-				if ((newbuffer = realloc(buffer, newsize)) == NULL) {
-					free(buffer);
-					return NULL;
-				}
-				buffer = newbuffer;
-				size = newsize;
-				memcpy(buffer + i, key, keylen);
-				i += keylen - 1;
-				j += 2;
-				break;
-			default:
-				buffer[i] = '%';
-				j++;
-				break;
-			}
+		} else if (p[j] == '%') {
+		       switch (p[j + 1]) {
+		       case '%':
+			       buffer[i] = '%';
+			       j += 2;
+			       break;
+		       case 's':
+			       if (!key) {
+				       free(buffer);
+				       return NULL;
+			       }
+			       keylen = strlen(key);
+			       if (!keylen) {
+				       j += 2;
+				       break;
+			       }
+			       newsize = size + keylen;
+			       if ((newbuffer = realloc(buffer, newsize)) == NULL) {
+				       free(buffer);
+				       return NULL;
+			       }
+			       buffer = newbuffer;
+			       size = newsize;
+			       memcpy(buffer + i, key, keylen);
+			       i += keylen - 1;
+			       j += 2;
+			       break;
+		       default:
+			       buffer[i] = '%';
+			       j++;
+			       break;
+		       }
 		} else {
-			buffer[i] = cp[j];
+			buffer[i] = p[j];
 			j++;
 		}
 	}
@@ -1300,6 +1434,9 @@ aldap_get_errno(struct aldap *a, const char **estr)
 	case ALDAP_ERR_OPERATION_FAILED:
 		*estr = "operation failed";
 		break;
+	case ALDAP_ERR_TLS_ERROR:
+		*estr = tls_error(a->tls);
+		break;
 	default:
 		*estr = "unknown";
 		break;
diff --git a/extras/tables/table-ldap/aldap.h b/extras/tables/table-ldap/aldap.h
index c1020dd..e297faa 100644
--- a/extras/tables/table-ldap/aldap.h
+++ b/extras/tables/table-ldap/aldap.h
@@ -1,3 +1,5 @@
+/*	$OpenBSD: aldap.h,v 1.4 2019/05/11 17:46:02 rob Exp $ */
+
 /*
  * Copyright (c) 2008 Alexander Schrijver <aschrij...@openbsd.org>
  * Copyright (c) 2006, 2007 Marc Balmer <mbal...@openbsd.org>
@@ -15,21 +17,34 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <ber.h>
 #include <stdio.h>
-#include "ber.h"
+#include <tls.h>
+
+#define LDAP_URL 		"ldap://";
+#define LDAPS_URL 		"ldaps://"
+#define LDAPTLS_URL 		"ldap+tls://"
+#define LDAPI_URL 		"ldapi://"
 
-#define LDAP_URL "ldap://";
-#define LDAP_PORT "389"
-#define LDAP_PAGED_OID  "1.2.840.113556.1.4.319"
+#define LDAP_PORT 		"389"
+#define LDAPS_PORT 		"636"
+#define LDAP_PAGED_OID		"1.2.840.113556.1.4.319"
+#define LDAP_STARTTLS_OID	"1.3.6.1.4.1.1466.20037"
 
 struct aldap {
 #define ALDAP_ERR_SUCCESS		0
 #define ALDAP_ERR_PARSER_ERROR		1
 #define ALDAP_ERR_INVALID_FILTER	2
 #define ALDAP_ERR_OPERATION_FAILED	3
-	uint8_t		err;
+#define ALDAP_ERR_TLS_ERROR		4
+	u_int8_t	err;
 	int		msgid;
 	struct ber	ber;
+
+	int		fd;
+	struct tls	*tls;
+
+	struct evbuffer *buf;
 };
 
 struct aldap_page_control {
@@ -60,18 +75,25 @@ struct aldap_message {
 		}			 search;
 	} body;
 	struct ber_element	*references;
-	struct aldap_page_control *page;
+	struct aldap_page_control *page; 
 };
 
 enum aldap_protocol {
 	LDAP,
-	LDAPS
+	LDAPS,
+	LDAPTLS,
+	LDAPI
+};
+
+struct aldap_stringset {
+	size_t			 len;
+	struct ber_octetstring	*str;
 };
 
 struct aldap_url {
 	int		 protocol;
 	char		*host;
-	char		*port;
+	char	 	*port;
 	char		*dn;
 #define MAXATTR 1024
 	char		*attributes[MAXATTR];
@@ -100,6 +122,9 @@ enum protocol_op {
 	LDAP_REQ_ABANDON_30	= 16,
 
 	LDAP_RES_SEARCH_REFERENCE = 19,
+
+	LDAP_REQ_EXTENDED	= 23,
+	LDAP_RES_EXTENDED	= 24
 };
 
 enum deref_aliases {
@@ -168,7 +193,7 @@ enum result_code {
 	LDAP_OTHER				= 80,
 };
 
-enum ldap_filter {
+enum filter {
 	LDAP_FILT_AND		= 0,
 	LDAP_FILT_OR		= 1,
 	LDAP_FILT_NOT		= 2,
@@ -180,17 +205,21 @@ enum ldap_filter {
 	LDAP_FILT_APPR		= 8,
 };
 
-enum ldap_subfilter {
+enum subfilter {
 	LDAP_FILT_SUBS_INIT	= 0,
 	LDAP_FILT_SUBS_ANY	= 1,
 	LDAP_FILT_SUBS_FIN	= 2,
 };
 
-struct aldap		*aldap_init(int fd);
+struct aldap		*aldap_init(int);
+int			 aldap_tls(struct aldap *, struct tls_config *,
+			    const char *);
 int			 aldap_close(struct aldap *);
 struct aldap_message	*aldap_parse(struct aldap *);
 void			 aldap_freemsg(struct aldap_message *);
 
+int	 		 aldap_req_starttls(struct aldap *);
+
 int	 aldap_bind(struct aldap *, char *, char *);
 int	 aldap_unbind(struct aldap *);
 int	 aldap_search(struct aldap *, char *, enum scope, char *, const char *, char **, int, int, int, struct aldap_page_control *);
@@ -199,19 +228,21 @@ int	 aldap_get_errno(struct aldap *, const char **);
 int	 aldap_get_resultcode(struct aldap_message *);
 char	*aldap_get_dn(struct aldap_message *);
 char	*aldap_get_diagmsg(struct aldap_message *);
-char	**aldap_get_references(struct aldap_message *);
+struct aldap_stringset	*aldap_get_references(struct aldap_message *);
 void	 aldap_free_references(char **values);
 int	 aldap_parse_url(const char *, struct aldap_url *);
 void	 aldap_free_url(struct aldap_url *);
-#if 0
-int	 aldap_search_url(struct aldap *, char *, int, int, int);
-#endif
+int	 aldap_search_url(struct aldap *, char *, int, int, int,
+	    struct aldap_page_control *);
 
 int	 aldap_count_attrs(struct aldap_message *);
-int	 aldap_match_attr(struct aldap_message *, char *, char ***);
-int	 aldap_first_attr(struct aldap_message *, char **, char ***);
-int	 aldap_next_attr(struct aldap_message *, char **, char ***);
-int	 aldap_free_attr(char **);
+int	 aldap_match_attr(struct aldap_message *, char *,
+	    struct aldap_stringset **);
+int	 aldap_first_attr(struct aldap_message *, char **, struct
+	    aldap_stringset **);
+int	 aldap_next_attr(struct aldap_message *, char **,
+	    struct aldap_stringset **);
+int	 aldap_free_attr(struct aldap_stringset *);
 
 struct aldap_page_control *aldap_parse_page_control(struct ber_element *, size_t len);
 void	 aldap_freepage(struct aldap_page_control *);
diff --git a/extras/tables/table-ldap/ber.c b/extras/tables/table-ldap/ber.c
index 0f92bb2..3aebd14 100644
--- a/extras/tables/table-ldap/ber.c
+++ b/extras/tables/table-ldap/ber.c
@@ -1,5 +1,7 @@
+/*	$OpenBSD: ber.c,v 1.26 2023/11/10 12:12:02 martijn Exp $ */
+
 /*
- * Copyright (c) 2007 Reyk Floeter <r...@vantronix.net>
+ * Copyright (c) 2007, 2012 Reyk Floeter <r...@openbsd.org>
  * Copyright (c) 2006, 2007 Claudio Jeker <clau...@openbsd.org>
  * Copyright (c) 2006, 2007 Marc Balmer <mbal...@openbsd.org>
  *
@@ -25,14 +27,13 @@
 #include <stdlib.h>
 #include <err.h>	/* XXX for debug output */
 #include <stdio.h>	/* XXX for debug output */
+#include <stdint.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdarg.h>
 
 #include "ber.h"
 
-#define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
-
 #define BER_TYPE_CONSTRUCTED	0x20	/* otherwise primitive */
 #define BER_TYPE_SINGLE_MAX	30
 #define BER_TAG_MASK		0x1f
@@ -40,17 +41,16 @@
 #define BER_TAG_TYPE_MASK	0x7f
 #define BER_CLASS_SHIFT		6
 
-static int	ber_dump_element(struct ber *ber, struct ber_element *root);
-static void	ber_dump_header(struct ber *ber, struct ber_element *root);
-static void	ber_putc(struct ber *ber, unsigned char c);
-static void	ber_write(struct ber *ber, void *buf, size_t len);
-static ssize_t	get_id(struct ber *b, unsigned long *tag, int *class,
+static int	ober_dump_element(struct ber *ber, struct ber_element *root);
+static void	ober_dump_header(struct ber *ber, struct ber_element *root);
+static void	ober_putc(struct ber *ber, u_char c);
+static void	ober_write(struct ber *ber, void *buf, size_t len);
+static ssize_t	get_id(struct ber *b, unsigned int *tag, int *class,
     int *cstruct);
 static ssize_t	get_len(struct ber *b, ssize_t *len);
-static ssize_t	ber_read_element(struct ber *ber, struct ber_element *elm);
-static ssize_t	ber_readbuf(struct ber *b, void *buf, size_t nbytes);
-static ssize_t	ber_getc(struct ber *b, unsigned char *c);
-static ssize_t	ber_read(struct ber *ber, void *buf, size_t len);
+static ssize_t	ober_read_element(struct ber *ber, struct ber_element *elm);
+static ssize_t	ober_getc(struct ber *b, u_char *c);
+static ssize_t	ober_read(struct ber *ber, void *buf, size_t len);
 
 #ifdef DEBUG
 #define DPRINTF(...)	printf(__VA_ARGS__)
@@ -59,7 +59,7 @@ static ssize_t	ber_read(struct ber *ber, void *buf, size_t len);
 #endif
 
 struct ber_element *
-ber_get_element(unsigned long encoding)
+ober_get_element(unsigned int encoding)
 {
 	struct ber_element *elm;
 
@@ -67,13 +67,13 @@ ber_get_element(unsigned long encoding)
 		return NULL;
 
 	elm->be_encoding = encoding;
-	ber_set_header(elm, BER_CLASS_UNIVERSAL, BER_TYPE_DEFAULT);
+	ober_set_header(elm, BER_CLASS_UNIVERSAL, BER_TYPE_DEFAULT);
 
 	return elm;
 }
 
 void
-ber_set_header(struct ber_element *elm, int class, unsigned long type)
+ober_set_header(struct ber_element *elm, int class, unsigned int type)
 {
 	elm->be_class = class & BER_CLASS_MASK;
 	if (type == BER_TYPE_DEFAULT)
@@ -82,7 +82,7 @@ ber_set_header(struct ber_element *elm, int class, unsigned long type)
 }
 
 void
-ber_link_elements(struct ber_element *prev, struct ber_element *elm)
+ober_link_elements(struct ber_element *prev, struct ber_element *elm)
 {
 	if (prev != NULL) {
 		if ((prev->be_encoding == BER_TYPE_SEQUENCE ||
@@ -95,7 +95,7 @@ ber_link_elements(struct ber_element *prev, struct ber_element *elm)
 }
 
 struct ber_element *
-ber_unlink_elements(struct ber_element *prev)
+ober_unlink_elements(struct ber_element *prev)
 {
 	struct ber_element *elm;
 
@@ -113,53 +113,53 @@ ber_unlink_elements(struct ber_element *prev)
 }
 
 void
-ber_replace_elements(struct ber_element *prev, struct ber_element *new)
+ober_replace_elements(struct ber_element *prev, struct ber_element *new)
 {
 	struct ber_element *ber, *next;
 
-	ber = ber_unlink_elements(prev);
-	next = ber_unlink_elements(ber);
-	ber_link_elements(new, next);
-	ber_link_elements(prev, new);
+	ber = ober_unlink_elements(prev);
+	next = ober_unlink_elements(ber);
+	ober_link_elements(new, next);
+	ober_link_elements(prev, new);
 
 	/* cleanup old element */
-	ber_free_elements(ber);
+	ober_free_elements(ber);
 }
 
 struct ber_element *
-ber_add_sequence(struct ber_element *prev)
+ober_add_sequence(struct ber_element *prev)
 {
 	struct ber_element *elm;
 
-	if ((elm = ber_get_element(BER_TYPE_SEQUENCE)) == NULL)
+	if ((elm = ober_get_element(BER_TYPE_SEQUENCE)) == NULL)
 		return NULL;
 
-	ber_link_elements(prev, elm);
+	ober_link_elements(prev, elm);
 
 	return elm;
 }
 
 struct ber_element *
-ber_add_set(struct ber_element *prev)
+ober_add_set(struct ber_element *prev)
 {
 	struct ber_element *elm;
 
-	if ((elm = ber_get_element(BER_TYPE_SET)) == NULL)
+	if ((elm = ober_get_element(BER_TYPE_SET)) == NULL)
 		return NULL;
 
-	ber_link_elements(prev, elm);
+	ober_link_elements(prev, elm);
 
 	return elm;
 }
 
 struct ber_element *
-ber_add_enumerated(struct ber_element *prev, long long val)
+ober_add_enumerated(struct ber_element *prev, long long val)
 {
 	struct ber_element *elm;
-	unsigned int i, len = 0;
-	unsigned char cur, last = 0;
+	u_int i, len = 0;
+	u_char cur, last = 0;
 
-	if ((elm = ber_get_element(BER_TYPE_ENUMERATED)) == NULL)
+	if ((elm = ober_get_element(BER_TYPE_ENUMERATED)) == NULL)
 		return NULL;
 
 	elm->be_numeric = val;
@@ -176,19 +176,19 @@ ber_add_enumerated(struct ber_element *prev, long long val)
 	}
 	elm->be_len = len + 1;
 
-	ber_link_elements(prev, elm);
+	ober_link_elements(prev, elm);
 
 	return elm;
 }
 
 struct ber_element *
-ber_add_integer(struct ber_element *prev, long long val)
+ober_add_integer(struct ber_element *prev, long long val)
 {
 	struct ber_element *elm;
-	unsigned int i, len = 0;
-	unsigned char cur, last = 0;
+	u_int i, len = 0;
+	u_char cur, last = 0;
 
-	if ((elm = ber_get_element(BER_TYPE_INTEGER)) == NULL)
+	if ((elm = ober_get_element(BER_TYPE_INTEGER)) == NULL)
 		return NULL;
 
 	elm->be_numeric = val;
@@ -205,157 +205,196 @@ ber_add_integer(struct ber_element *prev, long long val)
 	}
 	elm->be_len = len + 1;
 
-	ber_link_elements(prev, elm);
+	ober_link_elements(prev, elm);
 
 	return elm;
 }
 
 int
-ber_get_integer(struct ber_element *elm, long long *n)
+ober_get_integer(struct ber_element *elm, long long *n)
 {
 	if (elm->be_encoding != BER_TYPE_INTEGER)
 		return -1;
 
-	*n = elm->be_numeric;
+	if (n != NULL)
+		*n = elm->be_numeric;
 	return 0;
 }
 
 int
-ber_get_enumerated(struct ber_element *elm, long long *n)
+ober_get_enumerated(struct ber_element *elm, long long *n)
 {
 	if (elm->be_encoding != BER_TYPE_ENUMERATED)
 		return -1;
 
-	*n = elm->be_numeric;
+	if (n != NULL)
+		*n = elm->be_numeric;
 	return 0;
 }
 
-
 struct ber_element *
-ber_add_boolean(struct ber_element *prev, int boolean)
+ober_add_boolean(struct ber_element *prev, int bool)
 {
 	struct ber_element *elm;
 
-	if ((elm = ber_get_element(BER_TYPE_BOOLEAN)) == NULL)
+	if ((elm = ober_get_element(BER_TYPE_BOOLEAN)) == NULL)
 		return NULL;
 
-	elm->be_numeric = boolean ? 0xff : 0;
+	elm->be_numeric = bool ? 0xff : 0;
 	elm->be_len = 1;
 
-	ber_link_elements(prev, elm);
+	ober_link_elements(prev, elm);
 
 	return elm;
 }
 
 int
-ber_get_boolean(struct ber_element *elm, int *b)
+ober_get_boolean(struct ber_element *elm, int *b)
 {
 	if (elm->be_encoding != BER_TYPE_BOOLEAN)
 		return -1;
 
-	*b = !(elm->be_numeric == 0);
+	if (b != NULL)
+		*b = !(elm->be_numeric == 0);
 	return 0;
 }
 
 struct ber_element *
-ber_add_string(struct ber_element *prev, const char *string)
+ober_add_string(struct ber_element *prev, const char *string)
 {
-	return ber_add_nstring(prev, string, strlen(string));
+	return ober_add_nstring(prev, string, strlen(string));
 }
 
 struct ber_element *
-ber_add_nstring(struct ber_element *prev, const char *string0, size_t len)
+ober_add_nstring(struct ber_element *prev, const char *string0, size_t len)
 {
 	struct ber_element *elm;
 	char *string;
 
-	if ((string = calloc(1, len)) == NULL)
+	if ((string = calloc(1, len + 1)) == NULL)
 		return NULL;
-	if ((elm = ber_get_element(BER_TYPE_OCTETSTRING)) == NULL) {
+	if ((elm = ober_get_element(BER_TYPE_OCTETSTRING)) == NULL) {
 		free(string);
 		return NULL;
 	}
 
-	memmove(string, string0, len);
+	bcopy(string0, string, len);
 	elm->be_val = string;
 	elm->be_len = len;
 	elm->be_free = 1;		/* free string on cleanup */
 
-	ber_link_elements(prev, elm);
+	ober_link_elements(prev, elm);
 
 	return elm;
 }
 
+struct ber_element *
+ober_add_ostring(struct ber_element *prev, struct ber_octetstring *s)
+{
+	return ober_add_nstring(prev, s->ostr_val, s->ostr_len);
+}
+
+int
+ober_get_string(struct ber_element *elm, char **s)
+{
+	if (elm->be_encoding != BER_TYPE_OCTETSTRING)
+		return -1;
+	/* XXX some components use getstring on binary data containing \0 */
+#if 0
+	if (memchr(elm->be_val, '\0', elm->be_len) != NULL)
+		return -1;
+#endif
+
+	if (s != NULL)
+		*s = elm->be_val;
+	return 0;
+}
+
 int
-ber_get_string(struct ber_element *elm, char **s)
+ober_get_nstring(struct ber_element *elm, void **p, size_t *len)
 {
 	if (elm->be_encoding != BER_TYPE_OCTETSTRING)
 		return -1;
 
-	*s = elm->be_val;
+	if (len != NULL)
+		*len = elm->be_len;
+	if (p != NULL) {
+		if (len != NULL)
+			*p = elm->be_val;
+		else
+			*p = NULL;
+	}
 	return 0;
 }
 
 int
-ber_get_nstring(struct ber_element *elm, void **p, size_t *len)
+ober_get_ostring(struct ber_element *elm, struct ber_octetstring *s)
 {
 	if (elm->be_encoding != BER_TYPE_OCTETSTRING)
 		return -1;
 
-	*p = elm->be_val;
-	*len = elm->be_len;
+	if (s != NULL) {
+		s->ostr_val = elm->be_val;
+		s->ostr_len = elm->be_len;
+	}
 	return 0;
 }
 
 struct ber_element *
-ber_add_bitstring(struct ber_element *prev, const void *v0, size_t len)
+ober_add_bitstring(struct ber_element *prev, const void *v0, size_t len)
 {
 	struct ber_element *elm;
 	void *v;
 
 	if ((v = calloc(1, len)) == NULL)
 		return NULL;
-	if ((elm = ber_get_element(BER_TYPE_BITSTRING)) == NULL) {
+	if ((elm = ober_get_element(BER_TYPE_BITSTRING)) == NULL) {
 		free(v);
 		return NULL;
 	}
 
-	memmove(v, v0, len);
+	bcopy(v0, v, len);
 	elm->be_val = v;
 	elm->be_len = len;
 	elm->be_free = 1;		/* free string on cleanup */
 
-	ber_link_elements(prev, elm);
+	ober_link_elements(prev, elm);
 
 	return elm;
 }
 
 int
-ber_get_bitstring(struct ber_element *elm, void **v, size_t *len)
+ober_get_bitstring(struct ber_element *elm, void **v, size_t *len)
 {
 	if (elm->be_encoding != BER_TYPE_BITSTRING)
 		return -1;
 
-	*v = elm->be_val;
-	*len = elm->be_len;
+	if (len != NULL)
+		*len = elm->be_len;
+	if (v != NULL) {
+		if (len != NULL)
+			*v = elm->be_val;
+		else
+			*v = NULL;
+	}
 	return 0;
 }
 
 struct ber_element *
-ber_add_null(struct ber_element *prev)
+ober_add_null(struct ber_element *prev)
 {
 	struct ber_element *elm;
 
-	if ((elm = ber_get_element(BER_TYPE_NULL)) == NULL)
+	if ((elm = ober_get_element(BER_TYPE_NULL)) == NULL)
 		return NULL;
 
-	ber_link_elements(prev, elm);
+	ober_link_elements(prev, elm);
 
 	return elm;
 }
 
 int
-ber_get_null(struct ber_element *elm)
+ober_get_null(struct ber_element *elm)
 {
 	if (elm->be_encoding != BER_TYPE_NULL)
 		return -1;
@@ -364,20 +403,20 @@ ber_get_null(struct ber_element *elm)
 }
 
 struct ber_element *
-ber_add_eoc(struct ber_element *prev)
+ober_add_eoc(struct ber_element *prev)
 {
 	struct ber_element *elm;
 
-	if ((elm = ber_get_element(BER_TYPE_EOC)) == NULL)
+	if ((elm = ober_get_element(BER_TYPE_EOC)) == NULL)
 		return NULL;
 
-	ber_link_elements(prev, elm);
+	ober_link_elements(prev, elm);
 
 	return elm;
 }
 
 int
-ber_get_eoc(struct ber_element *elm)
+ober_get_eoc(struct ber_element *elm)
 {
 	if (elm->be_encoding != BER_TYPE_EOC)
 		return -1;
@@ -386,10 +425,10 @@ ber_get_eoc(struct ber_element *elm)
 }
 
 size_t
-ber_oid2ber(struct ber_oid *o, uint8_t *buf, size_t len)
+ober_oid2ber(struct ber_oid *o, u_int8_t *buf, size_t len)
 {
-	uint32_t	 v;
-	unsigned int	 i, j = 0, k;
+	u_int32_t	 v;
+	u_int		 i, j = 0, k;
 
 	if (o->bo_n < BER_MIN_OID_LEN || o->bo_n > BER_MAX_OID_LEN ||
 	    o->bo_id[0] > 2 || o->bo_id[1] > 40)
@@ -398,7 +437,7 @@ ber_oid2ber(struct ber_oid *o, uint8_t *buf, size_t len)
 	v = (o->bo_id[0] * 40) + o->bo_id[1];
 	for (i = 2, j = 0; i <= o->bo_n; v = o->bo_id[i], i++) {
 		for (k = 28; k >= 7; k -= 7) {
-			if (v >= (unsigned int)(1 << k)) {
+			if (v >= (u_int)(1 << k)) {
 				if (len)
 					buf[j] = v >> k | BER_TAG_MORE;
 				j++;
@@ -413,7 +452,7 @@ ber_oid2ber(struct ber_oid *o, uint8_t *buf, size_t len)
 }
 
 int
-ber_string2oid(const char *oidstr, struct ber_oid *o)
+ober_string2oid(const char *oidstr, struct ber_oid *o)
 {
 	char			*sp, *p, str[BUFSIZ];
 	const char		*errstr;
@@ -434,17 +473,38 @@ ber_string2oid(const char *oidstr, struct ber_oid *o)
 	return (0);
 }
 
+int
+ober_oid_cmp(struct ber_oid *a, struct ber_oid *b)
+{
+	size_t	 i, min;
+
+	min = a->bo_n < b->bo_n ? a->bo_n : b->bo_n;
+	for (i = 0; i < min; i++) {
+		if (a->bo_id[i] < b->bo_id[i])
+			return (-1);
+		if (a->bo_id[i] > b->bo_id[i])
+			return (1);
+	}
+	/* a is parent of b */
+	if (a->bo_n < b->bo_n)
+		return (-2);
+	/* a is child of b */
+	if (a->bo_n > b->bo_n)
+		return 2;
+	return (0);
+}
+
 struct ber_element *
-ber_add_oid(struct ber_element *prev, struct ber_oid *o)
+ober_add_oid(struct ber_element *prev, struct ber_oid *o)
 {
 	struct ber_element	*elm;
-	uint8_t			*buf;
+	u_int8_t		*buf;
 	size_t			 len;
 
-	if ((elm = ber_get_element(BER_TYPE_OBJECT)) == NULL)
+	if ((elm = ober_get_element(BER_TYPE_OBJECT)) == NULL)
 		return (NULL);
 
-	if ((len = ber_oid2ber(o, NULL, 0)) == 0)
+	if ((len = ober_oid2ber(o, NULL, 0)) == 0)
 		goto fail;
 
 	if ((buf = calloc(1, len)) == NULL)
@@ -454,57 +514,57 @@ ber_add_oid(struct ber_element *prev, struct ber_oid *o)
 	elm->be_len = len;
 	elm->be_free = 1;
 
-	if (ber_oid2ber(o, buf, len) != len)
+	if (ober_oid2ber(o, buf, len) != len)
 		goto fail;
 
-	ber_link_elements(prev, elm);
+	ober_link_elements(prev, elm);
 
 	return (elm);
 
  fail:
-	ber_free_elements(elm);
+	ober_free_elements(elm);
 	return (NULL);
 }
 
 struct ber_element *
-ber_add_noid(struct ber_element *prev, struct ber_oid *o, int n)
+ober_add_noid(struct ber_element *prev, struct ber_oid *o, int n)
 {
 	struct ber_oid		 no;
 
 	if (n > BER_MAX_OID_LEN)
 		return (NULL);
 	no.bo_n = n;
-	memmove(&no.bo_id, &o->bo_id, sizeof(no.bo_id));
+	bcopy(&o->bo_id, &no.bo_id, sizeof(no.bo_id));
 
-	return (ber_add_oid(prev, &no));
+	return (ober_add_oid(prev, &no));
 }
 
 struct ber_element *
-ber_add_oidstring(struct ber_element *prev, const char *oidstr)
+ober_add_oidstring(struct ber_element *prev, const char *oidstr)
 {
 	struct ber_oid		 o;
 
-	if (ber_string2oid(oidstr, &o) == -1)
+	if (ober_string2oid(oidstr, &o) == -1)
 		return (NULL);
 
-	return (ber_add_oid(prev, &o));
+	return (ober_add_oid(prev, &o));
 }
 
 int
-ber_get_oid(struct ber_element *elm, struct ber_oid *o)
+ober_get_oid(struct ber_element *elm, struct ber_oid *o)
 {
-	uint8_t		*buf;
+	u_int8_t	*buf;
 	size_t		 len, i = 0, j = 0;
 
 	if (elm->be_encoding != BER_TYPE_OBJECT)
 		return (-1);
 
+	if (o == NULL)
+		return 0;
+
 	buf = elm->be_val;
 	len = elm->be_len;
 
-	if (!buf[i])
-		return (-1);
-
 	memset(o, 0, sizeof(*o));
 	o->bo_id[j++] = buf[i] / 40;
 	o->bo_id[j++] = buf[i++] % 40;
@@ -519,122 +579,139 @@ ber_get_oid(struct ber_element *elm, struct ber_oid *o)
 	return (0);
 }
 
+#define _MAX_SEQ		 128
 struct ber_element *
-ber_printf_elements(struct ber_element *ber, char *fmt, ...)
+ober_printf_elements(struct ber_element *ber, char *fmt, ...)
 {
 	va_list			 ap;
-	int			 d, class;
+	int			 d, class, level = 0;
 	size_t			 len;
-	unsigned long		 type;
+	unsigned int		 type;
 	long long		 i;
 	char			*s;
 	void			*p;
 	struct ber_oid		*o;
-	struct ber_element	*sub = ber, *e;
+	struct ber_element	*parent[_MAX_SEQ], *e;
+	struct ber_element	*origber = ber, *firstber = NULL;
+
+	memset(parent, 0, sizeof(struct ber_element *) * _MAX_SEQ);
 
 	va_start(ap, fmt);
+	
 	while (*fmt) {
 		switch (*fmt++) {
 		case 'B':
 			p = va_arg(ap, void *);
 			len = va_arg(ap, size_t);
-			if ((ber = ber_add_bitstring(ber, p, len)) == NULL)
+			if ((ber = ober_add_bitstring(ber, p, len)) == NULL)
 				goto fail;
 			break;
 		case 'b':
 			d = va_arg(ap, int);
-			if ((ber = ber_add_boolean(ber, d)) == NULL)
+			if ((ber = ober_add_boolean(ber, d)) == NULL)
 				goto fail;
 			break;
 		case 'd':
 			d = va_arg(ap, int);
-			if ((ber = ber_add_integer(ber, d)) == NULL)
+			if ((ber = ober_add_integer(ber, d)) == NULL)
 				goto fail;
 			break;
 		case 'e':
 			e = va_arg(ap, struct ber_element *);
-			ber_link_elements(ber, e);
+			ober_link_elements(ber, e);
 			break;
 		case 'E':
 			i = va_arg(ap, long long);
-			if ((ber = ber_add_enumerated(ber, i)) == NULL)
+			if ((ber = ober_add_enumerated(ber, i)) == NULL)
 				goto fail;
 			break;
 		case 'i':
 			i = va_arg(ap, long long);
-			if ((ber = ber_add_integer(ber, i)) == NULL)
+			if ((ber = ober_add_integer(ber, i)) == NULL)
 				goto fail;
 			break;
 		case 'O':
 			o = va_arg(ap, struct ber_oid *);
-			if ((ber = ber_add_oid(ber, o)) == NULL)
+			if ((ber = ober_add_oid(ber, o)) == NULL)
 				goto fail;
 			break;
 		case 'o':
 			s = va_arg(ap, char *);
-			if ((ber = ber_add_oidstring(ber, s)) == NULL)
+			if ((ber = ober_add_oidstring(ber, s)) == NULL)
 				goto fail;
 			break;
 		case 's':
 			s = va_arg(ap, char *);
-			if ((ber = ber_add_string(ber, s)) == NULL)
+			if ((ber = ober_add_string(ber, s)) == NULL)
 				goto fail;
 			break;
 		case 't':
 			class = va_arg(ap, int);
-			type = va_arg(ap, unsigned long);
-			ber_set_header(ber, class, type);
+			type = va_arg(ap, unsigned int);
+			ober_set_header(ber, class, type);
 			break;
 		case 'x':
 			s = va_arg(ap, char *);
 			len = va_arg(ap, size_t);
-			if ((ber = ber_add_nstring(ber, s, len)) == NULL)
+			if ((ber = ober_add_nstring(ber, s, len)) == NULL)
 				goto fail;
 			break;
 		case '0':
-			if ((ber = ber_add_null(ber)) == NULL)
+			if ((ber = ober_add_null(ber)) == NULL)
 				goto fail;
 			break;
 		case '{':
-			if ((ber = sub = ber_add_sequence(ber)) == NULL)
+			if (level >= _MAX_SEQ-1)
+				goto fail;
+			if ((ber= ober_add_sequence(ber)) == NULL)
 				goto fail;
+			parent[level++] = ber;
 			break;
 		case '(':
-			if ((ber = sub = ber_add_set(ber)) == NULL)
+			if (level >= _MAX_SEQ-1)
+				goto fail;
+			if ((ber = ober_add_set(ber)) == NULL)
 				goto fail;
+			parent[level++] = ber;
 			break;
 		case '}':
 		case ')':
-			ber = sub;
+			if (level <= 0 || parent[level - 1] == NULL)
+				goto fail;
+			ber = parent[--level];
 			break;
 		case '.':
-			if ((e = ber_add_eoc(ber)) == NULL)
+			if ((e = ober_add_eoc(ber)) == NULL)
 				goto fail;
 			ber = e;
 			break;
 		default:
 			break;
 		}
+		if (firstber == NULL)
+			firstber = ber;
 	}
 	va_end(ap);
 
 	return (ber);
  fail:
-	ber_free_elements(ber);
+	if (origber != NULL)
+		ober_unlink_elements(origber);
+	ober_free_elements(firstber);
 	return (NULL);
 }
 
 int
-ber_scanf_elements(struct ber_element *ber, char *fmt, ...)
+ober_scanf_elements(struct ber_element *ber, char *fmt, ...)
 {
-#define _MAX_SEQ		 128
 	va_list			 ap;
 	int			*d, level = -1;
-	unsigned long		*t;
-	long long		*i;
+	unsigned int		*t;
+	long long		*i, l;
 	void			**ptr;
 	size_t			*len, ret = 0, n = strlen(fmt);
 	char			**s;
+	off_t			*pos;
 	struct ber_oid		*o;
 	struct ber_element	*parent[_MAX_SEQ], **e;
 
@@ -642,20 +719,35 @@ ber_scanf_elements(struct ber_element *ber, char *fmt, ...)
 
 	va_start(ap, fmt);
 	while (*fmt) {
+		if (ber == NULL && *fmt != '$' && *fmt != '}' && *fmt != ')')
+			goto fail;
 		switch (*fmt++) {
+		case '$':
+			if (ber != NULL)
+				goto fail;
+			ret++;
+			continue;
 		case 'B':
 			ptr = va_arg(ap, void **);
 			len = va_arg(ap, size_t *);
-			if (ber_get_bitstring(ber, ptr, len) == -1)
+			if (ober_get_bitstring(ber, ptr, len) == -1)
 				goto fail;
 			ret++;
 			break;
 		case 'b':
 			d = va_arg(ap, int *);
-			if (ber_get_boolean(ber, d) == -1)
+			if (ober_get_boolean(ber, d) == -1)
 				goto fail;
 			ret++;
 			break;
+		case 'd':
+			d = va_arg(ap, int *);
+			if (ober_get_integer(ber, &l) == -1)
+				goto fail;
+			if (d != NULL)
+				*d = l;
+			ret++;
+			break;
 		case 'e':
 			e = va_arg(ap, struct ber_element **);
 			*e = ber;
@@ -663,19 +755,19 @@ ber_scanf_elements(struct ber_element *ber, char *fmt, ...)
 			continue;
 		case 'E':
 			i = va_arg(ap, long long *);
-			if (ber_get_enumerated(ber, i) == -1)
+			if (ober_get_enumerated(ber, i) == -1)
 				goto fail;
 			ret++;
 			break;
 		case 'i':
 			i = va_arg(ap, long long *);
-			if (ber_get_integer(ber, i) == -1)
+			if (ober_get_integer(ber, i) == -1)
 				goto fail;
 			ret++;
 			break;
 		case 'o':
 			o = va_arg(ap, struct ber_oid *);
-			if (ber_get_oid(ber, o) == -1)
+			if (ober_get_oid(ber, o) == -1)
 				goto fail;
 			ret++;
 			break;
@@ -684,21 +776,23 @@ ber_scanf_elements(struct ber_element *ber, char *fmt, ...)
 			break;
 		case 's':
 			s = va_arg(ap, char **);
-			if (ber_get_string(ber, s) == -1)
+			if (ober_get_string(ber, s) == -1)
 				goto fail;
 			ret++;
 			break;
 		case 't':
 			d = va_arg(ap, int *);
-			t = va_arg(ap, unsigned long *);
-			*d = ber->be_class;
-			*t = ber->be_type;
+			t = va_arg(ap, unsigned int *);
+			if (d != NULL)
+				*d = ber->be_class;
+			if (t != NULL)
+				*t = ber->be_type;
 			ret++;
 			continue;
 		case 'x':
 			ptr = va_arg(ap, void **);
 			len = va_arg(ap, size_t *);
-			if (ber_get_nstring(ber, ptr, len) == -1)
+			if (ober_get_nstring(ber, ptr, len) == -1)
 				goto fail;
 			ret++;
 			break;
@@ -712,12 +806,17 @@ ber_scanf_elements(struct ber_element *ber, char *fmt, ...)
 				goto fail;
 			ret++;
 			break;
+		case 'p':
+			pos = va_arg(ap, off_t *);
+			*pos = ober_getpos(ber);
+			ret++;
+			continue;
 		case '{':
 		case '(':
 			if (ber->be_encoding != BER_TYPE_SEQUENCE &&
 			    ber->be_encoding != BER_TYPE_SET)
 				goto fail;
-			if (ber->be_sub == NULL || level >= _MAX_SEQ-1)
+			if (level >= _MAX_SEQ-1)
 				goto fail;
 			parent[++level] = ber;
 			ber = ber->be_sub;
@@ -725,17 +824,15 @@ ber_scanf_elements(struct ber_element *ber, char *fmt, ...)
 			continue;
 		case '}':
 		case ')':
-			if (parent[level] == NULL)
+			if (level < 0 || parent[level] == NULL)
 				goto fail;
 			ber = parent[level--];
 			ret++;
-			continue;
+			break;
 		default:
 			goto fail;
 		}
 
-		if (ber->be_next == NULL)
-			continue;
 		ber = ber->be_next;
 	}
 	va_end(ap);
@@ -747,24 +844,33 @@ ber_scanf_elements(struct ber_element *ber, char *fmt, ...)
 
 }
 
+ssize_t
+ober_get_writebuf(struct ber *b, void **buf)
+{
+	if (b->br_wbuf == NULL)
+		return -1;
+	*buf = b->br_wbuf;
+	return (b->br_wend - b->br_wbuf);
+}
+
 /*
- * write ber elements to the socket
+ * write ber elements to the write buffer
  *
  * params:
- *	ber	holds the socket
+ *	ber	holds the destination write buffer byte stream
  *	root	fully populated element tree
  *
  * returns:
- *      >=0     number of bytes written
+ *	>=0	number of bytes written
  *	-1	on failure and sets errno
  */
-int
-ber_write_elements(struct ber *ber, struct ber_element *root)
+ssize_t
+ober_write_elements(struct ber *ber, struct ber_element *root)
 {
 	size_t len;
 
 	/* calculate length because only the definite form is required */
-	len = ber_calc_len(root);
+	len = ober_calc_len(root);
 	DPRINTF("write ber element of %zd bytes length\n", len);
 
 	if (ber->br_wbuf != NULL && ber->br_wbuf + len > ber->br_wend) {
@@ -780,21 +886,24 @@ ber_write_elements(struct ber *ber, struct ber_element *root)
 	/* reset write pointer */
 	ber->br_wptr = ber->br_wbuf;
 
-	if (ber_dump_element(ber, root) == -1)
+	if (ober_dump_element(ber, root) == -1)
 		return -1;
 
-	/* XXX this should be moved to a different function */
-	if (ber->fd != -1)
-		return write(ber->fd, ber->br_wbuf, len);
-
 	return (len);
 }
 
+void
+ober_set_readbuf(struct ber *b, void *buf, size_t len)
+{
+	b->br_rbuf = b->br_rptr = buf;
+	b->br_rend = (u_int8_t *)buf + len;
+}
+
 /*
- * read ber elements from the socket
+ * read ber elements from the read buffer
  *
  * params:
- *	ber	holds the socket and lot more
+ *	ber	holds a fully populated read buffer byte stream
  *	root	if NULL, build up an element tree from what we receive on
  *		the wire. If not null, use the specified encoding for the
  *		elements received.
@@ -804,35 +913,93 @@ ber_write_elements(struct ber *ber, struct ber_element *root)
  *	NULL, type mismatch or read error
  */
 struct ber_element *
-ber_read_elements(struct ber *ber, struct ber_element *elm)
+ober_read_elements(struct ber *ber, struct ber_element *elm)
 {
 	struct ber_element *root = elm;
 
 	if (root == NULL) {
-		if ((root = ber_get_element(0)) == NULL)
+		if ((root = ober_get_element(0)) == NULL)
 			return NULL;
 	}
 
 	DPRINTF("read ber elements, root %p\n", root);
 
-	if (ber_read_element(ber, root) == -1) {
+	if (ober_read_element(ber, root) == -1) {
 		/* Cleanup if root was allocated by us */
 		if (elm == NULL)
-			ber_free_elements(root);
+			ober_free_elements(root);
 		return NULL;
 	}
 
 	return root;
 }
 
+off_t
+ober_getpos(struct ber_element *elm)
+{
+	return elm->be_offs;
+}
+
+struct ber_element *
+ober_dup(struct ber_element *orig)
+{
+	struct ber_element *new;
+
+	if ((new = malloc(sizeof(*new))) == NULL)
+		return NULL;
+	memcpy(new, orig, sizeof(*new));
+	new->be_next = NULL;
+	new->be_sub = NULL;
+
+	if (orig->be_next != NULL) {
+		if ((new->be_next = ober_dup(orig->be_next)) == NULL)
+			goto fail;
+	}
+	if (orig->be_encoding == BER_TYPE_SEQUENCE ||
+	    orig->be_encoding == BER_TYPE_SET) {
+		if (orig->be_sub != NULL) {
+			if ((new->be_sub = ober_dup(orig->be_sub)) == NULL)
+				goto fail;
+		}
+	} else if (orig->be_encoding == BER_TYPE_OCTETSTRING ||
+	    orig->be_encoding == BER_TYPE_BITSTRING ||
+	    orig->be_encoding == BER_TYPE_OBJECT) {
+		if (orig->be_val != NULL) {
+			if ((new->be_val = malloc(orig->be_len)) == NULL)
+				goto fail;
+			memcpy(new->be_val, orig->be_val, orig->be_len);
+		}
+	} else
+		new->be_numeric = orig->be_numeric;
+	return new;
+ fail:
+	ober_free_elements(new);
+	return NULL;
+}
+
+void
+ober_free_element(struct ber_element *root)
+{
+	if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE ||
+	    root->be_encoding == BER_TYPE_SET))
+		ober_free_elements(root->be_sub);
+	if (root->be_free && (root->be_encoding == BER_TYPE_OCTETSTRING ||
+	    root->be_encoding == BER_TYPE_BITSTRING ||
+	    root->be_encoding == BER_TYPE_OBJECT))
+		free(root->be_val);
+	free(root);
+}
+
 void
-ber_free_elements(struct ber_element *root)
+ober_free_elements(struct ber_element *root)
 {
+	if (root == NULL)
+		return;
 	if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE ||
 	    root->be_encoding == BER_TYPE_SET))
-		ber_free_elements(root->be_sub);
+		ober_free_elements(root->be_sub);
 	if (root->be_next)
-		ber_free_elements(root->be_next);
+		ober_free_elements(root->be_next);
 	if (root->be_free && (root->be_encoding == BER_TYPE_OCTETSTRING ||
 	    root->be_encoding == BER_TYPE_BITSTRING ||
 	    root->be_encoding == BER_TYPE_OBJECT))
@@ -841,16 +1008,16 @@ ber_free_elements(struct ber_element *root)
 }
 
 size_t
-ber_calc_len(struct ber_element *root)
+ober_calc_len(struct ber_element *root)
 {
-	unsigned long t;
+	unsigned int t;
 	size_t s;
 	size_t size = 2;	/* minimum 1 byte head and 1 byte size */
 
 	/* calculate the real length of a sequence or set */
 	if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE ||
 	    root->be_encoding == BER_TYPE_SET))
-		root->be_len = ber_calc_len(root->be_sub);
+		root->be_len = ober_calc_len(root->be_sub);
 
 	/* fix header length for extended types */
 	if (root->be_type > BER_TYPE_SINGLE_MAX)
@@ -862,27 +1029,50 @@ ber_calc_len(struct ber_element *root)
 
 	/* calculate the length of the following elements */
 	if (root->be_next)
-		size += ber_calc_len(root->be_next);
+		size += ober_calc_len(root->be_next);
 
 	/* This is an empty element, do not use a minimal size */
-	if (root->be_type == BER_TYPE_EOC && root->be_len == 0)
+	if (root->be_class == BER_CLASS_UNIVERSAL &&
+	    root->be_type == BER_TYPE_EOC && root->be_len == 0)
 		return (0);
 
 	return (root->be_len + size);
 }
 
+void
+ober_set_application(struct ber *b, unsigned int (*cb)(struct ber_element *))
+{
+	b->br_application = cb;
+}
+
+void
+ober_set_writecallback(struct ber_element *elm, void (*cb)(void *, size_t),
+    void *arg)
+{
+	elm->be_cb = cb;
+	elm->be_cbarg = arg;
+}
+
+void
+ober_free(struct ber *b)
+{
+	free(b->br_wbuf);
+}
+
 /*
  * internal functions
  */
 
 static int
-ber_dump_element(struct ber *ber, struct ber_element *root)
+ober_dump_element(struct ber *ber, struct ber_element *root)
 {
 	unsigned long long l;
 	int i;
 	uint8_t u;
 
-	ber_dump_header(ber, root);
+	ober_dump_header(ber, root);
+	if (root->be_cb)
+		root->be_cb(root->be_cbarg, ber->br_wptr - ber->br_wbuf);
 
 	switch (root->be_encoding) {
 	case BER_TYPE_BOOLEAN:
@@ -891,35 +1081,34 @@ ber_dump_element(struct ber *ber, struct ber_element *root)
 		l = (unsigned long long)root->be_numeric;
 		for (i = root->be_len; i > 0; i--) {
 			u = (l >> ((i - 1) * 8)) & 0xff;
-			ber_putc(ber, u);
+			ober_putc(ber, u);
 		}
 		break;
 	case BER_TYPE_BITSTRING:
-		return -1;
 	case BER_TYPE_OCTETSTRING:
 	case BER_TYPE_OBJECT:
-		ber_write(ber, root->be_val, root->be_len);
+		ober_write(ber, root->be_val, root->be_len);
 		break;
 	case BER_TYPE_NULL:	/* no payload */
 	case BER_TYPE_EOC:
 		break;
 	case BER_TYPE_SEQUENCE:
 	case BER_TYPE_SET:
-		if (root->be_sub && ber_dump_element(ber, root->be_sub) == -1)
+		if (root->be_sub && ober_dump_element(ber, root->be_sub) == -1)
 			return -1;
 		break;
 	}
 
 	if (root->be_next == NULL)
 		return 0;
-	return ber_dump_element(ber, root->be_next);
+	return ober_dump_element(ber, root->be_next);
 }
 
 static void
-ber_dump_header(struct ber *ber, struct ber_element *root)
+ober_dump_header(struct ber *ber, struct ber_element *root)
 {
-	unsigned char id = 0, t, buf[8];
-	unsigned long type;
+	u_char	id = 0, t, buf[5];
+	unsigned int type;
 	size_t size;
 
 	/* class universal, type encoding depending on type value */
@@ -930,14 +1119,14 @@ ber_dump_header(struct ber *ber, struct ber_element *root)
 		    root->be_encoding == BER_TYPE_SET)
 			id |= BER_TYPE_CONSTRUCTED;
 
-		ber_putc(ber, id);
+		ober_putc(ber, id);
 	} else {
 		id = BER_TAG_MASK | (root->be_class << BER_CLASS_SHIFT);
 		if (root->be_encoding == BER_TYPE_SEQUENCE ||
 		    root->be_encoding == BER_TYPE_SET)
 			id |= BER_TYPE_CONSTRUCTED;
 
-		ber_putc(ber, id);
+		ober_putc(ber, id);
 
 		for (t = 0, type = root->be_type; type > 0; type >>= 7)
 			buf[t++] = type & ~BER_TAG_MORE;
@@ -945,26 +1134,26 @@ ber_dump_header(struct ber *ber, struct ber_element *root)
 		while (t-- > 0) {
 			if (t > 0)
 				buf[t] |= BER_TAG_MORE;
-			ber_putc(ber, buf[t]);
+			ober_putc(ber, buf[t]);
 		}
 	}
 
 	if (root->be_len < BER_TAG_MORE) {
 		/* short form */
-		ber_putc(ber, root->be_len);
+		ober_putc(ber, root->be_len);
 	} else {
 		for (t = 0, size = root->be_len; size > 0; size >>= 8)
 			buf[t++] = size & 0xff;
 
-		ber_putc(ber, t | BER_TAG_MORE);
+		ober_putc(ber, t | BER_TAG_MORE);
 
 		while (t > 0)
-			ber_putc(ber, buf[--t]);
+			ober_putc(ber, buf[--t]);
 	}
 }
 
 static void
-ber_putc(struct ber *ber, unsigned char c)
+ober_putc(struct ber *ber, u_char c)
 {
 	if (ber->br_wptr + 1 <= ber->br_wend)
 		*ber->br_wptr = c;
@@ -972,11 +1161,10 @@ ber_putc(struct ber *ber, unsigned char c)
 }
 
 static void
-ber_write(struct ber *ber, void *buf, size_t len)
+ober_write(struct ber *ber, void *buf, size_t len)
 {
 	if (ber->br_wptr + len <= ber->br_wend)
-		memmove(ber->br_wptr, buf, len);
-
+		bcopy(buf, ber->br_wptr, len);
 	ber->br_wptr += len;
 }
 
@@ -984,13 +1172,13 @@ ber_write(struct ber *ber, void *buf, size_t len)
  * extract a BER encoded tag. There are two types, a short and long form.
  */
 static ssize_t
-get_id(struct ber *b, unsigned long *tag, int *class, int *cstruct)
+get_id(struct ber *b, unsigned int *tag, int *class, int *cstruct)
 {
-	unsigned char u;
+	u_char u;
 	size_t i = 0;
-	unsigned long t = 0;
+	unsigned int t = 0;
 
-	if (ber_getc(b, &u) == -1)
+	if (ober_getc(b, &u) == -1)
 		return -1;
 
 	*class = (u >> BER_CLASS_SHIFT) & BER_CLASS_MASK;
@@ -1002,17 +1190,23 @@ get_id(struct ber *b, unsigned long *tag, int *class, int *cstruct)
 	}
 
 	do {
-		if (ber_getc(b, &u) == -1)
+		if (ober_getc(b, &u) == -1)
 			return -1;
+
+		/* enforce minimal number of octets for tag > 30 */
+		if (i == 0 && (u & ~BER_TAG_MORE) == 0) {
+			errno = EINVAL;
+			return -1;
+		}
+
 		t = (t << 7) | (u & ~BER_TAG_MORE);
 		i++;
+		if (i > sizeof(unsigned int)) {
+			errno = ERANGE;
+			return -1;
+		}
 	} while (u & BER_TAG_MORE);
 
-	if (i > sizeof(unsigned long)) {
-		errno = ERANGE;
-		return -1;
-	}
-
 	*tag = t;
 	return i + 1;
 }
@@ -1023,10 +1217,10 @@ get_id(struct ber *b, unsigned long *tag, int *class, int *cstruct)
 static ssize_t
 get_len(struct ber *b, ssize_t *len)
 {
-	unsigned char u, n;
+	u_char	u, n;
 	ssize_t	s, r;
 
-	if (ber_getc(b, &u) == -1)
+	if (ober_getc(b, &u) == -1)
 		return -1;
 	if ((u & BER_TAG_MORE) == 0) {
 		/* short form */
@@ -1034,15 +1228,30 @@ get_len(struct ber *b, ssize_t *len)
 		return 1;
 	}
 
+	if (u == 0x80) {
+		/* Indefinite length not supported. */
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (u == 0xff) {
+		/* Reserved for future use. */
+		errno = EINVAL;
+		return -1;
+	}
+
 	n = u & ~BER_TAG_MORE;
-	if (sizeof(ssize_t) < n) {
+	/*
+	 * Limit to a decent size that works on all of our architectures.
+	 */
+	if (sizeof(int32_t) < n) {
 		errno = ERANGE;
 		return -1;
 	}
 	r = n + 1;
 
 	for (s = 0; n > 0; n--) {
-		if (ber_getc(b, &u) == -1)
+		if (ober_getc(b, &u) == -1)
 			return -1;
 		s = (s << 8) | u;
 	}
@@ -1053,38 +1262,52 @@ get_len(struct ber *b, ssize_t *len)
 		return -1;
 	}
 
-	if (s == 0) {
-		/* invalid encoding */
-		errno = EINVAL;
-		return -1;
-	}
-
 	*len = s;
 	return r;
 }
 
 static ssize_t
-ber_read_element(struct ber *ber, struct ber_element *elm)
+ober_read_element(struct ber *ber, struct ber_element *elm)
 {
 	long long val = 0;
 	struct ber_element *next;
-	unsigned long type;
-	int i, class, cstruct;
+	unsigned int type;
+	int i, class, cstruct, elements = 0;
 	ssize_t len, r, totlen = 0;
-	unsigned char c;
+	u_char c, last = 0;
 
 	if ((r = get_id(ber, &type, &class, &cstruct)) == -1)
 		return -1;
-	DPRINTF("ber read got class %d type %lu, %s\n",
-	    class, type, cstruct ? "constructive" : "primitive");
+	DPRINTF("ber read got class %d type %u, %s\n",
+	    class, type, cstruct ? "constructed" : "primitive");
 	totlen += r;
 	if ((r = get_len(ber, &len)) == -1)
 		return -1;
 	DPRINTF("ber read element size %zd\n", len);
 	totlen += r + len;
 
+	/* The encoding of boolean, integer, enumerated, and null values
+	 * must be primitive. */
+	if (class == BER_CLASS_UNIVERSAL)
+		if (type == BER_TYPE_BOOLEAN ||
+		    type == BER_TYPE_INTEGER ||
+		    type == BER_TYPE_ENUMERATED ||
+		    type == BER_TYPE_NULL)
+			if (cstruct) {
+				errno = EINVAL;
+				return -1;
+			}
+
+	/* If the total size of the element is larger than the buffer
+	 * don't bother to continue. */
+	if (len > ber->br_rend - ber->br_rptr) {
+		errno = ECANCELED;
+		return -1;
+	}
+
 	elm->be_type = type;
 	elm->be_len = len;
+	elm->be_offs = ber->br_offs;	/* element position within stream */
 	elm->be_class = class;
 
 	if (elm->be_encoding == 0) {
@@ -1109,20 +1332,40 @@ ber_read_element(struct ber *ber, struct ber_element *elm)
 	case BER_TYPE_EOC:	/* End-Of-Content */
 		break;
 	case BER_TYPE_BOOLEAN:
+		if (len != 1) {
+			errno = EINVAL;
+			return -1;
+		}
 	case BER_TYPE_INTEGER:
 	case BER_TYPE_ENUMERATED:
-		if (len > (ssize_t)sizeof(long long))
+		if (len < 1) {
+			errno = EINVAL;
 			return -1;
+		}
+		if (len > (ssize_t)sizeof(long long)) {
+			errno = ERANGE;
+			return -1;
+		}
 		for (i = 0; i < len; i++) {
-			if (ber_getc(ber, &c) != 1)
+			if (ober_getc(ber, &c) != 1)
 				return -1;
+
+			/* smallest number of contents octets only */
+			if ((i == 1 && last == 0 && (c & 0x80) == 0) ||
+			    (i == 1 && last == 0xff && (c & 0x80) != 0)) {
+				errno = EINVAL;
+				return -1;
+			}
+
 			val <<= 8;
 			val |= c;
+			last = c;
 		}
 
 		/* sign extend if MSB is set */
-		if (val >> ((i - 1) * 8) & 0x80)
-			val |= ULLONG_MAX << (i * 8);
+		if (len < (ssize_t)sizeof(long long) &&
+		    (val >> ((len - 1) * 8) & 0x80))
+			val |= ULLONG_MAX << (len * 8);
 		elm->be_numeric = val;
 		break;
 	case BER_TYPE_BITSTRING:
@@ -1131,7 +1374,7 @@ ber_read_element(struct ber *ber, struct ber_element *elm)
 			return -1;
 		elm->be_free = 1;
 		elm->be_len = len;
-		ber_read(ber, elm->be_val, len);
+		ober_read(ber, elm->be_val, len);
 		break;
 	case BER_TYPE_OCTETSTRING:
 	case BER_TYPE_OBJECT:
@@ -1140,28 +1383,47 @@ ber_read_element(struct ber *ber, struct ber_element *elm)
 			return -1;
 		elm->be_free = 1;
 		elm->be_len = len;
-		ber_read(ber, elm->be_val, len);
-		((unsigned char *)elm->be_val)[len] = '\0';
+		ober_read(ber, elm->be_val, len);
+		((u_char *)elm->be_val)[len] = '\0';
 		break;
 	case BER_TYPE_NULL:	/* no payload */
-		if (len != 0)
+		if (len != 0) {
+			errno = EINVAL;
 			return -1;
+		}
 		break;
 	case BER_TYPE_SEQUENCE:
 	case BER_TYPE_SET:
-		if (elm->be_sub == NULL) {
-			if ((elm->be_sub = ber_get_element(0)) == NULL)
+		if (len > 0 && elm->be_sub == NULL) {
+			if ((elm->be_sub = ober_get_element(0)) == NULL)
 				return -1;
 		}
 		next = elm->be_sub;
 		while (len > 0) {
-			r = ber_read_element(ber, next);
-			if (r == -1)
+			/*
+			 * Prevent stack overflow from excessive recursion
+			 * depth in ober_free_elements().
+			 */
+			if (elements >= BER_MAX_SEQ_ELEMENTS) {
+				errno = ERANGE;
+				return -1;
+			}
+			r = ober_read_element(ber, next);
+			if (r == -1) {
+				/* sub-element overflows sequence/set */
+				if (errno == ECANCELED)
+					errno = EINVAL;
 				return -1;
+			}
+			if (r > len) {
+				errno = EINVAL;
+				return -1;
+			}
+			elements++;
 			len -= r;
 			if (len > 0 && next->be_next == NULL) {
-				if ((next->be_next = ber_get_element(0)) ==
-				    NULL)
+				next->be_next = ober_get_element(0);
+				if (next->be_next == NULL)
 					return -1;
 			}
 			next = next->be_next;
@@ -1172,98 +1434,30 @@ ber_read_element(struct ber *ber, struct ber_element *elm)
 }
 
 static ssize_t
-ber_readbuf(struct ber *b, void *buf, size_t nbytes)
+ober_getc(struct ber *b, u_char *c)
 {
-	size_t	 sz;
-	size_t	 len;
-
-	if (b->br_rbuf == NULL)
-		return -1;
-
-	sz = b->br_rend - b->br_rptr;
-	len = MINIMUM(nbytes, sz);
-	if (len == 0) {
-		errno = ECANCELED;
-		return (-1);	/* end of buffer and parser wants more data */
-	}
-
-	memmove(buf, b->br_rptr, len);
-	b->br_rptr += len;
-
-	return (len);
+	return ober_read(b, c, 1);
 }
 
-void
-ber_set_readbuf(struct ber *b, void *buf, size_t len)
+static ssize_t
+ober_read(struct ber *ber, void *buf, size_t len)
 {
-	b->br_rbuf = b->br_rptr = buf;
-	b->br_rend = (uint8_t *)buf + len;
-}
+	size_t	sz;
 
-ssize_t
-ber_get_writebuf(struct ber *b, void **buf)
-{
-	if (b->br_wbuf == NULL)
+	if (ber->br_rbuf == NULL) {
+		errno = ENOBUFS;
 		return -1;
-	*buf = b->br_wbuf;
-	return (b->br_wend - b->br_wbuf);
-}
-
-void
-ber_set_application(struct ber *b, unsigned long (*cb)(struct ber_element *))
-{
-	b->br_application = cb;
-}
-
-void
-ber_free(struct ber *b)
-{
-	if (b->br_wbuf != NULL)
-		free (b->br_wbuf);
-}
-
-static ssize_t
-ber_getc(struct ber *b, unsigned char *c)
-{
-	ssize_t r;
-	/*
-	 * XXX calling read here is wrong in many ways. The most obvious one
-	 * being that we will block till data arrives.
-	 * But for now it is _good enough_ *gulp*
-	 */
-	if (b->fd == -1)
-		r = ber_readbuf(b, c, 1);
-	else
-		r = read(b->fd, c, 1);
-	return r;
-}
+	}
 
-static ssize_t
-ber_read(struct ber *ber, void *buf, size_t len)
-{
-	unsigned char *b = buf;
-	ssize_t	r, remain = len;
+	sz = ber->br_rend - ber->br_rptr;
+	if (len > sz) {
+		errno = ECANCELED;
+		return -1;	/* parser wants more data than available */
+	}
 
-	/*
-	 * XXX calling read here is wrong in many ways. The most obvious one
-	 * being that we will block till data arrives.
-	 * But for now it is _good enough_ *gulp*
-	 */
+	bcopy(ber->br_rptr, buf, len);
+	ber->br_rptr += len;
+	ber->br_offs += len;
 
-	while (remain > 0) {
-		if (ber->fd == -1)
-			r = ber_readbuf(ber, b, remain);
-		else
-			r = read(ber->fd, b, remain);
-		if (r == -1) {
-			if (errno == EINTR || errno == EAGAIN)
-				continue;
-			return -1;
-		}
-		if (r == 0)
-			return (b - (unsigned char *)buf);
-		b += r;
-		remain -= r;
-	}
-	return (b - (unsigned char *)buf);
+	return len;
 }
diff --git a/extras/tables/table-ldap/ber.h b/extras/tables/table-ldap/ber.h
index d656508..0bfdb07 100644
--- a/extras/tables/table-ldap/ber.h
+++ b/extras/tables/table-ldap/ber.h
@@ -1,5 +1,7 @@
+/*	$OpenBSD: ber.h,v 1.5 2021/10/31 16:42:08 tb Exp $ */
+
 /*
- * Copyright (c) 2007 Reyk Floeter <r...@vantronix.net>
+ * Copyright (c) 2007, 2012 Reyk Floeter <r...@openbsd.org>
  * Copyright (c) 2006, 2007 Claudio Jeker <clau...@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -15,13 +17,24 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#ifndef _BER_H
+#define _BER_H
+
+struct ber_octetstring {
+	size_t			 ostr_len;
+	const void		*ostr_val;
+};
+
 struct ber_element {
 	struct ber_element	*be_next;
-	unsigned long		 be_type;
-	unsigned long		 be_encoding;
+	unsigned int		 be_type;
+	unsigned int		 be_encoding;
 	size_t			 be_len;
+	off_t			 be_offs;
 	int			 be_free;
-	uint8_t			 be_class;
+	u_int8_t		 be_class;
+	void			(*be_cb)(void *, size_t);
+	void			*be_cbarg;
 	union {
 		struct ber_element	*bv_sub;
 		void			*bv_val;
@@ -33,19 +46,19 @@ struct ber_element {
 };
 
 struct ber {
-	int		 fd;
-	unsigned char	*br_wbuf;
-	unsigned char	*br_wptr;
-	unsigned char	*br_wend;
-	unsigned char	*br_rbuf;
-	unsigned char	*br_rptr;
-	unsigned char	*br_rend;
+	off_t	 br_offs;
+	u_char	*br_wbuf;
+	u_char	*br_wptr;
+	u_char	*br_wend;
+	u_char	*br_rbuf;
+	u_char	*br_rptr;
+	u_char	*br_rend;
 
-	unsigned long	(*br_application)(struct ber_element *);
+	unsigned int	(*br_application)(struct ber_element *);
 };
 
 /* well-known ber_element types */
-#define BER_TYPE_DEFAULT	((unsigned long)-1)
+#define BER_TYPE_DEFAULT	((unsigned int)-1)
 #define BER_TYPE_EOC		0
 #define BER_TYPE_BOOLEAN	1
 #define BER_TYPE_INTEGER	2
@@ -67,60 +80,74 @@ struct ber {
 #define BER_CLASS_MASK		0x3
 
 /* common definitions */
-#define BER_MIN_OID_LEN		2	/* OBJECT */
-#define BER_MAX_OID_LEN		32	/* OBJECT */
+#define BER_MIN_OID_LEN		2		/* X.690 section 8.19.5 */
+#define BER_MAX_OID_LEN		128		/* RFC 2578 section 7.1.3 */
+#define BER_MAX_SEQ_ELEMENTS	USHRT_MAX	/* 65535 */
 
 struct ber_oid {
-	uint32_t	bo_id[BER_MAX_OID_LEN + 1];
+	u_int32_t	bo_id[BER_MAX_OID_LEN + 1];
 	size_t		bo_n;
 };
 
 __BEGIN_DECLS
-struct ber_element	*ber_get_element(unsigned long);
-void			 ber_set_header(struct ber_element *, int,
-			    unsigned long);
-void			 ber_link_elements(struct ber_element *,
+struct ber_element	*ober_get_element(unsigned int);
+void			 ober_set_header(struct ber_element *, int,
+			    unsigned int);
+void			 ober_link_elements(struct ber_element *,
 			    struct ber_element *);
-struct ber_element	*ber_unlink_elements(struct ber_element *);
-void			 ber_replace_elements(struct ber_element *,
+struct ber_element	*ober_unlink_elements(struct ber_element *);
+void			 ober_replace_elements(struct ber_element *,
 			    struct ber_element *);
-struct ber_element	*ber_add_sequence(struct ber_element *);
-struct ber_element	*ber_add_set(struct ber_element *);
-struct ber_element	*ber_add_integer(struct ber_element *, long long);
-int			 ber_get_integer(struct ber_element *, long long *);
-struct ber_element	*ber_add_enumerated(struct ber_element *, long long);
-int			 ber_get_enumerated(struct ber_element *, long long *);
-struct ber_element	*ber_add_boolean(struct ber_element *, int);
-int			 ber_get_boolean(struct ber_element *, int *);
-struct ber_element	*ber_add_string(struct ber_element *, const char *);
-struct ber_element	*ber_add_nstring(struct ber_element *, const char *,
+struct ber_element	*ober_add_sequence(struct ber_element *);
+struct ber_element	*ober_add_set(struct ber_element *);
+struct ber_element	*ober_add_integer(struct ber_element *, long long);
+int			 ober_get_integer(struct ber_element *, long long *);
+struct ber_element	*ober_add_enumerated(struct ber_element *, long long);
+int			 ober_get_enumerated(struct ber_element *, long long *);
+struct ber_element	*ober_add_boolean(struct ber_element *, int);
+int			 ober_get_boolean(struct ber_element *, int *);
+struct ber_element	*ober_add_string(struct ber_element *, const char *);
+struct ber_element	*ober_add_nstring(struct ber_element *, const char *,
 			    size_t);
-int			 ber_get_string(struct ber_element *, char **);
-int			 ber_get_nstring(struct ber_element *, void **,
+struct ber_element	*ober_add_ostring(struct ber_element *,
+			    struct ber_octetstring *);
+int			 ober_get_string(struct ber_element *, char **);
+int			 ober_get_nstring(struct ber_element *, void **,
 			    size_t *);
-struct ber_element	*ber_add_bitstring(struct ber_element *, const void *,
+int			 ober_get_ostring(struct ber_element *,
+			    struct ber_octetstring *);
+struct ber_element	*ober_add_bitstring(struct ber_element *, const void *,
 			    size_t);
-int			 ber_get_bitstring(struct ber_element *, void **,
+int			 ober_get_bitstring(struct ber_element *, void **,
 			    size_t *);
-struct ber_element	*ber_add_null(struct ber_element *);
-int			 ber_get_null(struct ber_element *);
-struct ber_element	*ber_add_eoc(struct ber_element *);
-int			 ber_get_eoc(struct ber_element *);
-struct ber_element	*ber_add_oid(struct ber_element *, struct ber_oid *);
-struct ber_element	*ber_add_noid(struct ber_element *, struct ber_oid *, int);
-struct ber_element	*ber_add_oidstring(struct ber_element *, const char *);
-int			 ber_get_oid(struct ber_element *, struct ber_oid *);
-size_t			 ber_oid2ber(struct ber_oid *, uint8_t *, size_t);
-int			 ber_string2oid(const char *, struct ber_oid *);
-struct ber_element	*ber_printf_elements(struct ber_element *, char *, ...);
-int			 ber_scanf_elements(struct ber_element *, char *, ...);
-ssize_t			 ber_get_writebuf(struct ber *, void **);
-int			 ber_write_elements(struct ber *, struct ber_element *);
-void			 ber_set_readbuf(struct ber *, void *, size_t);
-struct ber_element	*ber_read_elements(struct ber *, struct ber_element *);
-void			 ber_free_elements(struct ber_element *);
-size_t			 ber_calc_len(struct ber_element *);
-void			 ber_set_application(struct ber *,
-			    unsigned long (*)(struct ber_element *));
-void			 ber_free(struct ber *);
+struct ber_element	*ober_add_null(struct ber_element *);
+int			 ober_get_null(struct ber_element *);
+struct ber_element	*ober_add_eoc(struct ber_element *);
+int			 ober_get_eoc(struct ber_element *);
+struct ber_element	*ober_add_oid(struct ber_element *, struct ber_oid *);
+struct ber_element	*ober_add_noid(struct ber_element *, struct ber_oid *, int);
+struct ber_element	*ober_add_oidstring(struct ber_element *, const char *);
+int			 ober_get_oid(struct ber_element *, struct ber_oid *);
+size_t			 ober_oid2ber(struct ber_oid *, u_int8_t *, size_t);
+int			 ober_string2oid(const char *, struct ber_oid *);
+struct ber_element	*ober_printf_elements(struct ber_element *, char *, ...);
+int			 ober_scanf_elements(struct ber_element *, char *, ...);
+ssize_t			 ober_get_writebuf(struct ber *, void **);
+ssize_t			 ober_write_elements(struct ber *, struct ber_element *);
+void			 ober_set_readbuf(struct ber *, void *, size_t);
+struct ber_element	*ober_read_elements(struct ber *, struct ber_element *);
+off_t			 ober_getpos(struct ber_element *);
+struct ber_element	*ober_dup(struct ber_element *);
+void			 ober_free_element(struct ber_element *);
+void			 ober_free_elements(struct ber_element *);
+size_t			 ober_calc_len(struct ber_element *);
+void			 ober_set_application(struct ber *,
+			    unsigned int (*)(struct ber_element *));
+void			 ober_set_writecallback(struct ber_element *,
+			    void (*)(void *, size_t), void *);
+void			 ober_free(struct ber *);
+int			 ober_oid_cmp(struct ber_oid *, struct ber_oid *);
+
 __END_DECLS
+
+#endif /* _BER_H */
diff --git a/extras/tables/table-ldap/table_ldap.c b/extras/tables/table-ldap/table_ldap.c
index 64f99a8..76d2c90 100644
--- a/extras/tables/table-ldap/table_ldap.c
+++ b/extras/tables/table-ldap/table_ldap.c
@@ -59,7 +59,7 @@ enum {
 struct query {
 	char	*filter;
 	char	*attrs[MAX_ATTRS];
-	int	 attrn;
+	size_t	 attrn;
 };
 
 static int ldap_run_query(int type, const char *, char *, size_t);
@@ -347,7 +347,7 @@ table_ldap_lookup(int service, struct dict *params, const char *key, char *dst,
 }
 
 static int
-ldap_query(const char *filter, const char *key, char **attributes, char ***outp, size_t n)
+ldap_query(const char *filter, const char *key, char **attributes, struct aldap_stringset **outp, size_t n)
 {
 	struct aldap_message		*m = NULL;
 	struct aldap_page_control	*pg = NULL;
@@ -412,12 +412,36 @@ end:
 	return ret;
 }
 
+static size_t
+ldap_strlcat(char *dst, const struct ber_octetstring str, size_t sz)
+{
+	size_t ret;
+	char *s = calloc(str.ostr_len + 1, sizeof(*s));
+	if (!s) {
+		log_warn("can not allocate memory");
+		return sz;
+	}
+
+	if (strlcpy(s, str.ostr_val, str.ostr_len + 1) >= str.ostr_len + 1) {
+		ret = sz;
+		goto out;
+	}
+	ret = strlcat(dst, s, sz);
+
+out:
+	free(s);
+	return ret;
+}
+
 static int
 ldap_run_query(int type, const char *key, char *dst, size_t sz)
 {
-	struct query	 *q;
-	char		**res[4], filter[MAX_LDAP_FILTERLEN];
-	int		  ret, i;
+	struct query	 	*q;
+	struct aldap_stringset	*res[MAX_ATTRS];
+	char			 filter[MAX_LDAP_FILTERLEN];
+	int		  	 ret;
+	size_t			 i;
+	char			*r, *user, *pwhash, *uid, *gid, *home;
 
 	switch (type) {
 	case K_ALIAS:		q = &queries[LDAP_ALIAS];	break;
@@ -454,12 +478,12 @@ ldap_run_query(int type, const char *key, char *dst, size_t sz)
 
 	case K_ALIAS:
 		memset(dst, 0, sz);
-		for (i = 0; res[0][i]; i++) {
+		for (i = 0; i < res[0]->len; i++) {
 			if (i && strlcat(dst, ", ", sz) >= sz) {
 				ret = -1;
 				break;
 			}
-			if (strlcat(dst, res[0][i], sz) >= sz) {
+			if (ldap_strlcat(dst, res[0]->str[i], sz) >= sz) {
 				ret = -1;
 				break;
 			}
@@ -468,17 +492,28 @@ ldap_run_query(int type, const char *key, char *dst, size_t sz)
 	case K_DOMAIN:
 	case K_MAILADDR:
 	case K_MAILADDRMAP:
-		if (strlcpy(dst, res[0][0], sz) >= sz)
+		r = strndup(res[0]->str[0].ostr_val, res[0]->str[0].ostr_len);
+		if (!r || strlcpy(dst, r, sz) >= sz)
 			ret = -1;
+		free(r);
 		break;
 	case K_CREDENTIALS:
-		if (snprintf(dst, sz, "%s:%s", res[0][0], res[1][0]) >= (int)sz)
+		user = strndup(res[0]->str[0].ostr_val, res[0]->str[0].ostr_len);
+		pwhash = strndup(res[1]->str[0].ostr_val, res[1]->str[0].ostr_len);
+		if (!user || !pwhash || snprintf(dst, sz, "%s:%s", user, pwhash) >= (int)sz)
 			ret = -1;
+		free(user);
+		free(pwhash);
 		break;
 	case K_USERINFO:
-		if (snprintf(dst, sz, "%s:%s:%s", res[0][0], res[1][0],
-		    res[2][0]) >= (int)sz)
+		uid = strndup(res[0]->str[0].ostr_val, res[0]->str[0].ostr_len);
+		gid = strndup(res[1]->str[0].ostr_val, res[1]->str[0].ostr_len);
+		home = strndup(res[2]->str[0].ostr_val, res[2]->str[0].ostr_len);
+		if (!uid || !gid || !home || snprintf(dst, sz, "%s:%s:%s", uid, gid, home) >= (int)sz)
 			ret = -1;
+		free(uid);
+		free(gid);
+		free(home);
 		break;
 	default:
 		log_warnx("warn: unsupported lookup kind");
-- 
2.39.2

From f133d47c5b989b80d8e8890a84ae21447b6838a7 Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Sun, 18 Feb 2024 17:42:09 +0100
Subject: [PATCH 2/4] table-ldap request only required attributes

---
 extras/tables/table-ldap/table_ldap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/extras/tables/table-ldap/table_ldap.c b/extras/tables/table-ldap/table_ldap.c
index 76d2c90..8aa8b15 100644
--- a/extras/tables/table-ldap/table_ldap.c
+++ b/extras/tables/table-ldap/table_ldap.c
@@ -366,7 +366,7 @@ ldap_query(const char *filter, const char *key, char **attributes, struct aldap_
 	found = -1;
 	do {
 		if ((ret = aldap_search(aldap, basedn__, LDAP_SCOPE_SUBTREE,
-		    filter__, key__, NULL, 0, 0, 0, pg)) == -1) {
+		    filter__, key__, attributes, 0, 0, 0, pg)) == -1) {
 			log_debug("ret=%d", ret);
 			return -1;
 		}
-- 
2.39.2

From 8498921f8e6c106a04c49586f8045867b4902b4f Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Sun, 18 Feb 2024 18:55:04 +0100
Subject: [PATCH 3/4] table-ldap handle more then one result

---
 extras/tables/table-ldap/table_ldap.c | 164 +++++++++++++++-----------
 1 file changed, 92 insertions(+), 72 deletions(-)

diff --git a/extras/tables/table-ldap/table_ldap.c b/extras/tables/table-ldap/table_ldap.c
index 8aa8b15..77af984 100644
--- a/extras/tables/table-ldap/table_ldap.c
+++ b/extras/tables/table-ldap/table_ldap.c
@@ -62,6 +62,10 @@ struct query {
 	size_t	 attrn;
 };
 
+struct query_result {
+	char	**v[MAX_ATTRS];
+};
+
 static int ldap_run_query(int type, const char *, char *, size_t);
 
 static char *config, *url, *username, *password, *basedn;
@@ -347,12 +351,26 @@ table_ldap_lookup(int service, struct dict *params, const char *key, char *dst,
 }
 
 static int
-ldap_query(const char *filter, const char *key, char **attributes, struct aldap_stringset **outp, size_t n)
+realloc_results(struct query_result **r, size_t *num)
+{
+	struct query_result *new;
+	size_t newsize = MAX(1, (*num)*2);
+	if ((new = reallocarray(*r, newsize, sizeof(**r))) == NULL)
+		return 0;
+	*num = newsize;
+	*r = new;
+	return 1;
+}
+
+static int
+ldap_query(const char *filter, const char *key, char **attributes, size_t attrn, struct query_result **results, size_t *nresults)
 {
 	struct aldap_message		*m = NULL;
 	struct aldap_page_control	*pg = NULL;
-	int				 ret, found;
-	size_t				 i;
+	struct aldap_stringset		*ldap_res;
+	struct query_result		*res = NULL;
+	int				 ret;
+	size_t				 i, j, k, found = 0, nres = 0;
 	char				 basedn__[MAX_LDAP_BASELEN];
 	char				 filter__[MAX_LDAP_FILTERLEN];
 	char				 key__[MAX_LDAP_IDENTIFIER];
@@ -363,12 +381,12 @@ ldap_query(const char *filter, const char *key, char **attributes, struct aldap_
 		return -1;
 	if (strlcpy(key__, key, sizeof key__) >= sizeof key__)
 		return -1;
-	found = -1;
+
 	do {
-		if ((ret = aldap_search(aldap, basedn__, LDAP_SCOPE_SUBTREE,
-		    filter__, key__, attributes, 0, 0, 0, pg)) == -1) {
-			log_debug("ret=%d", ret);
-			return -1;
+		ret = -1;
+		if (aldap_search(aldap, basedn__, LDAP_SCOPE_SUBTREE,
+		    filter__, key__, attributes, 0, 0, 0, pg) == -1) {
+			goto end;
 		}
 		if (pg != NULL) {
 			aldap_freepage(pg);
@@ -377,59 +395,60 @@ ldap_query(const char *filter, const char *key, char **attributes, struct aldap_
 
 		while ((m = aldap_parse(aldap)) != NULL) {
 			if (aldap->msgid != m->msgid)
-				goto error;
+				goto end;
 			if (m->message_type == LDAP_RES_SEARCH_RESULT) {
 				if (m->page != NULL && m->page->cookie_len)
 					pg = m->page;
 				aldap_freemsg(m);
 				m = NULL;
-				if (found == -1)
-					found = 0;
+				ret = 0;
 				break;
 			}
 			if (m->message_type != LDAP_RES_SEARCH_ENTRY)
-				goto error;
+				goto end;
 
-			found = 1;
-			for (i = 0; i < n; ++i)
-				if (aldap_match_attr(m, attributes[i], &outp[i]) != 1)
-					goto error;
+			if (found >= nres) {
+				if (!realloc_results(&res, &nres)) {
+					goto end;
+				}
+			}
+			memset(&res[found], 0, sizeof(res[found]));
+			for (i = 0; i < attrn; ++i) {
+				if (aldap_match_attr(m, attributes[i], &ldap_res) != 1) {
+					goto end;
+				}
+				res[found].v[i] = calloc(ldap_res->len + 1, sizeof(*res[found].v[i]));
+				for (j = 0; j < ldap_res->len; j++) {
+					res[found].v[i][j] = strndup(ldap_res->str[j].ostr_val, ldap_res->str[j].ostr_len);
+				}
+				aldap_free_attr(ldap_res);
+			}
 			aldap_freemsg(m);
 			m = NULL;
+			found++;
 		}
 	} while (pg != NULL);
 
-	ret = found;
-	goto end;
-
-error:
-	ret = -1;
-
 end:
-	if (m)
-		aldap_freemsg(m);
-	log_debug("debug: table_ldap: ldap_query: filter=%s, ret=%d", filter, ret);
-	return ret;
-}
-
-static size_t
-ldap_strlcat(char *dst, const struct ber_octetstring str, size_t sz)
-{
-	size_t ret;
-	char *s = calloc(str.ostr_len + 1, sizeof(*s));
-	if (!s) {
-		log_warn("can not allocate memory");
-		return sz;
-	}
-
-	if (strlcpy(s, str.ostr_val, str.ostr_len + 1) >= str.ostr_len + 1) {
-		ret = sz;
-		goto out;
+	if (ret == -1) {
+		for (i = 0; i < nres; i++) {
+			for (j = 0; j < attrn; j++) {
+				for (k = 0; res[i].v[j][k]; k++) {
+					free(res[i].v[j][k]);
+				}
+				free(res[i].v[j]);
+			}
+		}
+		free(res);
+	} else {
+		ret = found ? 1 : 0;
+		*results = res;
+		*nresults = nres;
 	}
-	ret = strlcat(dst, s, sz);
 
-out:
-	free(s);
+	if (m)
+		aldap_freemsg(m);
+	log_debug("debug: table_ldap: ldap_query: filter=%s, key=%s, ret=%d", filter, key, ret);
 	return ret;
 }
 
@@ -437,10 +456,10 @@ static int
 ldap_run_query(int type, const char *key, char *dst, size_t sz)
 {
 	struct query	 	*q;
-	struct aldap_stringset	*res[MAX_ATTRS];
+	struct query_result	*res = NULL;
 	char			 filter[MAX_LDAP_FILTERLEN];
 	int		  	 ret;
-	size_t			 i;
+	size_t			 i, j, k, nres = 0;
 	char			*r, *user, *pwhash, *uid, *gid, *home;
 
 	switch (type) {
@@ -469,51 +488,46 @@ ldap_run_query(int type, const char *key, char *dst, size_t sz)
 		return -1;
 	}
 
-	memset(res, 0, sizeof(res));
-	ret = ldap_query(filter, key, q->attrs, res, q->attrn);
+	ret = ldap_query(filter, key, q->attrs, q->attrn, &res, &nres);
 	if (ret <= 0 || dst == NULL)
 		goto end;
 
 	switch (type) {
 
 	case K_ALIAS:
+	case K_MAILADDRMAP:
 		memset(dst, 0, sz);
-		for (i = 0; i < res[0]->len; i++) {
-			if (i && strlcat(dst, ", ", sz) >= sz) {
-				ret = -1;
-				break;
-			}
-			if (ldap_strlcat(dst, res[0]->str[i], sz) >= sz) {
-				ret = -1;
-				break;
+		for (i = 0; ret != -1 && i < nres; i++) {
+			for (j = 0; res[i].v[0][j]; j++) {
+				if ((i || j) && strlcat(dst, ", ", sz) >= sz) {
+					ret = -1;
+					break;
+				}
+				if (strlcat(dst, res[i].v[0][j], sz) >= sz) {
+					ret = -1;
+					break;
+				}
 			}
 		}
 		break;
 	case K_DOMAIN:
 	case K_MAILADDR:
-	case K_MAILADDRMAP:
-		r = strndup(res[0]->str[0].ostr_val, res[0]->str[0].ostr_len);
+		r = res[0].v[0][0];
 		if (!r || strlcpy(dst, r, sz) >= sz)
 			ret = -1;
-		free(r);
 		break;
 	case K_CREDENTIALS:
-		user = strndup(res[0]->str[0].ostr_val, res[0]->str[0].ostr_len);
-		pwhash = strndup(res[1]->str[0].ostr_val, res[1]->str[0].ostr_len);
+		user = res[0].v[0][0];
+		pwhash = res[0].v[1][0];
 		if (!user || !pwhash || snprintf(dst, sz, "%s:%s", user, pwhash) >= (int)sz)
 			ret = -1;
-		free(user);
-		free(pwhash);
 		break;
 	case K_USERINFO:
-		uid = strndup(res[0]->str[0].ostr_val, res[0]->str[0].ostr_len);
-		gid = strndup(res[1]->str[0].ostr_val, res[1]->str[0].ostr_len);
-		home = strndup(res[2]->str[0].ostr_val, res[2]->str[0].ostr_len);
+		uid = res[0].v[0][0];
+		gid = res[0].v[1][0];
+		home = res[0].v[2][0];
 		if (!uid || !gid || !home || snprintf(dst, sz, "%s:%s:%s", uid, gid, home) >= (int)sz)
 			ret = -1;
-		free(uid);
-		free(gid);
-		free(home);
 		break;
 	default:
 		log_warnx("warn: unsupported lookup kind");
@@ -524,9 +538,15 @@ ldap_run_query(int type, const char *key, char *dst, size_t sz)
 		log_warnx("warn: could not format result");
 
 end:
-	for (i = 0; i < q->attrn; ++i)
-		if (res[i])
-			aldap_free_attr(res[i]);
+	for (i = 0; i < nres; i++) {
+		for (j = 0; j < q->attrn; ++j) {
+			for (k = 0; res[i].v[j][k]; k++) {
+				free(res[i].v[j][k]);
+			}
+			free(res[i].v[i]);
+		}
+	}
+	free(res);
 
 	return ret;
 }
-- 
2.39.2

From 113b58b82eb936d8afed7d8f706575ceaa58d73c Mon Sep 17 00:00:00 2001
From: Philipp Takacs <phil...@bureaucracy.de>
Date: Sun, 18 Feb 2024 15:11:54 +0100
Subject: [PATCH 4/4] add ldaps support

---
 extras/tables/table-ldap/table_ldap.c | 52 ++++++++++++++++++++++++---
 1 file changed, 47 insertions(+), 5 deletions(-)

diff --git a/extras/tables/table-ldap/table_ldap.c b/extras/tables/table-ldap/table_ldap.c
index 77af984..167fb42 100644
--- a/extras/tables/table-ldap/table_ldap.c
+++ b/extras/tables/table-ldap/table_ldap.c
@@ -68,7 +68,7 @@ struct query_result {
 
 static int ldap_run_query(int type, const char *, char *, size_t);
 
-static char *config, *url, *username, *password, *basedn;
+static char *config, *url, *username, *password, *basedn, *ca_file;
 
 static struct aldap *aldap;
 static struct query queries[LDAP_MAX];
@@ -89,18 +89,29 @@ static struct aldap *
 ldap_connect(const char *addr)
 {
 	struct aldap_url lu;
+	struct aldap *ldap = NULL;
+	struct tls_config *tls_config = NULL;
 	struct addrinfo	 hints, *res0, *res;
 	int		 error, fd = -1;
 
 	if (aldap_parse_url(addr, &lu) != 1) {
 		log_warnx("warn: ldap_parse_url fail");
-		return NULL;
+		goto out;
+	}
+	if (lu.protocol == LDAPI) {
+		log_warnx("ldapi:// is not suported yet");
+		goto out;
+	}
+	if (lu.protocol == LDAPTLS) {
+		log_warnx("ldap+tls:// is not suported yet");
+		goto out;
 	}
 
 	memset(&hints, 0, sizeof(hints));
 	hints.ai_family = PF_UNSPEC;
 	hints.ai_socktype = SOCK_STREAM;
 	hints.ai_flags = AI_NUMERICSERV;
+	log_debug("ldap connect to: %s:%s", lu.host, lu.port);
 	error = getaddrinfo(lu.host, lu.port, &hints, &res0);
 	if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
 		return NULL;
@@ -116,16 +127,45 @@ ldap_connect(const char *addr)
 			continue;
 
 		if (connect(fd, res->ai_addr, res->ai_addrlen) == 0) {
-			aldap_free_url(&lu);
-			return aldap_init(fd);
+			ldap = aldap_init(fd);
+			break;
 		}
 
 		close(fd);
 		fd = -1;
 	}
 
+	if (!ldap) {
+		log_debug("can not connect");
+		goto out;
+	}
+
+	if (lu.protocol == LDAPS || lu.protocol == LDAPTLS) {
+		tls_config = tls_config_new();
+		if (!tls_config) {
+			log_warn("warn: can not get tls_config");
+			aldap_close(ldap);
+			ldap = NULL;
+			goto out;
+		}
+		if (ca_file && tls_config_set_ca_file(tls_config, ca_file) == -1) {
+			log_warnx("warn: can't load ca file: %s", tls_config_error(tls_config));
+			aldap_close(ldap);
+			ldap = NULL;
+			goto out;
+		}
+		if (aldap_tls(ldap, tls_config, lu.host) == -1) {
+			log_warnx("warn: tls connection failed");
+			aldap_close(ldap);
+			ldap = NULL;
+			goto out;
+		}
+	}
+
+out:
+	tls_config_free(tls_config);
 	aldap_free_url(&lu);
-	return NULL;
+	return ldap;
 }
 
 static int
@@ -228,6 +268,8 @@ ldap_config(void)
 			read_value(&password, key, value);
 		else if (!strcmp(key, "basedn"))
 			read_value(&basedn, key, value);
+		else if (!strcmp(key, "ca_file"))
+			read_value(&ca_file, key, value);
 		else if (!strcmp(key, "alias_filter"))
 			read_value(&queries[LDAP_ALIAS].filter, key, value);
 		else if (!strcmp(key, "alias_attributes")) {
-- 
2.39.2

Reply via email to