summaryrefslogtreecommitdiffstats
path: root/usr.sbin/ypldap
diff options
context:
space:
mode:
authorrodrigc <rodrigc@FreeBSD.org>2015-11-16 16:48:43 +0000
committerrodrigc <rodrigc@FreeBSD.org>2015-11-16 16:48:43 +0000
commitd2078e379a508e6ee362fc30ecc041c9b5161d62 (patch)
treebfed3f14f9c5991858e9e33402cfc5fa2e8c3525 /usr.sbin/ypldap
parent3fdce597b1adb53c2c5917724d7807641e81c776 (diff)
downloadFreeBSD-src-d2078e379a508e6ee362fc30ecc041c9b5161d62.zip
FreeBSD-src-d2078e379a508e6ee362fc30ecc041c9b5161d62.tar.gz
Import ypldap from OpenBSD.
ypldap -- Intended to be a drop-in replacement for ypserv, gluing in a LDAP directory and thus providing support for users and groups stored in LDAP for the get{pw,gr}ent family of functions.
Diffstat (limited to 'usr.sbin/ypldap')
-rw-r--r--usr.sbin/ypldap/Makefile21
-rw-r--r--usr.sbin/ypldap/aldap.c1273
-rw-r--r--usr.sbin/ypldap/aldap.h221
-rw-r--r--usr.sbin/ypldap/ber.c1269
-rw-r--r--usr.sbin/ypldap/ber.h129
-rw-r--r--usr.sbin/ypldap/entries.c148
-rw-r--r--usr.sbin/ypldap/ldapclient.c704
-rw-r--r--usr.sbin/ypldap/log.c161
-rw-r--r--usr.sbin/ypldap/parse.y837
-rw-r--r--usr.sbin/ypldap/yp.c652
-rw-r--r--usr.sbin/ypldap/ypldap.882
-rw-r--r--usr.sbin/ypldap/ypldap.c650
-rw-r--r--usr.sbin/ypldap/ypldap.conf.5167
-rw-r--r--usr.sbin/ypldap/ypldap.h222
-rw-r--r--usr.sbin/ypldap/ypldap_dns.c253
15 files changed, 6789 insertions, 0 deletions
diff --git a/usr.sbin/ypldap/Makefile b/usr.sbin/ypldap/Makefile
new file mode 100644
index 0000000..f89277a
--- /dev/null
+++ b/usr.sbin/ypldap/Makefile
@@ -0,0 +1,21 @@
+# $OpenBSD: Makefile,v 1.8 2015/09/09 15:33:18 deraadt Exp $
+# $FreeBSD$
+
+PROG= ypldap
+SRCS= parse.y ypldap.c log.c \
+ ldapclient.c entries.c yp.c \
+ aldap.c ber.c \
+ ypldap_dns.c
+
+MAN= ypldap.8 ypldap.conf.5
+
+DPADD= ${LIBEVENT} ${LIBUTIL} ${LIBRPCSVC}
+LDADD= -levent -lutil -lrpcsvc
+CFLAGS+= -I${.CURDIR}
+CFLAGS+= -Wall
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ypldap/aldap.c b/usr.sbin/ypldap/aldap.c
new file mode 100644
index 0000000..03e5224
--- /dev/null
+++ b/usr.sbin/ypldap/aldap.c
@@ -0,0 +1,1273 @@
+/* $Id: aldap.c,v 1.30 2012/04/30 21:40:03 jmatthew Exp $ */
+/* $OpenBSD: aldap.c,v 1.30 2012/04/30 21:40:03 jmatthew Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2008 Alexander Schrijver <aschrijver@openbsd.org>
+ * Copyright (c) 2006, 2007 Marc Balmer <mbalmer@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "aldap.h"
+
+#if 0
+#define DEBUG
+#endif
+#define VERSION 3
+
+static struct ber_element *ldap_parse_search_filter(struct ber_element *,
+ char *);
+static struct ber_element *ldap_do_parse_search_filter(
+ struct ber_element *, char **);
+char **aldap_get_stringset(struct ber_element *);
+char *utoa(char *);
+char *parseval(char *, size_t);
+int aldap_create_page_control(struct ber_element *,
+ int, struct aldap_page_control *);
+
+#ifdef DEBUG
+void ldap_debug_elements(struct ber_element *);
+#endif
+
+#ifdef DEBUG
+#define DPRINTF(x...) printf(x)
+#define LDAP_DEBUG(x, y) do { fprintf(stderr, "*** " x "\n"); ldap_debug_elements(y); } while (0)
+#else
+#define DPRINTF(x...) do { } while (0)
+#define LDAP_DEBUG(x, y) do { } while (0)
+#endif
+
+int
+aldap_close(struct aldap *al)
+{
+ if (close(al->ber.fd) == -1)
+ return (-1);
+
+ ber_free(&al->ber);
+ free(al);
+
+ return (0);
+}
+
+struct aldap *
+aldap_init(int fd)
+{
+ struct aldap *a;
+
+ if ((a = calloc(1, sizeof(*a))) == NULL)
+ return NULL;
+ a->ber.fd = fd;
+
+ return a;
+}
+
+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)
+ goto fail;
+
+ elm = ber_printf_elements(root, "d{tdsst", ++ldap->msgid, BER_CLASS_APP,
+ (unsigned long)LDAP_REQ_BIND, VERSION, binddn, bindcred,
+ BER_CLASS_CONTEXT, (unsigned long)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)
+ goto fail;
+
+ return (ldap->msgid);
+fail:
+ if (root != NULL)
+ ber_free_elements(root);
+
+ ldap->err = ALDAP_ERR_OPERATION_FAILED;
+ return (-1);
+}
+
+int
+aldap_unbind(struct aldap *ldap)
+{
+ struct ber_element *root = NULL, *elm;
+ int error;
+
+ if ((root = ber_add_sequence(NULL)) == NULL)
+ goto fail;
+ elm = ber_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)
+ goto fail;
+
+ return (ldap->msgid);
+fail:
+ if (root != NULL)
+ ber_free_elements(root);
+
+ ldap->err = ALDAP_ERR_OPERATION_FAILED;
+
+ return (-1);
+}
+
+int
+aldap_search(struct aldap *ldap, char *basedn, enum scope scope, char *filter,
+ char **attrs, int typesonly, int sizelimit, int timelimit,
+ struct aldap_page_control *page)
+{
+ struct ber_element *root = NULL, *ber, *c;
+ int i, error;
+
+ if ((root = ber_add_sequence(NULL)) == NULL)
+ goto fail;
+
+ ber = ber_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP,
+ (unsigned long) 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,
+ timelimit, typesonly);
+ if (ber == NULL) {
+ ldap->err = ALDAP_ERR_OPERATION_FAILED;
+ goto fail;
+ }
+
+ if ((ber = ldap_parse_search_filter(ber, filter)) == NULL) {
+ ldap->err = ALDAP_ERR_PARSER_ERROR;
+ goto fail;
+ }
+
+ if ((ber = ber_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)
+ goto fail;
+ }
+
+ aldap_create_page_control(c, 100, page);
+
+ LDAP_DEBUG("aldap_search", root);
+
+ error = ber_write_elements(&ldap->ber, root);
+ ber_free_elements(root);
+ root = NULL;
+ if (error == -1) {
+ ldap->err = ALDAP_ERR_OPERATION_FAILED;
+ goto fail;
+ }
+
+ return (ldap->msgid);
+
+fail:
+ if (root != NULL)
+ ber_free_elements(root);
+
+ return (-1);
+}
+
+int
+aldap_create_page_control(struct ber_element *elm, int size,
+ struct aldap_page_control *page)
+{
+ int len;
+ struct ber c;
+ struct ber_element *ber = NULL;
+
+ c.br_wbuf = NULL;
+ c.fd = -1;
+
+ ber = ber_add_sequence(NULL);
+
+ if (page == NULL) {
+ if (ber_printf_elements(ber, "ds", 50, "") == NULL)
+ goto fail;
+ } else {
+ if (ber_printf_elements(ber, "dx", 50, page->cookie,
+ page->cookie_len) == NULL)
+ goto fail;
+ }
+
+ if ((len = ber_write_elements(&c, ber)) < 1)
+ goto fail;
+ if (ber_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);
+ return len;
+fail:
+ if (ber != NULL)
+ ber_free_elements(ber);
+ ber_free(&c);
+
+ return (-1);
+}
+
+struct aldap_message *
+aldap_parse(struct aldap *ldap)
+{
+ int class;
+ unsigned long type;
+ long long msgid = 0;
+ struct aldap_message *m;
+ struct ber_element *a = NULL, *ep;
+
+ if ((m = calloc(1, sizeof(struct aldap_message))) == NULL)
+ return NULL;
+
+ if ((m->msg = ber_read_elements(&ldap->ber, NULL)) == NULL)
+ goto parsefail;
+
+ LDAP_DEBUG("message", m->msg);
+
+ if (ber_scanf_elements(m->msg, "{ite", &msgid, &class, &type, &a) != 0)
+ goto parsefail;
+ m->msgid = msgid;
+ m->message_type = type;
+ m->protocol_op = a;
+
+ switch (m->message_type) {
+ case LDAP_RES_BIND:
+ case LDAP_RES_MODIFY:
+ case LDAP_RES_ADD:
+ case LDAP_RES_DELETE:
+ 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)
+ goto parsefail;
+ if (m->body.res.rescode == LDAP_REFERRAL)
+ if (ber_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);
+ if (class == 2 && type == 0)
+ m->page = aldap_parse_page_control(ep->be_sub->be_sub,
+ ep->be_sub->be_sub->be_len);
+ }
+ } else
+ m->page = NULL;
+ break;
+ case LDAP_RES_SEARCH_ENTRY:
+ if (ber_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)
+ goto parsefail;
+ break;
+ }
+
+ return m;
+parsefail:
+ 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)
+{
+ char *oid, *s;
+ char *encoded;
+ struct ber b;
+ struct ber_element *elm;
+ 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 ((page = malloc(sizeof(struct aldap_page_control))) == NULL) {
+ if (elm != NULL)
+ ber_free_elements(elm);
+ ber_free(&b);
+ return NULL;
+ }
+
+ ber_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) {
+ if (elm != NULL)
+ ber_free_elements(elm);
+ ber_free(&b);
+ free(page);
+ return NULL;
+ }
+ memcpy(page->cookie, s, page->cookie_len);
+
+ ber_free_elements(elm);
+ ber_free(&b);
+ return page;
+}
+
+void
+aldap_freepage(struct aldap_page_control *page)
+{
+ if (page->cookie)
+ free(page->cookie);
+ free(page);
+}
+
+void
+aldap_freemsg(struct aldap_message *msg)
+{
+ if (msg->msg)
+ ber_free_elements(msg->msg);
+ free(msg);
+}
+
+int
+aldap_get_resultcode(struct aldap_message *msg)
+{
+ return msg->body.res.rescode;
+}
+
+char *
+aldap_get_dn(struct aldap_message *msg)
+{
+ char *dn;
+
+ if (msg->dn == NULL)
+ return NULL;
+
+ if (ber_get_string(msg->dn, &dn) == -1)
+ return NULL;
+
+ return utoa(dn);
+}
+
+char **
+aldap_get_references(struct aldap_message *msg)
+{
+ if (msg->references == NULL)
+ return NULL;
+ return aldap_get_stringset(msg->references);
+}
+
+void
+aldap_free_references(char **values)
+{
+ int i;
+
+ if (values == NULL)
+ return;
+
+ for (i = 0; values[i] != NULL; i++)
+ free(values[i]);
+
+ free(values);
+}
+
+char *
+aldap_get_diagmsg(struct aldap_message *msg)
+{
+ char *s;
+
+ if (msg->body.res.diagmsg == NULL)
+ return NULL;
+
+ if (ber_get_string(msg->body.res.diagmsg, &s) == -1)
+ return NULL;
+
+ return utoa(s);
+}
+
+int
+aldap_count_attrs(struct aldap_message *msg)
+{
+ int i;
+ struct ber_element *a;
+
+ if (msg->body.search.attrs == NULL)
+ return (-1);
+
+ for (i = 0, a = msg->body.search.attrs;
+ a != NULL && ber_get_eoc(a) != 0;
+ i++, a = a->be_next)
+ ;
+
+ return i;
+}
+
+int
+aldap_first_attr(struct aldap_message *msg, char **outkey, char ***outvalues)
+{
+ struct ber_element *b, *c;
+ char *key;
+ char **ret;
+
+ if (msg->body.search.attrs == NULL)
+ goto fail;
+
+ if (ber_scanf_elements(msg->body.search.attrs, "{s(e)}e",
+ &key, &b, &c) != 0)
+ goto fail;
+
+ msg->body.search.iter = msg->body.search.attrs->be_next;
+
+ if ((ret = aldap_get_stringset(b)) == NULL)
+ goto fail;
+
+ (*outvalues) = ret;
+ (*outkey) = utoa(key);
+
+ return (1);
+fail:
+ (*outkey) = NULL;
+ (*outvalues) = NULL;
+ return (-1);
+}
+
+int
+aldap_next_attr(struct aldap_message *msg, char **outkey, char ***outvalues)
+{
+ struct ber_element *a, *b;
+ char *key;
+ char **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)
+ goto notfound;
+
+ if (ber_scanf_elements(msg->body.search.iter, "{s(e)}e", &key, &a, &b)
+ != 0)
+ goto fail;
+
+ msg->body.search.iter = msg->body.search.iter->be_next;
+
+ if ((ret = aldap_get_stringset(a)) == NULL)
+ goto fail;
+
+ (*outvalues) = ret;
+ (*outkey) = utoa(key);
+
+ return (1);
+fail:
+notfound:
+ (*outkey) = NULL;
+ (*outvalues) = NULL;
+ return (-1);
+}
+
+int
+aldap_match_attr(struct aldap_message *msg, char *inkey, char ***outvalues)
+{
+ struct ber_element *a, *b;
+ char *descr = NULL;
+ char **ret;
+
+ if (msg->body.search.attrs == NULL)
+ goto fail;
+
+ LDAP_DEBUG("attr", msg->body.search.attrs);
+
+ for (a = msg->body.search.attrs;;) {
+ if (a == NULL)
+ goto notfound;
+ if (ber_get_eoc(a) == 0)
+ goto notfound;
+ if (ber_scanf_elements(a, "{s(e", &descr, &b) != 0)
+ goto fail;
+ if (strcasecmp(descr, inkey) == 0)
+ goto attrfound;
+ a = a->be_next;
+ }
+
+attrfound:
+ if ((ret = aldap_get_stringset(b)) == NULL)
+ goto fail;
+
+ (*outvalues) = ret;
+
+ return (1);
+fail:
+notfound:
+ (*outvalues) = NULL;
+ return (-1);
+}
+
+int
+aldap_free_attr(char **values)
+{
+ int i;
+
+ if (values == NULL)
+ return -1;
+
+ for (i = 0; values[i] != NULL; i++)
+ free(values[i]);
+
+ free(values);
+
+ return (1);
+}
+
+#if 0
+void
+aldap_free_url(struct aldap_url *lu)
+{
+ free(lu->buffer);
+ free(lu->filter);
+}
+
+int
+aldap_parse_url(char *url, struct aldap_url *lu)
+{
+ char *p, *forward, *forward2;
+ const char *errstr = NULL;
+ int i;
+
+ if ((lu->buffer = p = strdup(url)) == NULL)
+ return (-1);
+
+ /* protocol */
+ if (strncasecmp(LDAP_URL, p, strlen(LDAP_URL)) != 0)
+ goto fail;
+ lu->protocol = LDAP;
+ p += strlen(LDAP_URL);
+
+ /* host and optional port */
+ if ((forward = strchr(p, '/')) != NULL)
+ *forward = '\0';
+ /* find the optional port */
+ if ((forward2 = strchr(p, ':')) != NULL) {
+ *forward2 = '\0';
+ /* if a port is given */
+ if (*(forward2+1) != '\0') {
+#define PORT_MAX UINT16_MAX
+ lu->port = strtonum(++forward2, 0, PORT_MAX, &errstr);
+ if (errstr)
+ goto fail;
+ }
+ }
+ /* fail if no host is given */
+ if (strlen(p) == 0)
+ goto fail;
+ lu->host = p;
+ if (forward == NULL)
+ goto done;
+ /* p is assigned either a pointer to a character or to '\0' */
+ p = ++forward;
+ if (strlen(p) == 0)
+ goto done;
+
+ /* dn */
+ if ((forward = strchr(p, '?')) != NULL)
+ *forward = '\0';
+ lu->dn = p;
+ if (forward == NULL)
+ goto done;
+ /* p is assigned either a pointer to a character or to '\0' */
+ p = ++forward;
+ if (strlen(p) == 0)
+ goto done;
+
+ /* attributes */
+ if ((forward = strchr(p, '?')) != NULL)
+ *forward = '\0';
+ for (i = 0; i < MAXATTR; i++) {
+ if ((forward2 = strchr(p, ',')) == NULL) {
+ if (strlen(p) == 0)
+ break;
+ lu->attributes[i] = p;
+ break;
+ }
+ *forward2 = '\0';
+ lu->attributes[i] = p;
+ p = ++forward2;
+ }
+ if (forward == NULL)
+ goto done;
+ /* p is assigned either a pointer to a character or to '\0' */
+ p = ++forward;
+ if (strlen(p) == 0)
+ goto done;
+
+ /* scope */
+ if ((forward = strchr(p, '?')) != NULL)
+ *forward = '\0';
+ if (strcmp(p, "base") == 0)
+ lu->scope = LDAP_SCOPE_BASE;
+ else if (strcmp(p, "one") == 0)
+ lu->scope = LDAP_SCOPE_ONELEVEL;
+ else if (strcmp(p, "sub") == 0)
+ lu->scope = LDAP_SCOPE_SUBTREE;
+ else
+ goto fail;
+ if (forward == NULL)
+ goto done;
+ p = ++forward;
+ if (strlen(p) == 0)
+ goto done;
+
+ /* filter */
+ if (p)
+ lu->filter = p;
+done:
+ free(url);
+ return (1);
+fail:
+ free(lu->buffer);
+ lu->buffer = NULL;
+ return (-1);
+}
+
+int
+aldap_search_url(struct aldap *ldap, char *url, int typesonly, int sizelimit,
+ int timelimit)
+{
+ struct aldap_url *lu;
+
+ if ((lu = calloc(1, sizeof(*lu))) == NULL)
+ return (-1);
+
+ if (aldap_parse_url(url, lu))
+ goto fail;
+
+ if (aldap_search(ldap, lu->dn, lu->scope, lu->filter, lu->attributes,
+ typesonly, sizelimit, timelimit) == -1)
+ goto fail;
+
+ aldap_free_url(lu);
+ return (ldap->msgid);
+fail:
+ aldap_free_url(lu);
+ return (-1);
+}
+#endif /* 0 */
+
+/*
+ * internal functions
+ */
+
+char **
+aldap_get_stringset(struct ber_element *elm)
+{
+ struct ber_element *a;
+ int i;
+ char **ret;
+ char *s;
+
+ 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 (i == 1)
+ return NULL;
+
+ if ((ret = calloc(i + 1, sizeof(char *))) == NULL)
+ 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;
+
+ return ret;
+}
+
+/*
+ * Base case for ldap_do_parse_search_filter
+ *
+ * returns:
+ * struct ber_element *, ber_element tree
+ * NULL, parse failed
+ */
+static struct ber_element *
+ldap_parse_search_filter(struct ber_element *ber, char *filter)
+{
+ struct ber_element *elm;
+ char *cp;
+
+ cp = filter;
+
+ if (cp == NULL || *cp == '\0') {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if ((elm = ldap_do_parse_search_filter(ber, &cp)) == NULL)
+ return (NULL);
+
+ if (*cp != '\0') {
+ ber_free_elements(elm);
+ ber_link_elements(ber, NULL);
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ return (elm);
+}
+
+/*
+ * Translate RFC4515 search filter string into ber_element tree
+ *
+ * returns:
+ * struct ber_element *, ber_element tree
+ * NULL, parse failed
+ *
+ * notes:
+ * when cp is passed to a recursive invocation, it is updated
+ * to point one character beyond the filter that was passed
+ * i.e., cp jumps to "(filter)" upon return
+ * ^
+ * goto's used to discriminate error-handling based on error type
+ * doesn't handle extended filters (yet)
+ *
+ */
+static struct ber_element *
+ldap_do_parse_search_filter(struct ber_element *prev, char **cpp)
+{
+ struct ber_element *elm, *root = NULL;
+ char *attr_desc, *attr_val, *parsed_val, *cp;
+ size_t len;
+ unsigned long type;
+
+ root = NULL;
+
+ /* cpp should pass in pointer to opening parenthesis of "(filter)" */
+ cp = *cpp;
+ if (*cp != '(')
+ goto syntaxfail;
+
+ switch (*++cp) {
+ case '&': /* AND */
+ case '|': /* OR */
+ if (*cp == '&')
+ type = LDAP_FILT_AND;
+ else
+ type = LDAP_FILT_OR;
+
+ if ((elm = ber_add_set(prev)) == NULL)
+ goto callfail;
+ root = elm;
+ ber_set_header(elm, BER_CLASS_CONTEXT, type);
+
+ if (*++cp != '(') /* opening `(` of filter */
+ goto syntaxfail;
+
+ while (*cp == '(') {
+ if ((elm =
+ ldap_do_parse_search_filter(elm, &cp)) == NULL)
+ goto bad;
+ }
+
+ if (*cp != ')') /* trailing `)` of filter */
+ goto syntaxfail;
+ break;
+
+ case '!': /* NOT */
+ if ((root = ber_add_sequence(prev)) == NULL)
+ goto callfail;
+ ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_NOT);
+
+ cp++; /* now points to sub-filter */
+ if ((elm = ldap_do_parse_search_filter(root, &cp)) == NULL)
+ goto bad;
+
+ if (*cp != ')') /* trailing `)` of filter */
+ goto syntaxfail;
+ break;
+
+ default: /* SIMPLE || PRESENCE */
+ attr_desc = cp;
+
+ len = strcspn(cp, "()<>~=");
+ cp += len;
+ switch (*cp) {
+ case '~':
+ type = LDAP_FILT_APPR;
+ cp++;
+ break;
+ case '<':
+ type = LDAP_FILT_LE;
+ cp++;
+ break;
+ case '>':
+ type = LDAP_FILT_GE;
+ cp++;
+ break;
+ case '=':
+ type = LDAP_FILT_EQ; /* assume EQ until disproven */
+ break;
+ case '(':
+ case ')':
+ default:
+ goto syntaxfail;
+ }
+ attr_val = ++cp;
+
+ /* presence filter */
+ if (strncmp(attr_val, "*)", 2) == 0) {
+ cp++; /* point to trailing `)` */
+ if ((root =
+ ber_add_nstring(prev, attr_desc, len)) == NULL)
+ goto bad;
+
+ ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_PRES);
+ break;
+ }
+
+ if ((root = ber_add_sequence(prev)) == NULL)
+ goto callfail;
+ ber_set_header(root, BER_CLASS_CONTEXT, type);
+
+ if ((elm = ber_add_nstring(root, attr_desc, len)) == NULL)
+ goto callfail;
+
+ len = strcspn(attr_val, "*)");
+ if (len == 0 && *cp != '*')
+ goto syntaxfail;
+ cp += len;
+ if (*cp == '\0')
+ goto syntaxfail;
+
+ if (*cp == '*') { /* substring filter */
+ int initial;
+
+ cp = attr_val;
+
+ ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_SUBS);
+
+ if ((elm = ber_add_sequence(elm)) == NULL)
+ goto callfail;
+
+ for (initial = 1;; cp++, initial = 0) {
+ attr_val = cp;
+
+ len = strcspn(attr_val, "*)");
+ if (len == 0) {
+ if (*cp == ')')
+ break;
+ else
+ continue;
+ }
+ cp += len;
+ if (*cp == '\0')
+ goto syntaxfail;
+
+ if (initial)
+ type = LDAP_FILT_SUBS_INIT;
+ else if (*cp == ')')
+ type = LDAP_FILT_SUBS_FIN;
+ else
+ type = LDAP_FILT_SUBS_ANY;
+
+ if ((parsed_val = parseval(attr_val, len)) ==
+ NULL)
+ goto callfail;
+ elm = ber_add_nstring(elm, parsed_val,
+ strlen(parsed_val));
+ free(parsed_val);
+ if (elm == NULL)
+ goto callfail;
+ ber_set_header(elm, BER_CLASS_CONTEXT, type);
+ if (type == LDAP_FILT_SUBS_FIN)
+ break;
+ }
+ break;
+ }
+
+ if ((parsed_val = parseval(attr_val, len)) == NULL)
+ goto callfail;
+ elm = ber_add_nstring(elm, parsed_val, strlen(parsed_val));
+ free(parsed_val);
+ if (elm == NULL)
+ goto callfail;
+ break;
+ }
+
+ cp++; /* now points one char beyond the trailing `)` */
+
+ *cpp = cp;
+ return (root);
+
+syntaxfail: /* XXX -- error reporting */
+callfail:
+bad:
+ if (root != NULL)
+ ber_free_elements(root);
+ ber_link_elements(prev, NULL);
+ return (NULL);
+}
+
+#ifdef DEBUG
+/*
+ * Display a list of ber elements.
+ *
+ */
+void
+ldap_debug_elements(struct ber_element *root)
+{
+ static int indent = 0;
+ long long v;
+ int d;
+ char *buf;
+ size_t len;
+ u_int i;
+ int constructed;
+ struct ber_oid o;
+
+ /* calculate lengths */
+ ber_calc_len(root);
+
+ switch (root->be_encoding) {
+ case BER_TYPE_SEQUENCE:
+ case BER_TYPE_SET:
+ constructed = root->be_encoding;
+ break;
+ default:
+ constructed = 0;
+ break;
+ }
+
+ fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
+ switch (root->be_class) {
+ case BER_CLASS_UNIVERSAL:
+ fprintf(stderr, "class: universal(%u) type: ", root->be_class);
+ switch (root->be_type) {
+ case BER_TYPE_EOC:
+ fprintf(stderr, "end-of-content");
+ break;
+ case BER_TYPE_BOOLEAN:
+ fprintf(stderr, "boolean");
+ break;
+ case BER_TYPE_INTEGER:
+ fprintf(stderr, "integer");
+ break;
+ case BER_TYPE_BITSTRING:
+ fprintf(stderr, "bit-string");
+ break;
+ case BER_TYPE_OCTETSTRING:
+ fprintf(stderr, "octet-string");
+ break;
+ case BER_TYPE_NULL:
+ fprintf(stderr, "null");
+ break;
+ case BER_TYPE_OBJECT:
+ fprintf(stderr, "object");
+ break;
+ case BER_TYPE_ENUMERATED:
+ fprintf(stderr, "enumerated");
+ break;
+ case BER_TYPE_SEQUENCE:
+ fprintf(stderr, "sequence");
+ break;
+ case BER_TYPE_SET:
+ fprintf(stderr, "set");
+ break;
+ }
+ break;
+ case BER_CLASS_APPLICATION:
+ fprintf(stderr, "class: application(%u) type: ",
+ root->be_class);
+ switch (root->be_type) {
+ case LDAP_REQ_BIND:
+ fprintf(stderr, "bind");
+ break;
+ case LDAP_RES_BIND:
+ fprintf(stderr, "bind");
+ break;
+ case LDAP_REQ_UNBIND_30:
+ break;
+ case LDAP_REQ_SEARCH:
+ fprintf(stderr, "search");
+ break;
+ case LDAP_RES_SEARCH_ENTRY:
+ fprintf(stderr, "search_entry");
+ break;
+ case LDAP_RES_SEARCH_RESULT:
+ fprintf(stderr, "search_result");
+ break;
+ case LDAP_REQ_MODIFY:
+ fprintf(stderr, "modify");
+ break;
+ case LDAP_RES_MODIFY:
+ fprintf(stderr, "modify");
+ break;
+ case LDAP_REQ_ADD:
+ fprintf(stderr, "add");
+ break;
+ case LDAP_RES_ADD:
+ fprintf(stderr, "add");
+ break;
+ case LDAP_REQ_DELETE_30:
+ fprintf(stderr, "delete");
+ break;
+ case LDAP_RES_DELETE:
+ fprintf(stderr, "delete");
+ break;
+ case LDAP_REQ_MODRDN:
+ fprintf(stderr, "modrdn");
+ break;
+ case LDAP_RES_MODRDN:
+ fprintf(stderr, "modrdn");
+ break;
+ case LDAP_REQ_COMPARE:
+ fprintf(stderr, "compare");
+ break;
+ case LDAP_RES_COMPARE:
+ fprintf(stderr, "compare");
+ break;
+ case LDAP_REQ_ABANDON_30:
+ fprintf(stderr, "abandon");
+ break;
+ }
+ break;
+ case BER_CLASS_PRIVATE:
+ fprintf(stderr, "class: private(%u) type: ", root->be_class);
+ fprintf(stderr, "encoding (%lu) type: ", root->be_encoding);
+ break;
+ case BER_CLASS_CONTEXT:
+ /* XXX: this is not correct */
+ fprintf(stderr, "class: context(%u) type: ", root->be_class);
+ switch(root->be_type) {
+ case LDAP_AUTH_SIMPLE:
+ fprintf(stderr, "auth simple");
+ break;
+ }
+ break;
+ default:
+ fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
+ break;
+ }
+ fprintf(stderr, "(%lu) encoding %lu ",
+ root->be_type, root->be_encoding);
+
+ if (constructed)
+ root->be_encoding = constructed;
+
+ switch (root->be_encoding) {
+ case BER_TYPE_BOOLEAN:
+ if (ber_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) {
+ fprintf(stderr, "<INVALID>\n");
+ break;
+ }
+ fprintf(stderr, "value %lld\n", v);
+ break;
+ case BER_TYPE_ENUMERATED:
+ if (ber_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) {
+ fprintf(stderr, "<INVALID>\n");
+ break;
+ }
+ fprintf(stderr, "hexdump ");
+ for (i = 0; i < len; i++)
+ fprintf(stderr, "%02x", buf[i]);
+ fprintf(stderr, "\n");
+ break;
+ case BER_TYPE_OBJECT:
+ if (ber_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) {
+ fprintf(stderr, "<INVALID>\n");
+ break;
+ }
+ fprintf(stderr, "string \"%.*s\"\n", len, buf);
+ break;
+ case BER_TYPE_NULL: /* no payload */
+ case BER_TYPE_EOC:
+ case BER_TYPE_SEQUENCE:
+ case BER_TYPE_SET:
+ default:
+ fprintf(stderr, "\n");
+ break;
+ }
+
+ if (constructed && root->be_sub) {
+ indent += 2;
+ ldap_debug_elements(root->be_sub);
+ indent -= 2;
+ }
+ if (root->be_next)
+ ldap_debug_elements(root->be_next);
+}
+#endif
+
+/*
+ * Convert UTF-8 to ASCII.
+ * notes:
+ * non-ASCII characters are displayed as '?'
+ * the argument u should be a NULL terminated sequence of UTF-8 bytes.
+ */
+char *
+utoa(char *u)
+{
+ int len, i, j;
+ 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++;
+ }
+
+ 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;
+ }
+ }
+
+ return str;
+}
+
+/*
+ * Parse a LDAP value
+ * notes:
+ * the argument u should be a NULL terminated sequence of ASCII bytes.
+ */
+char *
+parseval(char *p, size_t len)
+{
+ char hex[3];
+ char *cp = p, *buffer, *newbuffer;
+ size_t size, newsize, i, j;
+
+ size = 50;
+ 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] == '\\') {
+ strlcpy(hex, cp + j + 1, sizeof(hex));
+ buffer[i] = (char)strtoumax(hex, NULL, 16);
+ j += 3;
+ } else {
+ buffer[i] = cp[j];
+ j++;
+ }
+ }
+
+ return buffer;
+}
+
+int
+aldap_get_errno(struct aldap *a, const char **estr)
+{
+ switch (a->err) {
+ case ALDAP_ERR_SUCCESS:
+ *estr = "success";
+ break;
+ case ALDAP_ERR_PARSER_ERROR:
+ *estr = "parser failed";
+ break;
+ case ALDAP_ERR_INVALID_FILTER:
+ *estr = "invalid filter";
+ break;
+ case ALDAP_ERR_OPERATION_FAILED:
+ *estr = "operation failed";
+ break;
+ default:
+ *estr = "unknown";
+ break;
+ }
+ return (a->err);
+}
diff --git a/usr.sbin/ypldap/aldap.h b/usr.sbin/ypldap/aldap.h
new file mode 100644
index 0000000..cdf5316
--- /dev/null
+++ b/usr.sbin/ypldap/aldap.h
@@ -0,0 +1,221 @@
+/* $Id: aldap.h,v 1.9 2012/04/30 21:40:03 jmatthew Exp $ */
+/* $OpenBSD: aldap.h,v 1.9 2012/04/30 21:40:03 jmatthew Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2008 Alexander Schrijver <aschrijver@openbsd.org>
+ * Copyright (c) 2006, 2007 Marc Balmer <mbalmer@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include "ber.h"
+
+#define LDAP_URL "ldap://"
+#define LDAP_PORT 389
+#define LDAP_PAGED_OID "1.2.840.113556.1.4.319"
+
+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
+ u_int8_t err;
+ int msgid;
+ struct ber ber;
+};
+
+struct aldap_page_control {
+ int size;
+ char *cookie;
+ unsigned int cookie_len;
+};
+
+struct aldap_message {
+ int msgid;
+ int message_type;
+
+ struct ber_element *msg;
+
+ struct ber_element *header;
+ struct ber_element *protocol_op;
+
+ struct ber_element *dn;
+
+ union {
+ struct {
+ long long rescode;
+ struct ber_element *diagmsg;
+ } res;
+ struct {
+ struct ber_element *iter;
+ struct ber_element *attrs;
+ } search;
+ } body;
+ struct ber_element *references;
+ struct aldap_page_control *page;
+};
+
+enum aldap_protocol {
+ LDAP,
+ LDAPS
+};
+
+struct aldap_url {
+ int protocol;
+ char *host;
+ in_port_t port;
+ char *dn;
+#define MAXATTR 1024
+ char *attributes[MAXATTR];
+ int scope;
+ char *filter;
+ char *buffer;
+};
+
+enum protocol_op {
+ LDAP_REQ_BIND = 0,
+ LDAP_RES_BIND = 1,
+ LDAP_REQ_UNBIND_30 = 2,
+ LDAP_REQ_SEARCH = 3,
+ LDAP_RES_SEARCH_ENTRY = 4,
+ LDAP_RES_SEARCH_RESULT = 5,
+ LDAP_REQ_MODIFY = 6,
+ LDAP_RES_MODIFY = 7,
+ LDAP_REQ_ADD = 8,
+ LDAP_RES_ADD = 9,
+ LDAP_REQ_DELETE_30 = 10,
+ LDAP_RES_DELETE = 11,
+ LDAP_REQ_MODRDN = 12,
+ LDAP_RES_MODRDN = 13,
+ LDAP_REQ_COMPARE = 14,
+ LDAP_RES_COMPARE = 15,
+ LDAP_REQ_ABANDON_30 = 16,
+
+ LDAP_RES_SEARCH_REFERENCE = 19,
+};
+
+enum deref_aliases {
+ LDAP_DEREF_NEVER = 0,
+ LDAP_DEREF_SEARCHING = 1,
+ LDAP_DEREF_FINDING = 2,
+ LDAP_DEREF_ALWAYS = 3,
+};
+
+enum authentication_choice {
+ LDAP_AUTH_SIMPLE = 0,
+};
+
+enum scope {
+ LDAP_SCOPE_BASE = 0,
+ LDAP_SCOPE_ONELEVEL = 1,
+ LDAP_SCOPE_SUBTREE = 2,
+};
+
+enum result_code {
+ LDAP_SUCCESS = 0,
+ LDAP_OPERATIONS_ERROR = 1,
+ LDAP_PROTOCOL_ERROR = 2,
+ LDAP_TIMELIMIT_EXCEEDED = 3,
+ LDAP_SIZELIMIT_EXCEEDED = 4,
+ LDAP_COMPARE_FALSE = 5,
+ LDAP_COMPARE_TRUE = 6,
+ LDAP_STRONG_AUTH_NOT_SUPPORTED = 7,
+ LDAP_STRONG_AUTH_REQUIRED = 8,
+
+ LDAP_REFERRAL = 10,
+ LDAP_ADMINLIMIT_EXCEEDED = 11,
+ LDAP_UNAVAILABLE_CRITICAL_EXTENSION = 12,
+ LDAP_CONFIDENTIALITY_REQUIRED = 13,
+ LDAP_SASL_BIND_IN_PROGRESS = 14,
+ LDAP_NO_SUCH_ATTRIBUTE = 16,
+ LDAP_UNDEFINED_TYPE = 17,
+ LDAP_INAPPROPRIATE_MATCHING = 18,
+ LDAP_CONSTRAINT_VIOLATION = 19,
+ LDAP_TYPE_OR_VALUE_EXISTS = 20,
+ LDAP_INVALID_SYNTAX = 21,
+
+ LDAP_NO_SUCH_OBJECT = 32,
+ LDAP_ALIAS_PROBLEM = 33,
+ LDAP_INVALID_DN_SYNTAX = 34,
+
+ LDAP_ALIAS_DEREF_PROBLEM = 36,
+
+ LDAP_INAPPROPRIATE_AUTH = 48,
+ LDAP_INVALID_CREDENTIALS = 49,
+ LDAP_INSUFFICIENT_ACCESS = 50,
+ LDAP_BUSY = 51,
+ LDAP_UNAVAILABLE = 52,
+ LDAP_UNWILLING_TO_PERFORM = 53,
+ LDAP_LOOP_DETECT = 54,
+
+ LDAP_NAMING_VIOLATION = 64,
+ LDAP_OBJECT_CLASS_VIOLATION = 65,
+ LDAP_NOT_ALLOWED_ON_NONLEAF = 66,
+ LDAP_NOT_ALLOWED_ON_RDN = 67,
+ LDAP_ALREADY_EXISTS = 68,
+ LDAP_NO_OBJECT_CLASS_MODS = 69,
+
+ LDAP_AFFECTS_MULTIPLE_DSAS = 71,
+
+ LDAP_OTHER = 80,
+};
+
+enum filter {
+ LDAP_FILT_AND = 0,
+ LDAP_FILT_OR = 1,
+ LDAP_FILT_NOT = 2,
+ LDAP_FILT_EQ = 3,
+ LDAP_FILT_SUBS = 4,
+ LDAP_FILT_GE = 5,
+ LDAP_FILT_LE = 6,
+ LDAP_FILT_PRES = 7,
+ LDAP_FILT_APPR = 8,
+};
+
+enum subfilter {
+ LDAP_FILT_SUBS_INIT = 0,
+ LDAP_FILT_SUBS_ANY = 1,
+ LDAP_FILT_SUBS_FIN = 2,
+};
+
+struct aldap *aldap_init(int fd);
+int aldap_close(struct aldap *);
+struct aldap_message *aldap_parse(struct aldap *);
+void aldap_freemsg(struct aldap_message *);
+
+int aldap_bind(struct aldap *, char *, char *);
+int aldap_unbind(struct aldap *);
+int aldap_search(struct aldap *, char *, enum scope, char *, char **, int, int, int, struct aldap_page_control *);
+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 *);
+void aldap_free_references(char **values);
+#if 0
+int aldap_parse_url(char *, struct aldap_url *);
+void aldap_free_url(struct aldap_url *);
+int aldap_search_url(struct aldap *, char *, int, int, int);
+#endif
+
+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 **);
+
+struct aldap_page_control *aldap_parse_page_control(struct ber_element *, size_t len);
+void aldap_freepage(struct aldap_page_control *);
diff --git a/usr.sbin/ypldap/ber.c b/usr.sbin/ypldap/ber.c
new file mode 100644
index 0000000..d7e17b6
--- /dev/null
+++ b/usr.sbin/ypldap/ber.c
@@ -0,0 +1,1269 @@
+/* $OpenBSD: ber.c,v 1.9 2015/02/12 00:30:38 pelikan Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ * Copyright (c) 2006, 2007 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2006, 2007 Marc Balmer <mbalmer@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <err.h> /* XXX for debug output */
+#include <stdio.h> /* XXX for debug output */
+#include <strings.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
+#define BER_TAG_MORE 0x80 /* more subsequent octets */
+#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, u_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,
+ 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, u_char *c);
+static ssize_t ber_read(struct ber *ber, void *buf, size_t len);
+
+#ifdef DEBUG
+#define DPRINTF(...) printf(__VA_ARGS__)
+#else
+#define DPRINTF(...) do { } while (0)
+#endif
+
+struct ber_element *
+ber_get_element(unsigned long encoding)
+{
+ struct ber_element *elm;
+
+ if ((elm = calloc(1, sizeof(*elm))) == NULL)
+ return NULL;
+
+ elm->be_encoding = encoding;
+ ber_set_header(elm, BER_CLASS_UNIVERSAL, BER_TYPE_DEFAULT);
+
+ return elm;
+}
+
+void
+ber_set_header(struct ber_element *elm, int class, unsigned long type)
+{
+ elm->be_class = class & BER_CLASS_MASK;
+ if (type == BER_TYPE_DEFAULT)
+ type = elm->be_encoding;
+ elm->be_type = type;
+}
+
+void
+ber_link_elements(struct ber_element *prev, struct ber_element *elm)
+{
+ if (prev != NULL) {
+ if ((prev->be_encoding == BER_TYPE_SEQUENCE ||
+ prev->be_encoding == BER_TYPE_SET) &&
+ prev->be_sub == NULL)
+ prev->be_sub = elm;
+ else
+ prev->be_next = elm;
+ }
+}
+
+struct ber_element *
+ber_unlink_elements(struct ber_element *prev)
+{
+ struct ber_element *elm;
+
+ if ((prev->be_encoding == BER_TYPE_SEQUENCE ||
+ prev->be_encoding == BER_TYPE_SET) &&
+ prev->be_sub != NULL) {
+ elm = prev->be_sub;
+ prev->be_sub = NULL;
+ } else {
+ elm = prev->be_next;
+ prev->be_next = NULL;
+ }
+
+ return (elm);
+}
+
+void
+ber_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);
+
+ /* cleanup old element */
+ ber_free_elements(ber);
+}
+
+struct ber_element *
+ber_add_sequence(struct ber_element *prev)
+{
+ struct ber_element *elm;
+
+ if ((elm = ber_get_element(BER_TYPE_SEQUENCE)) == NULL)
+ return NULL;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+struct ber_element *
+ber_add_set(struct ber_element *prev)
+{
+ struct ber_element *elm;
+
+ if ((elm = ber_get_element(BER_TYPE_SET)) == NULL)
+ return NULL;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+struct ber_element *
+ber_add_enumerated(struct ber_element *prev, long long val)
+{
+ struct ber_element *elm;
+ u_int i, len = 0;
+ u_char cur, last = 0;
+
+ if ((elm = ber_get_element(BER_TYPE_ENUMERATED)) == NULL)
+ return NULL;
+
+ elm->be_numeric = val;
+
+ for (i = 0; i < sizeof(long long); i++) {
+ cur = val & 0xff;
+ if (cur != 0 && cur != 0xff)
+ len = i;
+ if ((cur == 0 && last & 0x80) ||
+ (cur == 0xff && (last & 0x80) == 0))
+ len = i;
+ val >>= 8;
+ last = cur;
+ }
+ elm->be_len = len + 1;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+struct ber_element *
+ber_add_integer(struct ber_element *prev, long long val)
+{
+ struct ber_element *elm;
+ u_int i, len = 0;
+ u_char cur, last = 0;
+
+ if ((elm = ber_get_element(BER_TYPE_INTEGER)) == NULL)
+ return NULL;
+
+ elm->be_numeric = val;
+
+ for (i = 0; i < sizeof(long long); i++) {
+ cur = val & 0xff;
+ if (cur != 0 && cur != 0xff)
+ len = i;
+ if ((cur == 0 && last & 0x80) ||
+ (cur == 0xff && (last & 0x80) == 0))
+ len = i;
+ val >>= 8;
+ last = cur;
+ }
+ elm->be_len = len + 1;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+int
+ber_get_integer(struct ber_element *elm, long long *n)
+{
+ if (elm->be_encoding != BER_TYPE_INTEGER)
+ return -1;
+
+ *n = elm->be_numeric;
+ return 0;
+}
+
+int
+ber_get_enumerated(struct ber_element *elm, long long *n)
+{
+ if (elm->be_encoding != BER_TYPE_ENUMERATED)
+ return -1;
+
+ *n = elm->be_numeric;
+ return 0;
+}
+
+
+struct ber_element *
+ber_add_boolean(struct ber_element *prev, int bool)
+{
+ struct ber_element *elm;
+
+ if ((elm = ber_get_element(BER_TYPE_BOOLEAN)) == NULL)
+ return NULL;
+
+ elm->be_numeric = bool ? 0xff : 0;
+ elm->be_len = 1;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+int
+ber_get_boolean(struct ber_element *elm, int *b)
+{
+ if (elm->be_encoding != BER_TYPE_BOOLEAN)
+ return -1;
+
+ *b = !(elm->be_numeric == 0);
+ return 0;
+}
+
+struct ber_element *
+ber_add_string(struct ber_element *prev, const char *string)
+{
+ return ber_add_nstring(prev, string, strlen(string));
+}
+
+struct ber_element *
+ber_add_nstring(struct ber_element *prev, const char *string0, size_t len)
+{
+ struct ber_element *elm;
+ char *string;
+
+ if ((string = calloc(1, len)) == NULL)
+ return NULL;
+ if ((elm = ber_get_element(BER_TYPE_OCTETSTRING)) == NULL) {
+ free(string);
+ return NULL;
+ }
+
+ 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);
+
+ return elm;
+}
+
+int
+ber_get_string(struct ber_element *elm, char **s)
+{
+ if (elm->be_encoding != BER_TYPE_OCTETSTRING)
+ return -1;
+
+ *s = elm->be_val;
+ return 0;
+}
+
+int
+ber_get_nstring(struct ber_element *elm, void **p, size_t *len)
+{
+ if (elm->be_encoding != BER_TYPE_OCTETSTRING)
+ return -1;
+
+ *p = elm->be_val;
+ *len = elm->be_len;
+ return 0;
+}
+
+struct ber_element *
+ber_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) {
+ free(v);
+ return NULL;
+ }
+
+ 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);
+
+ return elm;
+}
+
+int
+ber_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;
+ return 0;
+}
+
+struct ber_element *
+ber_add_null(struct ber_element *prev)
+{
+ struct ber_element *elm;
+
+ if ((elm = ber_get_element(BER_TYPE_NULL)) == NULL)
+ return NULL;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+int
+ber_get_null(struct ber_element *elm)
+{
+ if (elm->be_encoding != BER_TYPE_NULL)
+ return -1;
+
+ return 0;
+}
+
+struct ber_element *
+ber_add_eoc(struct ber_element *prev)
+{
+ struct ber_element *elm;
+
+ if ((elm = ber_get_element(BER_TYPE_EOC)) == NULL)
+ return NULL;
+
+ ber_link_elements(prev, elm);
+
+ return elm;
+}
+
+int
+ber_get_eoc(struct ber_element *elm)
+{
+ if (elm->be_encoding != BER_TYPE_EOC)
+ return -1;
+
+ return 0;
+}
+
+size_t
+ber_oid2ber(struct ber_oid *o, u_int8_t *buf, size_t len)
+{
+ 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)
+ return (0);
+
+ 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 >= (u_int)(1 << k)) {
+ if (len)
+ buf[j] = v >> k | BER_TAG_MORE;
+ j++;
+ }
+ }
+ if (len)
+ buf[j] = v & BER_TAG_TYPE_MASK;
+ j++;
+ }
+
+ return (j);
+}
+
+int
+ber_string2oid(const char *oidstr, struct ber_oid *o)
+{
+ char *sp, *p, str[BUFSIZ];
+ const char *errstr;
+
+ if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str))
+ return (-1);
+ bzero(o, sizeof(*o));
+
+ /* Parse OID strings in the common forms n.n.n, n_n_n_n, or n-n-n */
+ for (p = sp = str; p != NULL; sp = p) {
+ if ((p = strpbrk(p, "._-")) != NULL)
+ *p++ = '\0';
+ o->bo_id[o->bo_n++] = strtonum(sp, 0, UINT_MAX, &errstr);
+ if (errstr || o->bo_n > BER_MAX_OID_LEN)
+ return (-1);
+ }
+
+ return (0);
+}
+
+struct ber_element *
+ber_add_oid(struct ber_element *prev, struct ber_oid *o)
+{
+ struct ber_element *elm;
+ u_int8_t *buf;
+ size_t len;
+
+ if ((elm = ber_get_element(BER_TYPE_OBJECT)) == NULL)
+ return (NULL);
+
+ if ((len = ber_oid2ber(o, NULL, 0)) == 0)
+ goto fail;
+
+ if ((buf = calloc(1, len)) == NULL)
+ goto fail;
+
+ elm->be_val = buf;
+ elm->be_len = len;
+ elm->be_free = 1;
+
+ if (ber_oid2ber(o, buf, len) != len)
+ goto fail;
+
+ ber_link_elements(prev, elm);
+
+ return (elm);
+
+ fail:
+ ber_free_elements(elm);
+ return (NULL);
+}
+
+struct ber_element *
+ber_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;
+ bcopy(&o->bo_id, &no.bo_id, sizeof(no.bo_id));
+
+ return (ber_add_oid(prev, &no));
+}
+
+struct ber_element *
+ber_add_oidstring(struct ber_element *prev, const char *oidstr)
+{
+ struct ber_oid o;
+
+ if (ber_string2oid(oidstr, &o) == -1)
+ return (NULL);
+
+ return (ber_add_oid(prev, &o));
+}
+
+int
+ber_get_oid(struct ber_element *elm, struct ber_oid *o)
+{
+ u_int8_t *buf;
+ size_t len, i = 0, j = 0;
+
+ if (elm->be_encoding != BER_TYPE_OBJECT)
+ return (-1);
+
+ buf = elm->be_val;
+ len = elm->be_len;
+
+ if (!buf[i])
+ return (-1);
+
+ bzero(o, sizeof(*o));
+ o->bo_id[j++] = buf[i] / 40;
+ o->bo_id[j++] = buf[i++] % 40;
+ for (; i < len && j < BER_MAX_OID_LEN; i++) {
+ o->bo_id[j] = (o->bo_id[j] << 7) + (buf[i] & ~0x80);
+ if (buf[i] & 0x80)
+ continue;
+ j++;
+ }
+ o->bo_n = j;
+
+ return (0);
+}
+
+struct ber_element *
+ber_printf_elements(struct ber_element *ber, char *fmt, ...)
+{
+ va_list ap;
+ int d, class;
+ size_t len;
+ unsigned long type;
+ long long i;
+ char *s;
+ void *p;
+ struct ber_oid *o;
+ struct ber_element *sub = ber, *e;
+
+ 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)
+ goto fail;
+ break;
+ case 'b':
+ d = va_arg(ap, int);
+ if ((ber = ber_add_boolean(ber, d)) == NULL)
+ goto fail;
+ break;
+ case 'd':
+ d = va_arg(ap, int);
+ if ((ber = ber_add_integer(ber, d)) == NULL)
+ goto fail;
+ break;
+ case 'e':
+ e = va_arg(ap, struct ber_element *);
+ ber_link_elements(ber, e);
+ break;
+ case 'E':
+ i = va_arg(ap, long long);
+ if ((ber = ber_add_enumerated(ber, i)) == NULL)
+ goto fail;
+ break;
+ case 'i':
+ i = va_arg(ap, long long);
+ if ((ber = ber_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)
+ goto fail;
+ break;
+ case 'o':
+ s = va_arg(ap, char *);
+ if ((ber = ber_add_oidstring(ber, s)) == NULL)
+ goto fail;
+ break;
+ case 's':
+ s = va_arg(ap, char *);
+ if ((ber = ber_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);
+ break;
+ case 'x':
+ s = va_arg(ap, char *);
+ len = va_arg(ap, size_t);
+ if ((ber = ber_add_nstring(ber, s, len)) == NULL)
+ goto fail;
+ break;
+ case '0':
+ if ((ber = ber_add_null(ber)) == NULL)
+ goto fail;
+ break;
+ case '{':
+ if ((ber = sub = ber_add_sequence(ber)) == NULL)
+ goto fail;
+ break;
+ case '(':
+ if ((ber = sub = ber_add_set(ber)) == NULL)
+ goto fail;
+ break;
+ case '}':
+ case ')':
+ ber = sub;
+ break;
+ case '.':
+ if ((e = ber_add_eoc(ber)) == NULL)
+ goto fail;
+ ber = e;
+ break;
+ default:
+ break;
+ }
+ }
+ va_end(ap);
+
+ return (ber);
+ fail:
+ ber_free_elements(ber);
+ return (NULL);
+}
+
+int
+ber_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;
+ void **ptr;
+ size_t *len, ret = 0, n = strlen(fmt);
+ char **s;
+ struct ber_oid *o;
+ struct ber_element *parent[_MAX_SEQ], **e;
+
+ bzero(parent, sizeof(struct ber_element *) * _MAX_SEQ);
+
+ va_start(ap, fmt);
+ while (*fmt) {
+ switch (*fmt++) {
+ case 'B':
+ ptr = va_arg(ap, void **);
+ len = va_arg(ap, size_t *);
+ if (ber_get_bitstring(ber, ptr, len) == -1)
+ goto fail;
+ ret++;
+ break;
+ case 'b':
+ d = va_arg(ap, int *);
+ if (ber_get_boolean(ber, d) == -1)
+ goto fail;
+ ret++;
+ break;
+ case 'e':
+ e = va_arg(ap, struct ber_element **);
+ *e = ber;
+ ret++;
+ continue;
+ case 'E':
+ i = va_arg(ap, long long *);
+ if (ber_get_enumerated(ber, i) == -1)
+ goto fail;
+ ret++;
+ break;
+ case 'i':
+ i = va_arg(ap, long long *);
+ if (ber_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)
+ goto fail;
+ ret++;
+ break;
+ case 'S':
+ ret++;
+ break;
+ case 's':
+ s = va_arg(ap, char **);
+ if (ber_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;
+ ret++;
+ continue;
+ case 'x':
+ ptr = va_arg(ap, void **);
+ len = va_arg(ap, size_t *);
+ if (ber_get_nstring(ber, ptr, len) == -1)
+ goto fail;
+ ret++;
+ break;
+ case '0':
+ if (ber->be_encoding != BER_TYPE_NULL)
+ goto fail;
+ ret++;
+ break;
+ case '.':
+ if (ber->be_encoding != BER_TYPE_EOC)
+ goto fail;
+ ret++;
+ break;
+ 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)
+ goto fail;
+ parent[++level] = ber;
+ ber = ber->be_sub;
+ ret++;
+ continue;
+ case '}':
+ case ')':
+ if (parent[level] == NULL)
+ goto fail;
+ ber = parent[level--];
+ ret++;
+ continue;
+ default:
+ goto fail;
+ }
+
+ if (ber->be_next == NULL)
+ continue;
+ ber = ber->be_next;
+ }
+ va_end(ap);
+ return (ret == n ? 0 : -1);
+
+ fail:
+ va_end(ap);
+ return (-1);
+
+}
+
+/*
+ * write ber elements to the socket
+ *
+ * params:
+ * ber holds the socket
+ * root fully populated element tree
+ *
+ * returns:
+ * >=0 number of bytes written
+ * -1 on failure and sets errno
+ */
+int
+ber_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);
+ DPRINTF("write ber element of %zd bytes length\n", len);
+
+ if (ber->br_wbuf != NULL && ber->br_wbuf + len > ber->br_wend) {
+ free(ber->br_wbuf);
+ ber->br_wbuf = NULL;
+ }
+ if (ber->br_wbuf == NULL) {
+ if ((ber->br_wbuf = malloc(len)) == NULL)
+ return -1;
+ ber->br_wend = ber->br_wbuf + len;
+ }
+
+ /* reset write pointer */
+ ber->br_wptr = ber->br_wbuf;
+
+ if (ber_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);
+}
+
+/*
+ * read ber elements from the socket
+ *
+ * params:
+ * ber holds the socket and lot more
+ * 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.
+ *
+ * returns:
+ * !=NULL, elements read and store in the ber_element tree
+ * NULL, type mismatch or read error
+ */
+struct ber_element *
+ber_read_elements(struct ber *ber, struct ber_element *elm)
+{
+ struct ber_element *root = elm;
+
+ if (root == NULL) {
+ if ((root = ber_get_element(0)) == NULL)
+ return NULL;
+ }
+
+ DPRINTF("read ber elements, root %p\n", root);
+
+ if (ber_read_element(ber, root) == -1) {
+ /* Cleanup if root was allocated by us */
+ if (elm == NULL)
+ ber_free_elements(root);
+ return NULL;
+ }
+
+ return root;
+}
+
+void
+ber_free_elements(struct ber_element *root)
+{
+ if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE ||
+ root->be_encoding == BER_TYPE_SET))
+ ber_free_elements(root->be_sub);
+ if (root->be_next)
+ ber_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))
+ free(root->be_val);
+ free(root);
+}
+
+size_t
+ber_calc_len(struct ber_element *root)
+{
+ unsigned long 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);
+
+ /* fix header length for extended types */
+ if (root->be_type > BER_TYPE_SINGLE_MAX)
+ for (t = root->be_type; t > 0; t >>= 7)
+ size++;
+ if (root->be_len >= BER_TAG_MORE)
+ for (s = root->be_len; s > 0; s >>= 8)
+ size++;
+
+ /* calculate the length of the following elements */
+ if (root->be_next)
+ size += ber_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)
+ return (0);
+
+ return (root->be_len + size);
+}
+
+/*
+ * internal functions
+ */
+
+static int
+ber_dump_element(struct ber *ber, struct ber_element *root)
+{
+ unsigned long long l;
+ int i;
+ uint8_t u;
+
+ ber_dump_header(ber, root);
+
+ switch (root->be_encoding) {
+ case BER_TYPE_BOOLEAN:
+ case BER_TYPE_INTEGER:
+ case BER_TYPE_ENUMERATED:
+ 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);
+ }
+ break;
+ case BER_TYPE_BITSTRING:
+ return -1;
+ case BER_TYPE_OCTETSTRING:
+ case BER_TYPE_OBJECT:
+ ber_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)
+ return -1;
+ break;
+ }
+
+ if (root->be_next == NULL)
+ return 0;
+ return ber_dump_element(ber, root->be_next);
+}
+
+static void
+ber_dump_header(struct ber *ber, struct ber_element *root)
+{
+ u_char id = 0, t, buf[8];
+ unsigned long type;
+ size_t size;
+
+ /* class universal, type encoding depending on type value */
+ /* length encoding */
+ if (root->be_type <= BER_TYPE_SINGLE_MAX) {
+ id = root->be_type | (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);
+ } 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);
+
+ for (t = 0, type = root->be_type; type > 0; type >>= 7)
+ buf[t++] = type & ~BER_TAG_MORE;
+
+ while (t-- > 0) {
+ if (t > 0)
+ buf[t] |= BER_TAG_MORE;
+ ber_putc(ber, buf[t]);
+ }
+ }
+
+ if (root->be_len < BER_TAG_MORE) {
+ /* short form */
+ ber_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);
+
+ while (t > 0)
+ ber_putc(ber, buf[--t]);
+ }
+}
+
+static void
+ber_putc(struct ber *ber, u_char c)
+{
+ if (ber->br_wptr + 1 <= ber->br_wend)
+ *ber->br_wptr = c;
+ ber->br_wptr++;
+}
+
+static void
+ber_write(struct ber *ber, void *buf, size_t len)
+{
+ if (ber->br_wptr + len <= ber->br_wend)
+ bcopy(buf, ber->br_wptr, len);
+ ber->br_wptr += 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)
+{
+ u_char u;
+ size_t i = 0;
+ unsigned long t = 0;
+
+ if (ber_getc(b, &u) == -1)
+ return -1;
+
+ *class = (u >> BER_CLASS_SHIFT) & BER_CLASS_MASK;
+ *cstruct = (u & BER_TYPE_CONSTRUCTED) == BER_TYPE_CONSTRUCTED;
+
+ if ((u & BER_TAG_MASK) != BER_TAG_MASK) {
+ *tag = u & BER_TAG_MASK;
+ return 1;
+ }
+
+ do {
+ if (ber_getc(b, &u) == -1)
+ return -1;
+ t = (t << 7) | (u & ~BER_TAG_MORE);
+ i++;
+ } while (u & BER_TAG_MORE);
+
+ if (i > sizeof(unsigned long)) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ *tag = t;
+ return i + 1;
+}
+
+/*
+ * extract length of a ber object -- if length is unknown an error is returned.
+ */
+static ssize_t
+get_len(struct ber *b, ssize_t *len)
+{
+ u_char u, n;
+ ssize_t s, r;
+
+ if (ber_getc(b, &u) == -1)
+ return -1;
+ if ((u & BER_TAG_MORE) == 0) {
+ /* short form */
+ *len = u;
+ return 1;
+ }
+
+ n = u & ~BER_TAG_MORE;
+ if (sizeof(ssize_t) < n) {
+ errno = ERANGE;
+ return -1;
+ }
+ r = n + 1;
+
+ for (s = 0; n > 0; n--) {
+ if (ber_getc(b, &u) == -1)
+ return -1;
+ s = (s << 8) | u;
+ }
+
+ if (s < 0) {
+ /* overflow */
+ errno = ERANGE;
+ 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)
+{
+ long long val = 0;
+ struct ber_element *next;
+ unsigned long type;
+ int i, class, cstruct;
+ ssize_t len, r, totlen = 0;
+ u_char c;
+
+ 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");
+ totlen += r;
+ if ((r = get_len(ber, &len)) == -1)
+ return -1;
+ DPRINTF("ber read element size %zd\n", len);
+ totlen += r + len;
+
+ elm->be_type = type;
+ elm->be_len = len;
+ elm->be_class = class;
+
+ if (elm->be_encoding == 0) {
+ /* try to figure out the encoding via class, type and cstruct */
+ if (cstruct)
+ elm->be_encoding = BER_TYPE_SEQUENCE;
+ else if (class == BER_CLASS_UNIVERSAL)
+ elm->be_encoding = type;
+ else if (ber->br_application != NULL) {
+ /*
+ * Ask the application to map the encoding to a
+ * universal type. For example, a SMI IpAddress
+ * type is defined as 4 byte OCTET STRING.
+ */
+ elm->be_encoding = (*ber->br_application)(elm);
+ } else
+ /* last resort option */
+ elm->be_encoding = BER_TYPE_NULL;
+ }
+
+ switch (elm->be_encoding) {
+ case BER_TYPE_EOC: /* End-Of-Content */
+ break;
+ case BER_TYPE_BOOLEAN:
+ case BER_TYPE_INTEGER:
+ case BER_TYPE_ENUMERATED:
+ if (len > (ssize_t)sizeof(long long))
+ return -1;
+ for (i = 0; i < len; i++) {
+ if (ber_getc(ber, &c) != 1)
+ return -1;
+ val <<= 8;
+ val |= c;
+ }
+
+ /* sign extend if MSB is set */
+ if (val >> ((i - 1) * 8) & 0x80)
+ val |= ULLONG_MAX << (i * 8);
+ elm->be_numeric = val;
+ break;
+ case BER_TYPE_BITSTRING:
+ elm->be_val = malloc(len);
+ if (elm->be_val == NULL)
+ return -1;
+ elm->be_free = 1;
+ elm->be_len = len;
+ ber_read(ber, elm->be_val, len);
+ break;
+ case BER_TYPE_OCTETSTRING:
+ case BER_TYPE_OBJECT:
+ elm->be_val = malloc(len + 1);
+ if (elm->be_val == NULL)
+ return -1;
+ elm->be_free = 1;
+ elm->be_len = len;
+ ber_read(ber, elm->be_val, len);
+ ((u_char *)elm->be_val)[len] = '\0';
+ break;
+ case BER_TYPE_NULL: /* no payload */
+ if (len != 0)
+ 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)
+ return -1;
+ }
+ next = elm->be_sub;
+ while (len > 0) {
+ r = ber_read_element(ber, next);
+ if (r == -1)
+ return -1;
+ len -= r;
+ if (len > 0 && next->be_next == NULL) {
+ if ((next->be_next = ber_get_element(0)) ==
+ NULL)
+ return -1;
+ }
+ next = next->be_next;
+ }
+ break;
+ }
+ return totlen;
+}
+
+static ssize_t
+ber_readbuf(struct ber *b, void *buf, size_t nbytes)
+{
+ 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 */
+ }
+
+ bcopy(b->br_rptr, buf, len);
+ b->br_rptr += len;
+
+ return (len);
+}
+
+void
+ber_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;
+}
+
+ssize_t
+ber_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);
+}
+
+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, u_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)
+{
+ u_char *b = buf;
+ ssize_t r, remain = len;
+
+ /*
+ * 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*
+ */
+
+ 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 - (u_char *)buf);
+ b += r;
+ remain -= r;
+ }
+ return (b - (u_char *)buf);
+}
diff --git a/usr.sbin/ypldap/ber.h b/usr.sbin/ypldap/ber.h
new file mode 100644
index 0000000..eec02d4
--- /dev/null
+++ b/usr.sbin/ypldap/ber.h
@@ -0,0 +1,129 @@
+/* $OpenBSD: ber.h,v 1.2 2008/12/29 15:48:13 aschrijver Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
+ * Copyright (c) 2006, 2007 Claudio Jeker <claudio@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+struct ber_element {
+ struct ber_element *be_next;
+ unsigned long be_type;
+ unsigned long be_encoding;
+ size_t be_len;
+ int be_free;
+ u_int8_t be_class;
+ union {
+ struct ber_element *bv_sub;
+ void *bv_val;
+ long long bv_numeric;
+ } be_union;
+#define be_sub be_union.bv_sub
+#define be_val be_union.bv_val
+#define be_numeric be_union.bv_numeric
+};
+
+struct ber {
+ int fd;
+ 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 *);
+};
+
+/* well-known ber_element types */
+#define BER_TYPE_DEFAULT ((unsigned long)-1)
+#define BER_TYPE_EOC 0
+#define BER_TYPE_BOOLEAN 1
+#define BER_TYPE_INTEGER 2
+#define BER_TYPE_BITSTRING 3
+#define BER_TYPE_OCTETSTRING 4
+#define BER_TYPE_NULL 5
+#define BER_TYPE_OBJECT 6
+#define BER_TYPE_ENUMERATED 10
+#define BER_TYPE_SEQUENCE 16
+#define BER_TYPE_SET 17
+
+/* ber classes */
+#define BER_CLASS_UNIVERSAL 0x0
+#define BER_CLASS_UNIV BER_CLASS_UNIVERSAL
+#define BER_CLASS_APPLICATION 0x1
+#define BER_CLASS_APP BER_CLASS_APPLICATION
+#define BER_CLASS_CONTEXT 0x2
+#define BER_CLASS_PRIVATE 0x3
+#define BER_CLASS_MASK 0x3
+
+/* common definitions */
+#define BER_MIN_OID_LEN 2 /* OBJECT */
+#define BER_MAX_OID_LEN 32 /* OBJECT */
+
+struct ber_oid {
+ 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 *);
+struct ber_element *ber_unlink_elements(struct ber_element *);
+void ber_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 *,
+ size_t);
+int ber_get_string(struct ber_element *, char **);
+int ber_get_nstring(struct ber_element *, void **,
+ size_t *);
+struct ber_element *ber_add_bitstring(struct ber_element *, const void *,
+ size_t);
+int ber_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 *, u_int8_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 *);
+__END_DECLS
diff --git a/usr.sbin/ypldap/entries.c b/usr.sbin/ypldap/entries.c
new file mode 100644
index 0000000..b62c5fe
--- /dev/null
+++ b/usr.sbin/ypldap/entries.c
@@ -0,0 +1,148 @@
+/* $OpenBSD: entries.c,v 1.3 2015/01/16 06:40:22 deraadt Exp $ */
+/* $FreeBSD$ */
+/*
+ * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/tree.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "ypldap.h"
+
+void
+flatten_entries(struct env *env)
+{
+ size_t wrlen;
+ size_t len;
+ char *linep;
+ char *endp;
+ char *tmp;
+ struct userent *ue;
+ struct groupent *ge;
+
+ log_debug("flattening trees");
+ /*
+ * This takes all the line pointers in RB elements and
+ * concatenates them in a single string, to be able to
+ * implement next element lookup without tree traversal.
+ *
+ * An extra octet is alloced to make space for an additional NUL.
+ */
+ wrlen = env->sc_user_line_len;
+ if ((linep = calloc(1, env->sc_user_line_len + 1)) == NULL) {
+ /*
+ * XXX: try allocating a smaller chunk of memory
+ */
+ fatal("out of memory");
+ }
+ endp = linep;
+
+ RB_FOREACH(ue, user_name_tree, env->sc_user_names) {
+ /*
+ * we convert the first nul back to a column,
+ * copy the string and then convert it back to a nul.
+ */
+ ue->ue_line[strlen(ue->ue_line)] = ':';
+ log_debug("pushing line: %s", ue->ue_line);
+ len = strlen(ue->ue_line) + 1;
+ memcpy(endp, ue->ue_line, len);
+ endp[strcspn(endp, ":")] = '\0';
+ free(ue->ue_line);
+ ue->ue_line = endp;
+ endp += len;
+ wrlen -= len;
+
+ /*
+ * To save memory strdup(3) the netid_line which originally used
+ * LINE_WIDTH bytes
+ */
+ tmp = ue->ue_netid_line;
+ ue->ue_netid_line = strdup(tmp);
+ if (ue->ue_netid_line == NULL) {
+ fatal("out of memory");
+ }
+ free(tmp);
+ }
+ env->sc_user_lines = linep;
+
+ wrlen = env->sc_group_line_len;
+ if ((linep = calloc(1, env->sc_group_line_len + 1)) == NULL) {
+ /*
+ * XXX: try allocating a smaller chunk of memory
+ */
+ fatal("out of memory");
+ }
+ endp = linep;
+ RB_FOREACH(ge, group_name_tree, env->sc_group_names) {
+ /*
+ * we convert the first nul back to a column,
+ * copy the string and then convert it back to a nul.
+ */
+ ge->ge_line[strlen(ge->ge_line)] = ':';
+ log_debug("pushing line: %s", ge->ge_line);
+ len = strlen(ge->ge_line) + 1;
+ memcpy(endp, ge->ge_line, len);
+ endp[strcspn(endp, ":")] = '\0';
+ free(ge->ge_line);
+ ge->ge_line = endp;
+ endp += len;
+ wrlen -= len;
+ }
+ env->sc_group_lines = linep;
+}
+
+int
+userent_name_cmp(struct userent *ue1, struct userent *ue2)
+{
+ return (strcmp(ue1->ue_line, ue2->ue_line));
+}
+
+int
+userent_uid_cmp(struct userent *ue1, struct userent *ue2)
+{
+ return (ue1->ue_uid - ue2->ue_uid);
+}
+
+int
+groupent_name_cmp(struct groupent *ge1, struct groupent *ge2)
+{
+ return (strcmp(ge1->ge_line, ge2->ge_line));
+}
+
+int
+groupent_gid_cmp(struct groupent *ge1, struct groupent *ge2)
+{
+ return (ge1->ge_gid - ge2->ge_gid);
+}
+
+RB_GENERATE(user_name_tree, userent, ue_name_node, userent_name_cmp);
+RB_GENERATE(user_uid_tree, userent, ue_uid_node, userent_uid_cmp);
+RB_GENERATE(group_name_tree, groupent, ge_name_node, groupent_name_cmp);
+RB_GENERATE(group_gid_tree, groupent, ge_gid_node, groupent_gid_cmp);
diff --git a/usr.sbin/ypldap/ldapclient.c b/usr.sbin/ypldap/ldapclient.c
new file mode 100644
index 0000000..f6eb544
--- /dev/null
+++ b/usr.sbin/ypldap/ldapclient.c
@@ -0,0 +1,704 @@
+/* $OpenBSD: ldapclient.c,v 1.31 2014/11/16 23:24:44 tedu Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2008 Alexander Schrijver <aschrijver@openbsd.org>
+ * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/tree.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <errno.h>
+#include <err.h>
+#include <event.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "aldap.h"
+#include "ypldap.h"
+
+void client_sig_handler(int, short, void *);
+void client_dispatch_dns(int, short, void *);
+void client_dispatch_parent(int, short, void *);
+void client_shutdown(void);
+void client_connect(int, short, void *);
+void client_configure(struct env *);
+void client_periodic_update(int, short, void *);
+int client_build_req(struct idm *, struct idm_req *, struct aldap_message *,
+ int, int);
+int client_search_idm(struct env *, struct idm *, struct aldap *,
+ char **, char *, int, int, enum imsg_type);
+int client_try_idm(struct env *, struct idm *);
+int client_addr_init(struct idm *);
+int client_addr_free(struct idm *);
+
+struct aldap *client_aldap_open(struct ypldap_addr *);
+
+/*
+ * dummy wrapper to provide aldap_init with its fd's.
+ */
+struct aldap *
+client_aldap_open(struct ypldap_addr *addr)
+{
+ int fd = -1;
+ struct ypldap_addr *p;
+
+ for (p = addr; p != NULL; p = p->next) {
+ char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+ struct sockaddr *sa = (struct sockaddr *)&p->ss;
+
+ if (getnameinfo(sa, SA_LEN(sa), hbuf, sizeof(hbuf), sbuf,
+ sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV))
+ errx(1, "could not get numeric hostname");
+
+ if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0)
+ return NULL;
+
+ if (connect(fd, sa, SA_LEN(sa)) == 0)
+ break;
+
+ warn("connect to %s port %s (%s) failed", hbuf, sbuf, "tcp");
+ close(fd);
+ }
+
+ if (fd == -1)
+ return NULL;
+
+ return aldap_init(fd);
+}
+
+int
+client_addr_init(struct idm *idm)
+{
+ struct sockaddr_in *sa_in;
+ struct sockaddr_in6 *sa_in6;
+ struct ypldap_addr *h;
+
+ for (h = idm->idm_addr; h != NULL; h = h->next) {
+ switch (h->ss.ss_family) {
+ case AF_INET:
+ sa_in = (struct sockaddr_in *)&h->ss;
+ if (ntohs(sa_in->sin_port) == 0)
+ sa_in->sin_port = htons(LDAP_PORT);
+ idm->idm_state = STATE_DNS_DONE;
+ break;
+ case AF_INET6:
+ sa_in6 = (struct sockaddr_in6 *)&h->ss;
+ if (ntohs(sa_in6->sin6_port) == 0)
+ sa_in6->sin6_port = htons(LDAP_PORT);
+ idm->idm_state = STATE_DNS_DONE;
+ break;
+ default:
+ fatalx("king bula sez: wrong AF in client_addr_init");
+ /* not reached */
+ }
+ }
+
+ return (0);
+}
+
+int
+client_addr_free(struct idm *idm)
+{
+ struct ypldap_addr *h, *p;
+
+ if (idm->idm_addr == NULL)
+ return (-1);
+
+ for (h = idm->idm_addr; h != NULL; h = p) {
+ p = h->next;
+ free(h);
+ }
+
+ idm->idm_addr = NULL;
+
+ return (0);
+}
+
+void
+client_sig_handler(int sig, short event, void *p)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ client_shutdown();
+ break;
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+void
+client_dispatch_dns(int fd, short events, void *p)
+{
+ struct imsg imsg;
+ u_int16_t dlen;
+ u_char *data;
+ struct ypldap_addr *h;
+ int n, wait_cnt = 0;
+ struct idm *idm;
+ int shut = 0;
+
+ struct env *env = p;
+ struct imsgev *iev = env->sc_iev_dns;
+ struct imsgbuf *ibuf = &iev->ibuf;
+
+ if ((events & (EV_READ | EV_WRITE)) == 0)
+ fatalx("unknown event");
+
+ if (events & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0)
+ shut = 1;
+ }
+ if (events & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0)
+ shut = 1;
+ goto done;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("client_dispatch_dns: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_HOST_DNS:
+ TAILQ_FOREACH(idm, &env->sc_idms, idm_entry)
+ if (idm->idm_id == imsg.hdr.peerid)
+ break;
+ if (idm == NULL) {
+ log_warnx("IMSG_HOST_DNS with invalid peerID");
+ break;
+ }
+ if (idm->idm_addr != NULL) {
+ log_warnx("IMSG_HOST_DNS but addr != NULL!");
+ break;
+ }
+
+ dlen = imsg.hdr.len - IMSG_HEADER_SIZE;
+ if (dlen == 0) { /* no data -> temp error */
+ idm->idm_state = STATE_DNS_TEMPFAIL;
+ break;
+ }
+
+ data = (u_char *)imsg.data;
+ while (dlen >= sizeof(struct sockaddr_storage)) {
+ if ((h = calloc(1, sizeof(struct ypldap_addr))) ==
+ NULL)
+ fatal(NULL);
+ memcpy(&h->ss, data, sizeof(h->ss));
+
+ if (idm->idm_addr == NULL)
+ h->next = NULL;
+ else
+ h->next = idm->idm_addr;
+
+ idm->idm_addr = h;
+
+ data += sizeof(h->ss);
+ dlen -= sizeof(h->ss);
+ }
+ if (dlen != 0)
+ fatalx("IMSG_HOST_DNS: dlen != 0");
+
+ client_addr_init(idm);
+
+ break;
+ default:
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+ TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
+ if (client_try_idm(env, idm) == -1)
+ idm->idm_state = STATE_LDAP_FAIL;
+
+ if (idm->idm_state < STATE_LDAP_DONE)
+ wait_cnt++;
+ }
+ if (wait_cnt == 0)
+ imsg_compose_event(env->sc_iev, IMSG_END_UPDATE, 0, 0, -1,
+ NULL, 0);
+
+done:
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+void
+client_dispatch_parent(int fd, short events, void *p)
+{
+ int n;
+ int shut = 0;
+ struct imsg imsg;
+ struct env *env = p;
+ struct imsgev *iev = env->sc_iev;
+ struct imsgbuf *ibuf = &iev->ibuf;
+
+ if ((events & (EV_READ | EV_WRITE)) == 0)
+ fatalx("unknown event");
+
+ if (events & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0)
+ shut = 1;
+ }
+ if (events & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0)
+ shut = 1;
+ goto done;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("client_dispatch_parent: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CONF_START: {
+ struct env params;
+
+ if (env->sc_flags & F_CONFIGURING) {
+ log_warnx("configuration already in progress");
+ break;
+ }
+ memcpy(&params, imsg.data, sizeof(params));
+ log_debug("configuration starting");
+ env->sc_flags |= F_CONFIGURING;
+ purge_config(env);
+ memcpy(&env->sc_conf_tv, &params.sc_conf_tv,
+ sizeof(env->sc_conf_tv));
+ env->sc_flags |= params.sc_flags;
+ break;
+ }
+ case IMSG_CONF_IDM: {
+ struct idm *idm;
+
+ if (!(env->sc_flags & F_CONFIGURING))
+ break;
+ if ((idm = calloc(1, sizeof(*idm))) == NULL)
+ fatal(NULL);
+ memcpy(idm, imsg.data, sizeof(*idm));
+ idm->idm_env = env;
+ TAILQ_INSERT_TAIL(&env->sc_idms, idm, idm_entry);
+ break;
+ }
+ case IMSG_CONF_END:
+ env->sc_flags &= ~F_CONFIGURING;
+ log_debug("applying configuration");
+ client_configure(env);
+ break;
+ default:
+ log_debug("client_dispatch_parent: unexpect imsg %d",
+ imsg.hdr.type);
+
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+done:
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+void
+client_shutdown(void)
+{
+ log_info("ldap client exiting");
+ _exit(0);
+}
+
+pid_t
+ldapclient(int pipe_main2client[2])
+{
+ pid_t pid, dns_pid;
+ int pipe_dns[2];
+ struct passwd *pw;
+ struct event ev_sigint;
+ struct event ev_sigterm;
+ struct env env;
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("cannot fork");
+ break;
+ case 0:
+ break;
+ default:
+ return (pid);
+ }
+
+ bzero(&env, sizeof(env));
+ TAILQ_INIT(&env.sc_idms);
+
+ if ((pw = getpwnam(YPLDAP_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_dns) == -1)
+ fatal("socketpair");
+ dns_pid = ypldap_dns(pipe_dns, pw);
+ close(pipe_dns[1]);
+
+#ifndef DEBUG
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir");
+#else
+#warning disabling chrooting in DEBUG mode
+#endif
+ setproctitle("ldap client");
+ ypldap_process = PROC_CLIENT;
+
+#ifndef DEBUG
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("cannot drop privileges");
+#else
+#warning disabling privilege revocation in DEBUG mode
+#endif
+
+ event_init();
+ signal(SIGPIPE, SIG_IGN);
+ signal_set(&ev_sigint, SIGINT, client_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, client_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+
+ close(pipe_main2client[0]);
+ if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
+ fatal(NULL);
+ if ((env.sc_iev_dns = calloc(1, sizeof(*env.sc_iev_dns))) == NULL)
+ fatal(NULL);
+
+ env.sc_iev->events = EV_READ;
+ env.sc_iev->data = &env;
+ imsg_init(&env.sc_iev->ibuf, pipe_main2client[1]);
+ env.sc_iev->handler = client_dispatch_parent;
+ event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
+ env.sc_iev->handler, &env);
+ event_add(&env.sc_iev->ev, NULL);
+
+ env.sc_iev_dns->events = EV_READ;
+ env.sc_iev_dns->data = &env;
+ imsg_init(&env.sc_iev_dns->ibuf, pipe_dns[0]);
+ env.sc_iev_dns->handler = client_dispatch_dns;
+ event_set(&env.sc_iev_dns->ev, env.sc_iev_dns->ibuf.fd,
+ env.sc_iev_dns->events, env.sc_iev_dns->handler, &env);
+ event_add(&env.sc_iev_dns->ev, NULL);
+
+ event_dispatch();
+ client_shutdown();
+
+ return (0);
+
+}
+
+int
+client_build_req(struct idm *idm, struct idm_req *ir, struct aldap_message *m,
+ int min_attr, int max_attr)
+{
+ char **ldap_attrs;
+ int i, k;
+
+ bzero(ir, sizeof(*ir));
+ for (i = min_attr; i < max_attr; i++) {
+ if (idm->idm_flags & F_FIXED_ATTR(i)) {
+ if (strlcat(ir->ir_line, idm->idm_attrs[i],
+ sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
+ /*
+ * entry yields a line > 1024, trash it.
+ */
+ return (-1);
+
+ if (i == ATTR_UID) {
+ ir->ir_key.ik_uid = strtonum(
+ idm->idm_attrs[i], 0,
+ UID_MAX, NULL);
+ } else if (i == ATTR_GR_GID) {
+ ir->ir_key.ik_gid = strtonum(
+ idm->idm_attrs[i], 0,
+ GID_MAX, NULL);
+ }
+ } else if (idm->idm_list & F_LIST(i)) {
+ aldap_match_attr(m, idm->idm_attrs[i], &ldap_attrs);
+ for (k = 0; k >= 0 && ldap_attrs && ldap_attrs[k] != NULL; k++) {
+ /* XXX: Fail when attributes have illegal characters e.g. ',' */
+ if (strlcat(ir->ir_line, ldap_attrs[k],
+ sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
+ continue;
+ if (ldap_attrs[k+1] != NULL)
+ if (strlcat(ir->ir_line, ",",
+ sizeof(ir->ir_line))
+ >= sizeof(ir->ir_line)) {
+ aldap_free_attr(ldap_attrs);
+ return (-1);
+ }
+ }
+ aldap_free_attr(ldap_attrs);
+ } else {
+ if (aldap_match_attr(m, idm->idm_attrs[i], &ldap_attrs) == -1)
+ return (-1);
+ if (ldap_attrs[0] == NULL)
+ return (-1);
+ if (strlcat(ir->ir_line, ldap_attrs[0],
+ sizeof(ir->ir_line)) >= sizeof(ir->ir_line)) {
+ aldap_free_attr(ldap_attrs);
+ return (-1);
+ }
+ if (i == ATTR_UID) {
+ ir->ir_key.ik_uid = strtonum(
+ ldap_attrs[0], 0, UID_MAX, NULL);
+ } else if (i == ATTR_GR_GID) {
+ ir->ir_key.ik_uid = strtonum(
+ ldap_attrs[0], 0, GID_MAX, NULL);
+ }
+ aldap_free_attr(ldap_attrs);
+ }
+
+ if (i + 1 != max_attr)
+ if (strlcat(ir->ir_line, ":",
+ sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+client_search_idm(struct env *env, struct idm *idm, struct aldap *al,
+ char **attrs, char *filter, int min_attr, int max_attr,
+ enum imsg_type type)
+{
+ struct idm_req ir;
+ struct aldap_message *m;
+ struct aldap_page_control *pg = NULL;
+ const char *errstr;
+ char *dn;
+
+ dn = idm->idm_basedn;
+ if (type == IMSG_GRP_ENTRY && idm->idm_groupdn[0] != '\0')
+ dn = idm->idm_groupdn;
+
+ do {
+ if (aldap_search(al, dn, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0, 0, 0, pg) == -1) {
+ aldap_get_errno(al, &errstr);
+ log_debug("%s", errstr);
+ return (-1);
+ }
+
+ if (pg != NULL) {
+ aldap_freepage(pg);
+ pg = NULL;
+ }
+
+ while ((m = aldap_parse(al)) != NULL) {
+ if (al->msgid != m->msgid) {
+ goto fail;
+ }
+
+ if (m->message_type == LDAP_RES_SEARCH_RESULT) {
+ if (m->page != NULL && m->page->cookie_len != 0)
+ pg = m->page;
+ else
+ pg = NULL;
+
+ aldap_freemsg(m);
+ break;
+ }
+
+ if (m->message_type != LDAP_RES_SEARCH_ENTRY) {
+ goto fail;
+ }
+
+ if (client_build_req(idm, &ir, m, min_attr, max_attr) == 0)
+ imsg_compose_event(env->sc_iev, type, 0, 0, -1,
+ &ir, sizeof(ir));
+
+ aldap_freemsg(m);
+ }
+ } while (pg != NULL);
+
+ return (0);
+
+fail:
+ aldap_freemsg(m);
+ if (pg != NULL) {
+ aldap_freepage(pg);
+ }
+
+ return (-1);
+}
+
+int
+client_try_idm(struct env *env, struct idm *idm)
+{
+ const char *where;
+ char *attrs[ATTR_MAX+1];
+ int i, j;
+ struct aldap_message *m;
+ struct aldap *al;
+
+ where = "connect";
+ if ((al = client_aldap_open(idm->idm_addr)) == NULL)
+ return (-1);
+
+ if (idm->idm_flags & F_NEEDAUTH) {
+ where = "binding";
+ if (aldap_bind(al, idm->idm_binddn, idm->idm_bindcred) == -1)
+ goto bad;
+
+ where = "parsing";
+ if ((m = aldap_parse(al)) == NULL)
+ goto bad;
+ where = "verifying msgid";
+ if (al->msgid != m->msgid) {
+ aldap_freemsg(m);
+ goto bad;
+ }
+ aldap_freemsg(m);
+ }
+
+ bzero(attrs, sizeof(attrs));
+ for (i = 0, j = 0; i < ATTR_MAX; i++) {
+ if (idm->idm_flags & F_FIXED_ATTR(i))
+ continue;
+ attrs[j++] = idm->idm_attrs[i];
+ }
+ attrs[j] = NULL;
+
+ /*
+ * build password line.
+ */
+ where = "search";
+ log_debug("searching password entries");
+ if (client_search_idm(env, idm, al, attrs,
+ idm->idm_filters[FILTER_USER], 0, ATTR_MAX, IMSG_PW_ENTRY) == -1)
+ goto bad;
+
+ bzero(attrs, sizeof(attrs));
+ for (i = ATTR_GR_MIN, j = 0; i < ATTR_GR_MAX; i++) {
+ if (idm->idm_flags & F_FIXED_ATTR(i))
+ continue;
+ attrs[j++] = idm->idm_attrs[i];
+ }
+ attrs[j] = NULL;
+
+ /*
+ * build group line.
+ */
+ where = "search";
+ log_debug("searching group entries");
+ if (client_search_idm(env, idm, al, attrs,
+ idm->idm_filters[FILTER_GROUP], ATTR_GR_MIN, ATTR_GR_MAX,
+ IMSG_GRP_ENTRY) == -1)
+ goto bad;
+
+ aldap_close(al);
+
+ idm->idm_state = STATE_LDAP_DONE;
+
+ return (0);
+bad:
+ aldap_close(al);
+ log_debug("directory %s errored out in %s", idm->idm_name, where);
+ return (-1);
+}
+
+void
+client_periodic_update(int fd, short event, void *p)
+{
+ struct env *env = p;
+
+ struct idm *idm;
+ int fail_cnt = 0;
+
+ /* If LDAP isn't finished, notify the master process to trash the
+ * update. */
+ TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
+ if (idm->idm_state < STATE_LDAP_DONE)
+ fail_cnt++;
+
+ idm->idm_state = STATE_NONE;
+
+ client_addr_free(idm);
+ }
+ if (fail_cnt > 0) {
+ log_debug("trash the update");
+ imsg_compose_event(env->sc_iev, IMSG_TRASH_UPDATE, 0, 0, -1,
+ NULL, 0);
+ }
+
+ client_configure(env);
+}
+
+void
+client_configure(struct env *env)
+{
+ struct timeval tv;
+ struct idm *idm;
+ u_int16_t dlen;
+
+ log_debug("connecting to directories");
+
+ imsg_compose_event(env->sc_iev, IMSG_START_UPDATE, 0, 0, -1, NULL, 0);
+
+ /* Start the DNS lookups */
+ TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
+ dlen = strlen(idm->idm_name) + 1;
+ imsg_compose_event(env->sc_iev_dns, IMSG_HOST_DNS, idm->idm_id,
+ 0, -1, idm->idm_name, dlen);
+ }
+
+ tv.tv_sec = env->sc_conf_tv.tv_sec;
+ tv.tv_usec = env->sc_conf_tv.tv_usec;
+ evtimer_set(&env->sc_conf_ev, client_periodic_update, env);
+ evtimer_add(&env->sc_conf_ev, &tv);
+}
diff --git a/usr.sbin/ypldap/log.c b/usr.sbin/ypldap/log.c
new file mode 100644
index 0000000..aa2e8fa
--- /dev/null
+++ b/usr.sbin/ypldap/log.c
@@ -0,0 +1,161 @@
+/* $OpenBSD: log.c,v 1.1 2008/06/26 15:10:01 pyr Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+void log_init(int);
+void log_warn(const char *, ...);
+void log_warnx(const char *, ...);
+void log_info(const char *, ...);
+void log_debug(const char *, ...);
+__dead void fatal(const char *);
+__dead void fatalx(const char *);
+
+int debug;
+
+void vlog(int, const char *, va_list);
+void logit(int, const char *, ...);
+
+void
+log_init(int n_debug)
+{
+ extern char *__progname;
+
+ debug = n_debug;
+
+ if (!debug)
+ openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+ tzset();
+}
+
+void
+logit(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vlog(pri, fmt, ap);
+ va_end(ap);
+}
+
+void
+vlog(int pri, const char *fmt, va_list ap)
+{
+ char *nfmt;
+
+ if (debug) {
+ /* best effort in out of mem situations */
+ if (asprintf(&nfmt, "%s\n", fmt) == -1) {
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vfprintf(stderr, nfmt, ap);
+ free(nfmt);
+ }
+ fflush(stderr);
+ } else
+ vsyslog(pri, fmt, ap);
+}
+
+
+void
+log_warn(const char *emsg, ...)
+{
+ char *nfmt;
+ va_list ap;
+
+ /* best effort to even work in out of memory situations */
+ if (emsg == NULL)
+ logit(LOG_CRIT, "%s", strerror(errno));
+ else {
+ va_start(ap, emsg);
+
+ if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) {
+ /* we tried it... */
+ vlog(LOG_CRIT, emsg, ap);
+ logit(LOG_CRIT, "%s", strerror(errno));
+ } else {
+ vlog(LOG_CRIT, nfmt, ap);
+ free(nfmt);
+ }
+ va_end(ap);
+ }
+}
+
+void
+log_warnx(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_CRIT, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_info(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_INFO, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (debug > 1) {
+ va_start(ap, emsg);
+ vlog(LOG_DEBUG, emsg, ap);
+ va_end(ap);
+ }
+}
+
+void
+fatal(const char *emsg)
+{
+ if (emsg == NULL)
+ logit(LOG_CRIT, "fatal: %s", strerror(errno));
+ else
+ if (errno)
+ logit(LOG_CRIT, "fatal: %s: %s",
+ emsg, strerror(errno));
+ else
+ logit(LOG_CRIT, "fatal: %s", emsg);
+
+ exit(1);
+}
+
+void
+fatalx(const char *emsg)
+{
+ errno = 0;
+ fatal(emsg);
+}
diff --git a/usr.sbin/ypldap/parse.y b/usr.sbin/ypldap/parse.y
new file mode 100644
index 0000000..1900909
--- /dev/null
+++ b/usr.sbin/ypldap/parse.y
@@ -0,0 +1,837 @@
+/* $OpenBSD: parse.y,v 1.18 2015/01/16 06:40:22 deraadt Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
+ * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "ypldap.h"
+
+TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
+static struct file {
+ TAILQ_ENTRY(file) entry;
+ FILE *stream;
+ char *name;
+ int lineno;
+ int errors;
+} *file, *topfile;
+struct file *pushfile(const char *, int);
+int popfile(void);
+int check_file_secrecy(int, const char *);
+int yyparse(void);
+int yylex(void);
+int yyerror(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)))
+ __attribute__((__nonnull__ (1)));
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int lgetc(int);
+int lungetc(int);
+int findeol(void);
+
+TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+ TAILQ_ENTRY(sym) entry;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
+int symset(const char *, const char *, int);
+char *symget(const char *);
+
+struct env *conf = NULL;
+struct idm *idm = NULL;
+static int errors = 0;
+
+typedef struct {
+ union {
+ int64_t number;
+ char *string;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token SERVER FILTER ATTRIBUTE BASEDN BINDDN GROUPDN BINDCRED MAPS CHANGE DOMAIN PROVIDE
+%token USER GROUP TO EXPIRE HOME SHELL GECOS UID GID INTERVAL
+%token PASSWD NAME FIXED LIST GROUPNAME GROUPPASSWD GROUPGID MAP
+%token INCLUDE DIRECTORY CLASS PORT ERROR GROUPMEMBERS
+%token <v.string> STRING
+%token <v.number> NUMBER
+%type <v.number> opcode attribute
+%type <v.string> port
+
+%%
+
+grammar : /* empty */
+ | grammar '\n'
+ | grammar include '\n'
+ | grammar varset '\n'
+ | grammar directory '\n'
+ | grammar main '\n'
+ | grammar error '\n' { file->errors++; }
+ ;
+
+nl : '\n' optnl
+ ;
+
+optnl : '\n' optnl
+ | /* empty */
+ ;
+
+
+include : INCLUDE STRING {
+ struct file *nfile;
+
+ if ((nfile = pushfile($2, 0)) == NULL) {
+ yyerror("failed to include file %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+
+ file = nfile;
+ lungetc('\n');
+ }
+ ;
+
+varset : STRING '=' STRING {
+ if (symset($1, $3, 0) == -1)
+ fatal("cannot store variable");
+ free($1);
+ free($3);
+ }
+ ;
+
+port : /* empty */ { $$ = NULL; }
+ | PORT STRING { $$ = $2; }
+ ;
+
+opcode : GROUP { $$ = 0; }
+ | PASSWD { $$ = 1; }
+ ;
+
+
+attribute : NAME { $$ = 0; }
+ | PASSWD { $$ = 1; }
+ | UID { $$ = 2; }
+ | GID { $$ = 3; }
+ | CLASS { $$ = 4; }
+ | CHANGE { $$ = 5; }
+ | EXPIRE { $$ = 6; }
+ | GECOS { $$ = 7; }
+ | HOME { $$ = 8; }
+ | SHELL { $$ = 9; }
+ | GROUPNAME { $$ = 10; }
+ | GROUPPASSWD { $$ = 11; }
+ | GROUPGID { $$ = 12; }
+ | GROUPMEMBERS { $$ = 13; }
+ ;
+
+diropt : BINDDN STRING {
+ idm->idm_flags |= F_NEEDAUTH;
+ if (strlcpy(idm->idm_binddn, $2,
+ sizeof(idm->idm_binddn)) >=
+ sizeof(idm->idm_binddn)) {
+ yyerror("directory binddn truncated");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ | BINDCRED STRING {
+ idm->idm_flags |= F_NEEDAUTH;
+ if (strlcpy(idm->idm_bindcred, $2,
+ sizeof(idm->idm_bindcred)) >=
+ sizeof(idm->idm_bindcred)) {
+ yyerror("directory bindcred truncated");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ | BASEDN STRING {
+ if (strlcpy(idm->idm_basedn, $2,
+ sizeof(idm->idm_basedn)) >=
+ sizeof(idm->idm_basedn)) {
+ yyerror("directory basedn truncated");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ | GROUPDN STRING {
+ if(strlcpy(idm->idm_groupdn, $2,
+ sizeof(idm->idm_groupdn)) >=
+ sizeof(idm->idm_groupdn)) {
+ yyerror("directory groupdn truncated");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ | opcode FILTER STRING {
+ if (strlcpy(idm->idm_filters[$1], $3,
+ sizeof(idm->idm_filters[$1])) >=
+ sizeof(idm->idm_filters[$1])) {
+ yyerror("filter truncated");
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
+ | ATTRIBUTE attribute MAPS TO STRING {
+ if (strlcpy(idm->idm_attrs[$2], $5,
+ sizeof(idm->idm_attrs[$2])) >=
+ sizeof(idm->idm_attrs[$2])) {
+ yyerror("attribute truncated");
+ free($5);
+ YYERROR;
+ }
+ free($5);
+ }
+ | FIXED ATTRIBUTE attribute STRING {
+ if (strlcpy(idm->idm_attrs[$3], $4,
+ sizeof(idm->idm_attrs[$3])) >=
+ sizeof(idm->idm_attrs[$3])) {
+ yyerror("attribute truncated");
+ free($4);
+ YYERROR;
+ }
+ idm->idm_flags |= F_FIXED_ATTR($3);
+ free($4);
+ }
+ | LIST attribute MAPS TO STRING {
+ if (strlcpy(idm->idm_attrs[$2], $5,
+ sizeof(idm->idm_attrs[$2])) >=
+ sizeof(idm->idm_attrs[$2])) {
+ yyerror("attribute truncated");
+ free($5);
+ YYERROR;
+ }
+ idm->idm_list |= F_LIST($2);
+ free($5);
+ }
+ ;
+
+directory : DIRECTORY STRING port {
+ if ((idm = calloc(1, sizeof(*idm))) == NULL)
+ fatal(NULL);
+ idm->idm_id = conf->sc_maxid++;
+
+ if (strlcpy(idm->idm_name, $2,
+ sizeof(idm->idm_name)) >=
+ sizeof(idm->idm_name)) {
+ yyerror("attribute truncated");
+ free($2);
+ YYERROR;
+ }
+
+ free($2);
+ } '{' optnl diropts '}' {
+ TAILQ_INSERT_TAIL(&conf->sc_idms, idm, idm_entry);
+ idm = NULL;
+ }
+ ;
+
+main : INTERVAL NUMBER {
+ conf->sc_conf_tv.tv_sec = $2;
+ conf->sc_conf_tv.tv_usec = 0;
+ }
+ | DOMAIN STRING {
+ if (strlcpy(conf->sc_domainname, $2,
+ sizeof(conf->sc_domainname)) >=
+ sizeof(conf->sc_domainname)) {
+ yyerror("domainname truncated");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ | PROVIDE MAP STRING {
+ if (strcmp($3, "passwd.byname") == 0)
+ conf->sc_flags |= YPMAP_PASSWD_BYNAME;
+ else if (strcmp($3, "passwd.byuid") == 0)
+ conf->sc_flags |= YPMAP_PASSWD_BYUID;
+ else if (strcmp($3, "master.passwd.byname") == 0)
+ conf->sc_flags |= YPMAP_MASTER_PASSWD_BYNAME;
+ else if (strcmp($3, "master.passwd.byuid") == 0)
+ conf->sc_flags |= YPMAP_MASTER_PASSWD_BYUID;
+ else if (strcmp($3, "group.byname") == 0)
+ conf->sc_flags |= YPMAP_GROUP_BYNAME;
+ else if (strcmp($3, "group.bygid") == 0)
+ conf->sc_flags |= YPMAP_GROUP_BYGID;
+ else if (strcmp($3, "netid.byname") == 0)
+ conf->sc_flags |= YPMAP_NETID_BYNAME;
+ else {
+ yyerror("unsupported map type: %s", $3);
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
+ ;
+
+diropts : diropts diropt nl
+ | diropt optnl
+ ;
+
+%%
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+
+ file->errors++;
+ va_start(ap, fmt);
+ if (vasprintf(&msg, fmt, ap) == -1)
+ fatalx("yyerror vasprintf");
+ va_end(ap);
+ logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
+ free(msg);
+ return (0);
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+ return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ { "attribute", ATTRIBUTE },
+ { "basedn", BASEDN },
+ { "bindcred", BINDCRED },
+ { "binddn", BINDDN },
+ { "change", CHANGE },
+ { "class", CLASS },
+ { "directory", DIRECTORY },
+ { "domain", DOMAIN },
+ { "expire", EXPIRE },
+ { "filter", FILTER },
+ { "fixed", FIXED },
+ { "gecos", GECOS },
+ { "gid", GID },
+ { "group", GROUP },
+ { "groupdn", GROUPDN },
+ { "groupgid", GROUPGID },
+ { "groupmembers", GROUPMEMBERS },
+ { "groupname", GROUPNAME },
+ { "grouppasswd", GROUPPASSWD },
+ { "home", HOME },
+ { "include", INCLUDE },
+ { "interval", INTERVAL },
+ { "list", LIST },
+ { "map", MAP },
+ { "maps", MAPS },
+ { "name", NAME },
+ { "passwd", PASSWD },
+ { "port", PORT },
+ { "provide", PROVIDE },
+ { "server", SERVER },
+ { "shell", SHELL },
+ { "to", TO },
+ { "uid", UID },
+ { "user", USER },
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p)
+ return (p->k_val);
+ else
+ return (STRING);
+}
+
+#define MAXPUSHBACK 128
+
+u_char *parsebuf;
+int parseindex;
+u_char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+int
+lgetc(int quotec)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ if (quotec) {
+ if ((c = getc(file->stream)) == EOF) {
+ yyerror("reached end of file while parsing "
+ "quoted string");
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ return (quotec);
+ }
+ return (c);
+ }
+
+ while ((c = getc(file->stream)) == '\\') {
+ next = getc(file->stream);
+ if (next != '\n') {
+ c = next;
+ break;
+ }
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+
+ while (c == EOF) {
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ c = getc(file->stream);
+ }
+ return (c);
+}
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ if (pushback_index)
+ c = pushback_buffer[--pushback_index];
+ else
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ u_char buf[8096];
+ u_char *p, *val;
+ int quotec, next, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(0)) == ' ' || c == '\t')
+ ; /* nothing */
+
+ yylval.lineno = file->lineno;
+ if (c == '#')
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && parsebuf == NULL) {
+ while (1) {
+ if ((c = lgetc(0)) == EOF)
+ return (0);
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return (findeol());
+ }
+ parsebuf = val;
+ parseindex = 0;
+ goto top;
+ }
+
+ switch (c) {
+ case '\'':
+ case '"':
+ quotec = c;
+ while (1) {
+ if ((c = lgetc(quotec)) == EOF)
+ return (0);
+ if (c == '\n') {
+ file->lineno++;
+ continue;
+ } else if (c == '\\') {
+ if ((next = lgetc(quotec)) == EOF)
+ return (0);
+ if (next == quotec || c == ' ' || c == '\t')
+ c = next;
+ else if (next == '\n') {
+ file->lineno++;
+ continue;
+ } else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ } else if (c == '\0') {
+ yyerror("syntax error");
+ return (findeol());
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ err(1, "yylex: strdup");
+ return (STRING);
+ }
+
+#define allowed_to_end_number(x) \
+ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && isdigit(c));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+ const char *errstr = NULL;
+
+ *p = '\0';
+ yylval.v.number = strtonum(buf, LLONG_MIN,
+ LLONG_MAX, &errstr);
+ if (errstr) {
+ yyerror("\"%s\" invalid number: %s",
+ buf, errstr);
+ return (findeol());
+ }
+ return (NUMBER);
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc(*--p);
+ c = *--p;
+ if (c == '-')
+ return (c);
+ }
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && x != '<' && x != '>' && \
+ x != '!' && x != '=' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ err(1, "yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+int
+check_file_secrecy(int fd, const char *fname)
+{
+ struct stat st;
+
+ if (fstat(fd, &st)) {
+ log_warn("cannot stat %s", fname);
+ return (-1);
+ }
+ if (st.st_uid != 0 && st.st_uid != getuid()) {
+ log_warnx("%s: owner not root or current user", fname);
+ return (-1);
+ }
+ if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
+ log_warnx("%s: group writable or world read/writable", fname);
+ return (-1);
+ }
+ return (0);
+}
+
+struct file *
+pushfile(const char *name, int secret)
+{
+ struct file *nfile;
+
+ if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
+ log_warn("malloc");
+ return (NULL);
+ }
+ if ((nfile->name = strdup(name)) == NULL) {
+ log_warn("malloc");
+ free(nfile);
+ return (NULL);
+ }
+ if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ log_warn("%s", nfile->name);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ } else if (secret &&
+ check_file_secrecy(fileno(nfile->stream), nfile->name)) {
+ fclose(nfile->stream);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ }
+ nfile->lineno = 1;
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return (nfile);
+}
+
+int
+popfile(void)
+{
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
+ prev->errors += file->errors;
+
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file);
+ file = prev;
+ return (file ? 0 : EOF);
+}
+
+int
+parse_config(struct env *x_conf, const char *filename, int opts)
+{
+ struct sym *sym, *next;
+
+ conf = x_conf;
+ bzero(conf, sizeof(*conf));
+
+ TAILQ_INIT(&conf->sc_idms);
+ conf->sc_conf_tv.tv_sec = DEFAULT_INTERVAL;
+ conf->sc_conf_tv.tv_usec = 0;
+
+ errors = 0;
+
+ if ((file = pushfile(filename, 1)) == NULL) {
+ return (-1);
+ }
+ topfile = file;
+
+ /*
+ * parse configuration
+ */
+ setservent(1);
+ yyparse();
+ endservent();
+ errors = file->errors;
+ popfile();
+
+ /* Free macros and check which have not been used. */
+ for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
+ next = TAILQ_NEXT(sym, entry);
+ if ((opts & YPLDAP_OPT_VERBOSE) && !sym->used)
+ fprintf(stderr, "warning: macro '%s' not "
+ "used\n", sym->nam);
+ if (!sym->persist) {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+
+ if (errors) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+ struct sym *sym;
+
+ for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
+ sym = TAILQ_NEXT(sym, entry))
+ ; /* nothing */
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return (0);
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return (-1);
+
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return (-1);
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return (-1);
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entry);
+ return (0);
+}
+
+int
+cmdline_symset(char *s)
+{
+ char *sym, *val;
+ int ret;
+ size_t len;
+
+ if ((val = strrchr(s, '=')) == NULL)
+ return (-1);
+
+ len = strlen(s) - strlen(val) + 1;
+ if ((sym = malloc(len)) == NULL)
+ errx(1, "cmdline_symset: malloc");
+
+ (void)strlcpy(sym, s, len);
+
+ ret = symset(sym, val + 1, 1);
+ free(sym);
+
+ return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entry)
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return (sym->val);
+ }
+ return (NULL);
+}
diff --git a/usr.sbin/ypldap/yp.c b/usr.sbin/ypldap/yp.c
new file mode 100644
index 0000000..13cc8c0
--- /dev/null
+++ b/usr.sbin/ypldap/yp.c
@@ -0,0 +1,652 @@
+/* $OpenBSD: yp.c,v 1.14 2015/02/11 01:26:00 pelikan Exp $ */
+/* $FreeBSD$ */
+/*
+ * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/tree.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_rmt.h>
+#include <rpcsvc/yp.h>
+#include <rpcsvc/ypclnt.h>
+
+#include "ypldap.h"
+
+void yp_dispatch(struct svc_req *, SVCXPRT *);
+void yp_disable_events(void);
+void yp_fd_event(int, short, void *);
+int yp_check(struct svc_req *);
+int yp_valid_domain(char *, struct ypresp_val *);
+void yp_make_val(struct ypresp_val *, char *, int);
+void yp_make_keyval(struct ypresp_key_val *, char *, char *);
+
+static struct env *env;
+
+struct yp_event {
+ TAILQ_ENTRY(yp_event) ye_entry;
+ struct event ye_event;
+};
+
+struct yp_data {
+ SVCXPRT *yp_trans_udp;
+ SVCXPRT *yp_trans_tcp;
+ TAILQ_HEAD(, yp_event) yd_events;
+};
+
+void
+yp_disable_events(void)
+{
+ struct yp_event *ye;
+
+ while ((ye = TAILQ_FIRST(&env->sc_yp->yd_events)) != NULL) {
+ TAILQ_REMOVE(&env->sc_yp->yd_events, ye, ye_entry);
+ event_del(&ye->ye_event);
+ free(ye);
+ }
+}
+
+void
+yp_enable_events(void)
+{
+ int i;
+ extern fd_set *__svc_fdset;
+ extern int __svc_fdsetsize;
+ struct yp_event *ye;
+
+ for (i = 0; i < __svc_fdsetsize; i++) {
+ if (FD_ISSET(i, __svc_fdset)) {
+ if ((ye = calloc(1, sizeof(*ye))) == NULL)
+ fatal(NULL);
+ event_set(&ye->ye_event, i, EV_READ, yp_fd_event, NULL);
+ event_add(&ye->ye_event, NULL);
+ TAILQ_INSERT_TAIL(&env->sc_yp->yd_events, ye, ye_entry);
+ }
+ }
+}
+
+void
+yp_fd_event(int fd, short event, void *p)
+{
+ svc_getreq_common(fd);
+ yp_disable_events();
+ yp_enable_events();
+}
+
+void
+yp_init(struct env *x_env)
+{
+ struct yp_data *yp;
+
+ if ((yp = calloc(1, sizeof(*yp))) == NULL)
+ fatal(NULL);
+ TAILQ_INIT(&yp->yd_events);
+
+ env = x_env;
+ env->sc_yp = yp;
+
+ (void)pmap_unset(YPPROG, YPVERS);
+
+ if ((yp->yp_trans_udp = svcudp_create(RPC_ANYSOCK)) == NULL)
+ fatal("cannot create udp service");
+ if ((yp->yp_trans_tcp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL)
+ fatal("cannot create tcp service");
+
+ if (!svc_register(yp->yp_trans_udp, YPPROG, YPVERS,
+ yp_dispatch, IPPROTO_UDP)) {
+ fatal("unable to register (YPPROG, YPVERS, udp)");
+ }
+ if (!svc_register(yp->yp_trans_tcp, YPPROG, YPVERS,
+ yp_dispatch, IPPROTO_TCP)) {
+ fatal("unable to register (YPPROG, YPVERS, tcp)");
+ }
+}
+
+/*
+ * lots of inspiration from ypserv by Mats O Jansson
+ */
+void
+yp_dispatch(struct svc_req *req, SVCXPRT *trans)
+{
+ xdrproc_t xdr_argument;
+ xdrproc_t xdr_result;
+ char *result;
+ char *(*cb)(char *, struct svc_req *);
+ union {
+ domainname ypproc_domain_2_arg;
+ domainname ypproc_domain_nonack_2_arg;
+ ypreq_key ypproc_match_2_arg;
+ ypreq_nokey ypproc_first_2_arg;
+ ypreq_key ypproc_next_2_arg;
+ ypreq_xfr ypproc_xfr_2_arg;
+ ypreq_nokey ypproc_all_2_arg;
+ ypreq_nokey ypproc_master_2_arg;
+ ypreq_nokey ypproc_order_2_arg;
+ domainname ypproc_maplist_2_arg;
+ } argument;
+
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_void;
+ cb = NULL;
+ switch (req->rq_proc) {
+ case YPPROC_NULL:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_void;
+ if (yp_check(req) == -1)
+ return;
+ result = NULL;
+ if (!svc_sendreply(trans, (xdrproc_t) xdr_void,
+ (void *)&result))
+ svcerr_systemerr(trans);
+ return;
+ case YPPROC_DOMAIN:
+ xdr_argument = (xdrproc_t) xdr_domainname;
+ xdr_result = (xdrproc_t) xdr_bool;
+ if (yp_check(req) == -1)
+ return;
+ cb = (void *)ypproc_domain_2_svc;
+ break;
+ case YPPROC_DOMAIN_NONACK:
+ xdr_argument = (xdrproc_t) xdr_domainname;
+ xdr_result = (xdrproc_t) xdr_bool;
+ if (yp_check(req) == -1)
+ return;
+ cb = (void *)ypproc_domain_nonack_2_svc;
+ break;
+ case YPPROC_MATCH:
+ xdr_argument = (xdrproc_t) xdr_ypreq_key;
+ xdr_result = (xdrproc_t) xdr_ypresp_val;
+ if (yp_check(req) == -1)
+ return;
+ cb = (void *)ypproc_match_2_svc;
+ break;
+ case YPPROC_FIRST:
+ xdr_argument = (xdrproc_t) xdr_ypreq_nokey;
+ xdr_result = (xdrproc_t) xdr_ypresp_key_val;
+ if (yp_check(req) == -1)
+ return;
+ cb = (void *)ypproc_first_2_svc;
+ break;
+ case YPPROC_NEXT:
+ xdr_argument = (xdrproc_t) xdr_ypreq_key;
+ xdr_result = (xdrproc_t) xdr_ypresp_key_val;
+ if (yp_check(req) == -1)
+ return;
+ cb = (void *)ypproc_next_2_svc;
+ break;
+ case YPPROC_XFR:
+ if (yp_check(req) == -1)
+ return;
+ svcerr_noproc(trans);
+ return;
+ case YPPROC_CLEAR:
+ log_debug("ypproc_clear");
+ if (yp_check(req) == -1)
+ return;
+ svcerr_noproc(trans);
+ return;
+ case YPPROC_ALL:
+ log_debug("ypproc_all");
+ if (yp_check(req) == -1)
+ return;
+ cb = (void *)ypproc_all_2_svc;
+ break;
+ case YPPROC_MASTER:
+ log_debug("ypproc_master");
+ if (yp_check(req) == -1)
+ return;
+ cb = (void *)ypproc_master_2_svc;
+ break;
+ case YPPROC_ORDER:
+ log_debug("ypproc_order");
+ if (yp_check(req) == -1)
+ return;
+ svcerr_noproc(trans);
+ return;
+ case YPPROC_MAPLIST:
+ log_debug("ypproc_maplist");
+ if (yp_check(req) == -1)
+ return;
+ cb = (void *)ypproc_maplist_2_svc;
+ break;
+ default:
+ svcerr_noproc(trans);
+ return;
+ }
+ (void)memset(&argument, 0, sizeof(argument));
+
+ if (!svc_getargs(trans, xdr_argument, (caddr_t)&argument)) {
+ svcerr_decode(trans);
+ return;
+ }
+ result = (*cb)((char *)&argument, req);
+ if (result != NULL && !svc_sendreply(trans, xdr_result, result))
+ svcerr_systemerr(trans);
+ if (!svc_freeargs(trans, xdr_argument, (caddr_t)&argument)) {
+ /*
+ * ypserv does it too.
+ */
+ fatal("unable to free arguments");
+ }
+}
+
+int
+yp_check(struct svc_req *req)
+{
+ struct sockaddr_in *caller;
+
+ caller = svc_getcaller(req->rq_xprt);
+ /*
+ * We might want to know who we allow here.
+ */
+ return (0);
+}
+
+int
+yp_valid_domain(char *domain, struct ypresp_val *res)
+{
+ if (domain == NULL) {
+ log_debug("NULL domain !");
+ return (-1);
+ }
+ if (strcmp(domain, env->sc_domainname) != 0) {
+ res->stat = YP_NODOM;
+ return (-1);
+ }
+ return (0);
+}
+
+bool_t *
+ypproc_domain_2_svc(domainname *arg, struct svc_req *req)
+{
+ static bool_t res;
+
+ res = (bool_t)1;
+ if (strcmp(*arg, env->sc_domainname) != 0)
+ res = (bool_t)0;
+ return (&res);
+}
+
+bool_t *
+ypproc_domain_nonack_2_svc(domainname *arg, struct svc_req *req)
+{
+ static bool_t res;
+
+ if (strcmp(*arg, env->sc_domainname) != 0)
+ return NULL;
+ res = (bool_t)1;
+ return (&res);
+}
+
+ypresp_val *
+ypproc_match_2_svc(ypreq_key *arg, struct svc_req *req)
+{
+ struct userent ukey;
+ struct userent *ue;
+ struct groupent gkey;
+ struct groupent *ge;
+ static struct ypresp_val res;
+ const char *estr;
+ char *bp, *cp;
+ char key[YPMAXRECORD+1];
+
+ log_debug("matching '%.*s' in map %s", arg->key.keydat_len,
+ arg->key.keydat_val, arg->map);
+
+ if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
+ return (&res);
+
+ if (env->sc_user_names == NULL) {
+ /*
+ * tree not ready.
+ */
+ return (NULL);
+ }
+
+ if (arg->key.keydat_len > YPMAXRECORD) {
+ log_debug("argument too long");
+ return (NULL);
+ }
+ bzero(key, sizeof(key));
+ (void)strncpy(key, arg->key.keydat_val, arg->key.keydat_len);
+
+ if (strcmp(arg->map, "passwd.byname") == 0 ||
+ strcmp(arg->map, "master.passwd.byname") == 0) {
+ ukey.ue_line = key;
+ if ((ue = RB_FIND(user_name_tree, env->sc_user_names,
+ &ukey)) == NULL) {
+ res.stat = YP_NOKEY;
+ return (&res);
+ }
+
+ yp_make_val(&res, ue->ue_line, 1);
+ return (&res);
+ } else if (strcmp(arg->map, "passwd.byuid") == 0 ||
+ strcmp(arg->map, "master.passwd.byuid") == 0) {
+ ukey.ue_uid = strtonum(key, 0, UID_MAX, &estr);
+ if (estr) {
+ res.stat = YP_BADARGS;
+ return (&res);
+ }
+
+ if ((ue = RB_FIND(user_uid_tree, &env->sc_user_uids,
+ &ukey)) == NULL) {
+ res.stat = YP_NOKEY;
+ return (&res);
+ }
+
+ yp_make_val(&res, ue->ue_line, 1);
+ return (&res);
+ } else if (strcmp(arg->map, "group.bygid") == 0) {
+ gkey.ge_gid = strtonum(key, 0, GID_MAX, &estr);
+ if (estr) {
+ res.stat = YP_BADARGS;
+ return (&res);
+ }
+ if ((ge = RB_FIND(group_gid_tree, &env->sc_group_gids,
+ &gkey)) == NULL) {
+ res.stat = YP_NOKEY;
+ return (&res);
+ }
+
+ yp_make_val(&res, ge->ge_line, 1);
+ return (&res);
+ } else if (strcmp(arg->map, "group.byname") == 0) {
+ gkey.ge_line = key;
+ if ((ge = RB_FIND(group_name_tree, env->sc_group_names,
+ &gkey)) == NULL) {
+ res.stat = YP_NOKEY;
+ return (&res);
+ }
+
+ yp_make_val(&res, ge->ge_line, 1);
+ return (&res);
+ } else if (strcmp(arg->map, "netid.byname") == 0) {
+ bp = cp = key;
+
+ if (strncmp(bp, "unix.", strlen("unix.")) != 0) {
+ res.stat = YP_BADARGS;
+ return (&res);
+ }
+
+ bp += strlen("unix.");
+
+ if (*bp == '\0') {
+ res.stat = YP_BADARGS;
+ return (&res);
+ }
+
+ if (!(cp = strsep(&bp, "@"))) {
+ res.stat = YP_BADARGS;
+ return (&res);
+ }
+
+ if (strcmp(bp, arg->domain) != 0) {
+ res.stat = YP_BADARGS;
+ return (&res);
+ }
+
+ ukey.ue_uid = strtonum(cp, 0, UID_MAX, &estr);
+ if (estr) {
+ res.stat = YP_BADARGS;
+ return (&res);
+ }
+
+ if ((ue = RB_FIND(user_uid_tree, &env->sc_user_uids,
+ &ukey)) == NULL) {
+ res.stat = YP_NOKEY;
+ return (&res);
+ }
+
+ yp_make_val(&res, ue->ue_netid_line, 0);
+ return (&res);
+
+ } else {
+ log_debug("unknown map %s", arg->map);
+ res.stat = YP_NOMAP;
+ return (&res);
+ }
+}
+
+ypresp_key_val *
+ypproc_first_2_svc(ypreq_nokey *arg, struct svc_req *req)
+{
+ static struct ypresp_key_val res;
+
+ if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
+ return (&res);
+
+ if (strcmp(arg->map, "passwd.byname") == 0 ||
+ strcmp(arg->map, "master.passwd.byname") == 0) {
+ if (env->sc_user_lines == NULL)
+ return (NULL);
+
+ yp_make_keyval(&res, env->sc_user_lines, env->sc_user_lines);
+ } else if (strcmp(arg->map, "group.byname") == 0) {
+ if (env->sc_group_lines == NULL)
+ return (NULL);
+
+ yp_make_keyval(&res, env->sc_group_lines, env->sc_group_lines);
+ } else {
+ log_debug("unknown map %s", arg->map);
+ res.stat = YP_NOMAP;
+ }
+
+ return (&res);
+}
+
+ypresp_key_val *
+ypproc_next_2_svc(ypreq_key *arg, struct svc_req *req)
+{
+ struct userent ukey;
+ struct userent *ue;
+ struct groupent gkey;
+ struct groupent *ge;
+ char *line;
+ static struct ypresp_key_val res;
+ char key[YPMAXRECORD+1];
+
+ if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
+ return (&res);
+
+ if (strcmp(arg->map, "passwd.byname") == 0 ||
+ strcmp(arg->map, "master.passwd.byname") == 0) {
+ bzero(key, sizeof(key));
+ (void)strncpy(key, arg->key.keydat_val,
+ arg->key.keydat_len);
+ ukey.ue_line = key;
+ if ((ue = RB_FIND(user_name_tree, env->sc_user_names,
+ &ukey)) == NULL) {
+ /*
+ * canacar's trick:
+ * the user might have been deleted in between calls
+ * to next since the tree may be modified by a reload.
+ * next should still return the next user in
+ * lexicographical order, hence insert the search key
+ * and look up the next field, then remove it again.
+ */
+ RB_INSERT(user_name_tree, env->sc_user_names, &ukey);
+ if ((ue = RB_NEXT(user_name_tree, &env->sc_user_names,
+ &ukey)) == NULL) {
+ RB_REMOVE(user_name_tree, env->sc_user_names,
+ &ukey);
+ res.stat = YP_NOKEY;
+ return (&res);
+ }
+ RB_REMOVE(user_name_tree, env->sc_user_names, &ukey);
+ }
+ line = ue->ue_line + (strlen(ue->ue_line) + 1);
+ line = line + (strlen(line) + 1);
+ yp_make_keyval(&res, line, line);
+ return (&res);
+
+
+ } else if (strcmp(arg->map, "group.byname") == 0) {
+ bzero(key, sizeof(key));
+ (void)strncpy(key, arg->key.keydat_val,
+ arg->key.keydat_len);
+
+ gkey.ge_line = key;
+ if ((ge = RB_FIND(group_name_tree, env->sc_group_names,
+ &gkey)) == NULL) {
+ /*
+ * canacar's trick reloaded.
+ */
+ RB_INSERT(group_name_tree, env->sc_group_names, &gkey);
+ if ((ge = RB_NEXT(group_name_tree, &env->sc_group_names,
+ &gkey)) == NULL) {
+ RB_REMOVE(group_name_tree, env->sc_group_names,
+ &gkey);
+ res.stat = YP_NOKEY;
+ return (&res);
+ }
+ RB_REMOVE(group_name_tree, env->sc_group_names, &gkey);
+ }
+
+ line = ge->ge_line + (strlen(ge->ge_line) + 1);
+ line = line + (strlen(line) + 1);
+ yp_make_keyval(&res, line, line);
+ return (&res);
+ } else {
+ log_debug("unknown map %s", arg->map);
+ res.stat = YP_NOMAP;
+ return (&res);
+ }
+}
+
+ypresp_all *
+ypproc_all_2_svc(ypreq_nokey *arg, struct svc_req *req)
+{
+ static struct ypresp_all res;
+
+ if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
+ return (&res);
+
+ svcerr_auth(req->rq_xprt, AUTH_FAILED);
+ return (NULL);
+}
+
+ypresp_master *
+ypproc_master_2_svc(ypreq_nokey *arg, struct svc_req *req)
+{
+ static struct ypresp_master res;
+
+ if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
+ return (&res);
+
+ res.stat = YP_YPERR;
+ return (&res);
+}
+
+ypresp_maplist *
+ypproc_maplist_2_svc(domainname *arg, struct svc_req *req)
+{
+ size_t i;
+ static struct {
+ char *name;
+ int cond;
+ } mapnames[] = {
+ { "passwd.byname", YPMAP_PASSWD_BYNAME },
+ { "passwd.byuid", YPMAP_PASSWD_BYUID },
+ { "master.passwd.byname", YPMAP_MASTER_PASSWD_BYNAME },
+ { "master.passwd.byuid", YPMAP_MASTER_PASSWD_BYUID },
+ { "group.byname", YPMAP_GROUP_BYNAME },
+ { "group.bygid", YPMAP_GROUP_BYGID },
+ { "netid.byname", YPMAP_NETID_BYNAME },
+ };
+ static ypresp_maplist res;
+ static struct ypmaplist maps[sizeof(mapnames) / sizeof(mapnames[0])];
+
+ if (yp_valid_domain(*arg, (struct ypresp_val *)&res) == -1)
+ return (&res);
+
+ res.stat = YP_TRUE;
+ res.maps = NULL;
+ for (i = 0; i < sizeof(mapnames) / sizeof(mapnames[0]); i++) {
+ if (!(env->sc_flags & mapnames[i].cond))
+ continue;
+ maps[i].map = mapnames[i].name;
+ maps[i].next = res.maps;
+ res.maps = &maps[i];
+ }
+
+ return (&res);
+}
+
+void
+yp_make_val(struct ypresp_val *res, char *line, int replacecolon)
+{
+ static char buf[LINE_WIDTH];
+
+ bzero(buf, sizeof(buf));
+
+ if (replacecolon)
+ line[strlen(line)] = ':';
+ (void)strlcpy(buf, line, sizeof(buf));
+ if (replacecolon)
+ line[strcspn(line, ":")] = '\0';
+ log_debug("sending out %s", buf);
+
+ res->stat = YP_TRUE;
+ res->val.valdat_len = strlen(buf);
+ res->val.valdat_val = buf;
+}
+
+void
+yp_make_keyval(struct ypresp_key_val *res, char *key, char *line)
+{
+ static char keybuf[YPMAXRECORD+1];
+ static char buf[LINE_WIDTH];
+
+ bzero(keybuf, sizeof(keybuf));
+ bzero(buf, sizeof(buf));
+
+ (void)strlcpy(keybuf, key, sizeof(keybuf));
+ res->key.keydat_len = strlen(keybuf);
+ res->key.keydat_val = keybuf;
+
+ if (*line == '\0') {
+ res->stat = YP_NOMORE;
+ return;
+ }
+ res->stat = YP_TRUE;
+ line[strlen(line)] = ':';
+ (void)strlcpy(buf, line, sizeof(buf));
+ line[strcspn(line, ":")] = '\0';
+ log_debug("sending out %s => %s", keybuf, buf);
+
+ res->val.valdat_len = strlen(buf);
+ res->val.valdat_val = buf;
+}
diff --git a/usr.sbin/ypldap/ypldap.8 b/usr.sbin/ypldap/ypldap.8
new file mode 100644
index 0000000..2974e24
--- /dev/null
+++ b/usr.sbin/ypldap/ypldap.8
@@ -0,0 +1,82 @@
+.\" $OpenBSD: ypldap.8,v 1.10 2015/07/27 17:28:40 sobrado Exp $
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: July 27 2015 $
+.Dt YPLDAP 8
+.Os
+.Sh NAME
+.Nm ypldap
+.Nd YP map server using LDAP backend
+.Sh SYNOPSIS
+.Nm
+.Op Fl dnv
+.Op Fl D Ar macro Ns = Ns Ar value
+.Op Fl f Ar file
+.Sh DESCRIPTION
+.Nm
+is a daemon providing YP maps using LDAP as a backend.
+RFC 2307 or similar LDAP schemas can be tied to the different YP maps.
+.Nm
+has the same role as
+.Xr ypserv 8
+and the two daemons are exclusive.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl D Ar macro Ns = Ns Ar value
+Define
+.Ar macro
+to be set to
+.Ar value
+on the command line.
+Overrides the definition of
+.Ar macro
+in the configuration file.
+.It Fl d
+Do not daemonize.
+If this option is specified,
+.Nm
+will run in the foreground and log to
+.Em stderr .
+.It Fl f Ar file
+Specify an alternative configuration file.
+.It Fl n
+Configtest mode.
+Only check the configuration file for validity.
+.It Fl v
+Produce more verbose output.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/ypldap.confXX" -compact
+.It Pa /etc/ypldap.conf
+Default
+.Nm
+configuration file.
+.El
+.Sh SEE ALSO
+.Xr ypldap.conf 5 ,
+.Xr ypbind 8
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 4.4 .
+.Sh AUTHORS
+The
+.Nm
+program was written by
+.An Pierre-Yves Ritschard .
diff --git a/usr.sbin/ypldap/ypldap.c b/usr.sbin/ypldap/ypldap.c
new file mode 100644
index 0000000..ab94428
--- /dev/null
+++ b/usr.sbin/ypldap/ypldap.c
@@ -0,0 +1,650 @@
+/* $OpenBSD: ypldap.c,v 1.16 2015/11/02 10:06:06 jmatthew Exp $ */
+/* $FreeBSD */
+
+/*
+ * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <sys/tree.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "ypldap.h"
+
+__dead void usage(void);
+int check_child(pid_t, const char *);
+void main_sig_handler(int, short, void *);
+void main_shutdown(void);
+void main_dispatch_client(int, short, void *);
+void main_configure_client(struct env *);
+void main_init_timer(int, short, void *);
+void main_start_update(struct env *);
+void main_trash_update(struct env *);
+void main_end_update(struct env *);
+int main_create_user_groups(struct env *);
+void purge_config(struct env *);
+void reconfigure(struct env *);
+
+int pipe_main2client[2];
+
+pid_t client_pid = 0;
+char *conffile = YPLDAP_CONF_FILE;
+int opts = 0;
+
+void
+usage(void)
+{
+ extern const char *__progname;
+
+ fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
+ __progname);
+ exit(1);
+}
+
+int
+check_child(pid_t pid, const char *pname)
+{
+ int status;
+
+ if (waitpid(pid, &status, WNOHANG) > 0) {
+ if (WIFEXITED(status)) {
+ log_warnx("check_child: lost child %s exited", pname);
+ return (1);
+ }
+ if (WIFSIGNALED(status)) {
+ log_warnx("check_child: lost child %s terminated; "
+ "signal %d", pname, WTERMSIG(status));
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/* ARGUSED */
+void
+main_sig_handler(int sig, short event, void *p)
+{
+ int die = 0;
+
+ switch (sig) {
+ case SIGTERM:
+ case SIGINT:
+ die = 1;
+ /* FALLTHROUGH */
+ case SIGCHLD:
+ if (check_child(client_pid, "ldap client")) {
+ client_pid = 0;
+ die = 1;
+ }
+ if (die)
+ main_shutdown();
+ break;
+ case SIGHUP:
+ /* reconfigure */
+ break;
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+void
+main_shutdown(void)
+{
+ _exit(0);
+}
+
+void
+main_start_update(struct env *env)
+{
+ env->update_trashed = 0;
+
+ log_debug("starting directory update");
+ env->sc_user_line_len = 0;
+ env->sc_group_line_len = 0;
+ if ((env->sc_user_names_t = calloc(1,
+ sizeof(*env->sc_user_names_t))) == NULL ||
+ (env->sc_group_names_t = calloc(1,
+ sizeof(*env->sc_group_names_t))) == NULL)
+ fatal(NULL);
+ RB_INIT(env->sc_user_names_t);
+ RB_INIT(env->sc_group_names_t);
+}
+
+/*
+ * XXX: Currently this function should only be called when updating is
+ * finished. A notification should be send to ldapclient that it should stop
+ * sending new pwd/grp entries before it can be called from different places.
+ */
+void
+main_trash_update(struct env *env)
+{
+ struct userent *ue;
+ struct groupent *ge;
+
+ env->update_trashed = 1;
+
+ while ((ue = RB_ROOT(env->sc_user_names_t)) != NULL) {
+ RB_REMOVE(user_name_tree,
+ env->sc_user_names_t, ue);
+ free(ue->ue_line);
+ free(ue->ue_netid_line);
+ free(ue);
+ }
+ free(env->sc_user_names_t);
+ env->sc_user_names_t = NULL;
+ while ((ge = RB_ROOT(env->sc_group_names_t))
+ != NULL) {
+ RB_REMOVE(group_name_tree,
+ env->sc_group_names_t, ge);
+ free(ge->ge_line);
+ free(ge);
+ }
+ free(env->sc_group_names_t);
+ env->sc_group_names_t = NULL;
+}
+
+int
+main_create_user_groups(struct env *env)
+{
+ struct userent *ue;
+ struct userent ukey;
+ struct groupent *ge;
+ gid_t pw_gid;
+ char *bp, *cp;
+ char *p;
+ const char *errstr = NULL;
+ size_t len;
+
+ RB_FOREACH(ue, user_name_tree, env->sc_user_names_t) {
+ bp = cp = ue->ue_line;
+
+ /* name */
+ bp += strlen(bp) + 1;
+
+ /* password */
+ bp += strcspn(bp, ":") + 1;
+
+ /* uid */
+ bp += strcspn(bp, ":") + 1;
+
+ /* gid */
+ bp[strcspn(bp, ":")] = '\0';
+
+ pw_gid = (gid_t)strtonum(bp, 0, GID_MAX, &errstr);
+ if (errstr) {
+ log_warnx("main: failed to parse gid for uid: %d\n", ue->ue_uid);
+ return (-1);
+ }
+
+ /* bring gid column back to its proper state */
+ bp[strlen(bp)] = ':';
+
+ if ((ue->ue_netid_line = calloc(1, LINE_WIDTH)) == NULL) {
+ return (-1);
+ }
+
+ if (snprintf(ue->ue_netid_line, LINE_WIDTH-1, "%d:%d", ue->ue_uid, pw_gid) >= LINE_WIDTH) {
+
+ return (-1);
+ }
+
+ ue->ue_gid = pw_gid;
+ }
+
+ RB_FOREACH(ge, group_name_tree, env->sc_group_names_t) {
+ bp = cp = ge->ge_line;
+
+ /* name */
+ bp += strlen(bp) + 1;
+
+ /* password */
+ bp += strcspn(bp, ":") + 1;
+
+ /* gid */
+ bp += strcspn(bp, ":") + 1;
+
+ cp = bp;
+ if (*bp == '\0')
+ continue;
+ bp = cp;
+ for (;;) {
+ if (!(cp = strsep(&bp, ",")))
+ break;
+ ukey.ue_line = cp;
+ if ((ue = RB_FIND(user_name_tree, env->sc_user_names_t,
+ &ukey)) == NULL) {
+ /* User not found */
+ log_warnx("main: user: %s is referenced as a "
+ "group member, but can't be found in the "
+ "users map.\n", ukey.ue_line);
+ if (bp != NULL)
+ *(bp-1) = ',';
+ continue;
+ }
+ if (bp != NULL)
+ *(bp-1) = ',';
+
+ /* Make sure the new group doesn't equal to the main gid */
+ if (ge->ge_gid == ue->ue_gid)
+ continue;
+
+ len = strlen(ue->ue_netid_line);
+ p = ue->ue_netid_line + len;
+
+ if ((snprintf(p, LINE_WIDTH-len-1, ",%d",
+ ge->ge_gid)) >= (int)(LINE_WIDTH-len)) {
+ return (-1);
+ }
+ }
+ }
+
+ return (0);
+}
+
+void
+main_end_update(struct env *env)
+{
+ struct userent *ue;
+ struct groupent *ge;
+
+ if (env->update_trashed)
+ return;
+
+ log_debug("updates are over, cleaning up trees now");
+
+ if (main_create_user_groups(env) == -1) {
+ main_trash_update(env);
+ return;
+ }
+
+ if (env->sc_user_names == NULL) {
+ env->sc_user_names = env->sc_user_names_t;
+ env->sc_user_lines = NULL;
+ env->sc_user_names_t = NULL;
+
+ env->sc_group_names = env->sc_group_names_t;
+ env->sc_group_lines = NULL;
+ env->sc_group_names_t = NULL;
+
+ flatten_entries(env);
+ goto make_uids;
+ }
+
+ /*
+ * clean previous tree.
+ */
+ while ((ue = RB_ROOT(env->sc_user_names)) != NULL) {
+ RB_REMOVE(user_name_tree, env->sc_user_names,
+ ue);
+ free(ue->ue_netid_line);
+ free(ue);
+ }
+ free(env->sc_user_names);
+ free(env->sc_user_lines);
+
+ env->sc_user_names = env->sc_user_names_t;
+ env->sc_user_lines = NULL;
+ env->sc_user_names_t = NULL;
+
+ while ((ge = RB_ROOT(env->sc_group_names)) != NULL) {
+ RB_REMOVE(group_name_tree,
+ env->sc_group_names, ge);
+ free(ge);
+ }
+ free(env->sc_group_names);
+ free(env->sc_group_lines);
+
+ env->sc_group_names = env->sc_group_names_t;
+ env->sc_group_lines = NULL;
+ env->sc_group_names_t = NULL;
+
+
+ flatten_entries(env);
+
+ /*
+ * trees are flat now. build up uid, gid and netid trees.
+ */
+
+make_uids:
+ RB_INIT(&env->sc_user_uids);
+ RB_INIT(&env->sc_group_gids);
+ RB_FOREACH(ue, user_name_tree, env->sc_user_names)
+ RB_INSERT(user_uid_tree,
+ &env->sc_user_uids, ue);
+ RB_FOREACH(ge, group_name_tree, env->sc_group_names)
+ RB_INSERT(group_gid_tree,
+ &env->sc_group_gids, ge);
+
+}
+
+void
+main_dispatch_client(int fd, short events, void *p)
+{
+ int n;
+ int shut = 0;
+ struct env *env = p;
+ struct imsgev *iev = env->sc_iev;
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct idm_req ir;
+ struct imsg imsg;
+
+ if ((events & (EV_READ | EV_WRITE)) == 0)
+ fatalx("unknown event");
+
+ if (events & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0)
+ shut = 1;
+ }
+ if (events & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0)
+ shut = 1;
+ goto done;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("main_dispatch_client: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_START_UPDATE:
+ main_start_update(env);
+ break;
+ case IMSG_PW_ENTRY: {
+ struct userent *ue;
+ size_t len;
+
+ if (env->update_trashed)
+ break;
+
+ (void)memcpy(&ir, imsg.data, sizeof(ir));
+ if ((ue = calloc(1, sizeof(*ue))) == NULL ||
+ (ue->ue_line = strdup(ir.ir_line)) == NULL) {
+ /*
+ * should cancel tree update instead.
+ */
+ fatal("out of memory");
+ }
+ ue->ue_uid = ir.ir_key.ik_uid;
+ len = strlen(ue->ue_line) + 1;
+ ue->ue_line[strcspn(ue->ue_line, ":")] = '\0';
+ if (RB_INSERT(user_name_tree, env->sc_user_names_t,
+ ue) != NULL) { /* dup */
+ free(ue->ue_line);
+ free(ue);
+ } else
+ env->sc_user_line_len += len;
+ break;
+ }
+ case IMSG_GRP_ENTRY: {
+ struct groupent *ge;
+ size_t len;
+
+ if (env->update_trashed)
+ break;
+
+ (void)memcpy(&ir, imsg.data, sizeof(ir));
+ if ((ge = calloc(1, sizeof(*ge))) == NULL ||
+ (ge->ge_line = strdup(ir.ir_line)) == NULL) {
+ /*
+ * should cancel tree update instead.
+ */
+ fatal("out of memory");
+ }
+ ge->ge_gid = ir.ir_key.ik_gid;
+ len = strlen(ge->ge_line) + 1;
+ ge->ge_line[strcspn(ge->ge_line, ":")] = '\0';
+ if (RB_INSERT(group_name_tree, env->sc_group_names_t,
+ ge) != NULL) { /* dup */
+ free(ge->ge_line);
+ free(ge);
+ } else
+ env->sc_group_line_len += len;
+ break;
+ }
+ case IMSG_TRASH_UPDATE:
+ main_trash_update(env);
+ break;
+ case IMSG_END_UPDATE: {
+ main_end_update(env);
+ break;
+ }
+ default:
+ log_debug("main_dispatch_client: unexpected imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+done:
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ log_debug("king bula sez: ran into dead pipe");
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+void
+main_configure_client(struct env *env)
+{
+ struct idm *idm;
+ struct imsgev *iev = env->sc_iev;
+
+ imsg_compose_event(iev, IMSG_CONF_START, 0, 0, -1, env, sizeof(*env));
+ TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
+ imsg_compose_event(iev, IMSG_CONF_IDM, 0, 0, -1,
+ idm, sizeof(*idm));
+ }
+ imsg_compose_event(iev, IMSG_CONF_END, 0, 0, -1, NULL, 0);
+}
+
+void
+main_init_timer(int fd, short event, void *p)
+{
+ struct env *env = p;
+
+ main_configure_client(env);
+}
+
+void
+purge_config(struct env *env)
+{
+ struct idm *idm;
+
+ while ((idm = TAILQ_FIRST(&env->sc_idms)) != NULL) {
+ TAILQ_REMOVE(&env->sc_idms, idm, idm_entry);
+ free(idm);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ int debug;
+ struct passwd *pw;
+ struct env env;
+ struct event ev_sigint;
+ struct event ev_sigterm;
+ struct event ev_sigchld;
+ struct event ev_sighup;
+ struct event ev_timer;
+ struct timeval tv;
+
+ debug = 0;
+ ypldap_process = PROC_MAIN;
+
+ log_init(1);
+
+ while ((c = getopt(argc, argv, "dD:nf:v")) != -1) {
+ switch (c) {
+ case 'd':
+ debug = 2;
+ break;
+ case 'D':
+ if (cmdline_symset(optarg) < 0)
+ log_warnx("could not parse macro definition %s",
+ optarg);
+ break;
+ case 'n':
+ debug = 2;
+ opts |= YPLDAP_OPT_NOACTION;
+ break;
+ case 'f':
+ conffile = optarg;
+ break;
+ case 'v':
+ opts |= YPLDAP_OPT_VERBOSE;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc)
+ usage();
+
+ RB_INIT(&env.sc_user_uids);
+ RB_INIT(&env.sc_group_gids);
+
+ if (parse_config(&env, conffile, opts))
+ exit(1);
+ if (opts & YPLDAP_OPT_NOACTION) {
+ fprintf(stderr, "configuration OK\n");
+ exit(0);
+ }
+
+ if (geteuid())
+ errx(1, "need root privileges");
+
+ log_init(debug);
+
+ if (!debug) {
+ if (daemon(1, 0) == -1)
+ err(1, "failed to daemonize");
+ }
+
+ log_info("startup%s", (debug > 1)?" [debug mode]":"");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, PF_UNSPEC,
+ pipe_main2client) == -1)
+ fatal("socketpair");
+
+ client_pid = ldapclient(pipe_main2client);
+
+ setproctitle("parent");
+ event_init();
+
+ signal_set(&ev_sigint, SIGINT, main_sig_handler, &env);
+ signal_set(&ev_sigterm, SIGTERM, main_sig_handler, &env);
+ signal_set(&ev_sighup, SIGHUP, main_sig_handler, &env);
+ signal_set(&ev_sigchld, SIGCHLD, main_sig_handler, &env);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sighup, NULL);
+ signal_add(&ev_sigchld, NULL);
+
+ close(pipe_main2client[1]);
+ if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
+ fatal(NULL);
+ imsg_init(&env.sc_iev->ibuf, pipe_main2client[0]);
+ env.sc_iev->handler = main_dispatch_client;
+
+ env.sc_iev->events = EV_READ;
+ env.sc_iev->data = &env;
+ event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
+ env.sc_iev->handler, &env);
+ event_add(&env.sc_iev->ev, NULL);
+
+ yp_init(&env);
+
+ if ((pw = getpwnam(YPLDAP_USER)) == NULL)
+ fatal("getpwnam");
+
+#ifndef DEBUG
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("cannot drop privileges");
+#else
+#warning disabling privilege revocation in debug mode
+#endif
+
+ bzero(&tv, sizeof(tv));
+ evtimer_set(&ev_timer, main_init_timer, &env);
+ evtimer_add(&ev_timer, &tv);
+
+ yp_enable_events();
+ event_dispatch();
+ main_shutdown();
+
+ return (0);
+}
+
+void
+imsg_event_add(struct imsgev *iev)
+{
+ if (iev->handler == NULL) {
+ imsg_flush(&iev->ibuf);
+ return;
+ }
+
+ iev->events = EV_READ;
+ if (iev->ibuf.w.queued)
+ iev->events |= EV_WRITE;
+
+ event_del(&iev->ev);
+ event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
+ event_add(&iev->ev, NULL);
+}
+
+int
+imsg_compose_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid,
+ pid_t pid, int fd, void *data, u_int16_t datalen)
+{
+ int ret;
+
+ if ((ret = imsg_compose(&iev->ibuf, type, peerid,
+ pid, fd, data, datalen)) != -1)
+ imsg_event_add(iev);
+ return (ret);
+}
diff --git a/usr.sbin/ypldap/ypldap.conf.5 b/usr.sbin/ypldap/ypldap.conf.5
new file mode 100644
index 0000000..5c22b3b
--- /dev/null
+++ b/usr.sbin/ypldap/ypldap.conf.5
@@ -0,0 +1,167 @@
+.\" $OpenBSD: ypldap.conf.5,v 1.19 2012/04/30 11:28:25 jmatthew Exp $
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: April 30 2012 $
+.Dt YPLDAP.CONF 5
+.Os
+.Sh NAME
+.Nm ypldap.conf
+.Nd LDAP YP map daemon configuration file
+.Sh DESCRIPTION
+The
+.Xr ypldap 8
+daemon provides YP maps using LDAP as a backend.
+.Sh SECTIONS
+The
+.Nm
+config file is divided into three main sections.
+.Bl -tag -width xxxx
+.It Sy Macros
+User-defined variables may be defined and used later, simplifying the
+configuration file.
+.It Sy Global Configuration
+Global settings for
+.Xr ypldap 8 .
+.It Sy Directories
+LDAP Directory specific parameters.
+.El
+.Sh MACROS
+Much like
+.Xr cpp 1
+or
+.Xr m4 1 ,
+macros can be defined that will later be expanded in context.
+Macro names must start with a letter, digit, or underscore,
+and may contain any of those characters.
+Macro names may not be reserved words (for example,
+.Ic domain ) .
+Macros are not expanded inside quotes.
+.Pp
+For example:
+.Bd -literal -offset indent
+
+fixed_gecos="Pulled from LDAP"
+
+fixed attribute gecos $fixed_gecos
+.Ed
+.Sh GLOBAL CONFIGURATION
+Global settings concern the main behaviour of the daemon.
+.Pp
+.Bl -tag -width Ds -compact
+.It domain Ar string
+Specify the name of the NIS domain
+.Nm
+will provide.
+.It interval Ar seconds
+Specify the interval in seconds at which the whole directory will be pulled
+from LDAP.
+.It provide map Ar string
+Specify a map that should be provided by
+.Nm
+The currently implemented maps are: passwd.byname, passwd.byuid,
+group.byname, group.bygid.
+.El
+.Sh DIRECTORIES
+Directories are used to describe the LDAP schema and help
+.Nm
+convert LDAP entries to
+.Xr passwd 5 ,
+.Xr master.passwd 5 ,
+and
+.Xr group 5
+lines.
+A directory declaration is of the following form:
+.Bd -literal -offset indent
+directory "some.host" {
+ # directives
+}
+.Ed
+.Pp
+Valid directives for directories are:
+.Bl -tag -width Ds
+.It Xo
+.Ic attribute Ar name Ic maps to Ar string
+.Xc
+Map the
+.Xr passwd 5 ,
+.Xr master.passwd 5 ,
+or
+.Xr group 5
+attribute to the LDAP attribute name supplied.
+.It Ic basedn Ar string
+Use the supplied search base as starting point for the directory search.
+.It Ic groupdn Ar string
+Use the supplied search base as starting point for the directory search for
+groups.
+If not supplied, the basedn value will be used.
+.It Ic bindcred Ar string
+Use the supplied credentials for simple authentication against the directory.
+.It Ic binddn Ar string
+Use the supplied Distinguished Name to bind to the directory.
+.It Ic fixed attribute Ar attribute string
+Do not retrieve the specified attribute from LDAP but
+instead set it unconditionally to the supplied value for
+every entry.
+.It Ic group filter Ar string
+Use the supplied LDAP filter to retrieve group entries.
+.It Xo
+.Ic list Ar name Ic maps to Ar string
+.Xc
+Map the
+.Xr passwd 5 ,
+.Xr master.passwd 5 ,
+or
+.Xr group 5
+attribute to the LDAP attribute name supplied.
+A list creates a comma separated list of all the LDAP attributes found.
+.Pp
+Valid attributes are:
+.Pp
+.Bl -tag -width groupmembers -offset indent -compact
+.It Ic name
+.It Ic passwd
+.It Ic uid
+.It Ic gid
+.It Ic gecos
+.It Ic home
+.It Ic shell
+.It Ic change
+.It Ic expire
+.It Ic class
+.It Ic groupname
+.It Ic grouppasswd
+.It Ic groupgid
+.It Ic groupmembers
+.El
+.It Ic passwd filter Ar string
+Use the supplied LDAP filter to retrieve password entries.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/ypldap.conf" -compact
+.It Pa /etc/ypldap.conf
+.Xr ypldap 8
+configuration file.
+.El
+.Sh SEE ALSO
+.Xr ypbind 8 ,
+.Xr ypldap 8 ,
+.Xr ypserv 8
+.Sh HISTORY
+The
+.Nm
+file format first appeared in
+.Ox 4.4 .
diff --git a/usr.sbin/ypldap/ypldap.h b/usr.sbin/ypldap/ypldap.h
new file mode 100644
index 0000000..fafec94
--- /dev/null
+++ b/usr.sbin/ypldap/ypldap.h
@@ -0,0 +1,222 @@
+/* $OpenBSD: ypldap.h,v 1.16 2015/01/16 06:40:22 deraadt Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <imsg.h>
+
+#define YPLDAP_USER "_ypldap"
+#define YPLDAP_CONF_FILE "/etc/ypldap.conf"
+#define DEFAULT_INTERVAL 600
+#define LINE_WIDTH 1024
+#define FILTER_WIDTH 128
+#define ATTR_WIDTH 32
+
+#define MAX_SERVERS_DNS 8
+
+enum imsg_type {
+ IMSG_NONE,
+ IMSG_CONF_START,
+ IMSG_CONF_IDM,
+ IMSG_CONF_END,
+ IMSG_START_UPDATE,
+ IMSG_END_UPDATE,
+ IMSG_TRASH_UPDATE,
+ IMSG_PW_ENTRY,
+ IMSG_GRP_ENTRY,
+ IMSG_HOST_DNS
+};
+
+struct ypldap_addr {
+ struct ypldap_addr *next;
+ struct sockaddr_storage ss;
+};
+
+enum {
+ PROC_MAIN,
+ PROC_CLIENT
+} ypldap_process;
+
+struct userent {
+ RB_ENTRY(userent) ue_name_node;
+ RB_ENTRY(userent) ue_uid_node;
+ uid_t ue_uid;
+ char *ue_line;
+ char *ue_netid_line;
+ gid_t ue_gid;
+};
+
+struct groupent {
+ RB_ENTRY(groupent) ge_name_node;
+ RB_ENTRY(groupent) ge_gid_node;
+ gid_t ge_gid;
+ char *ge_line;
+};
+
+enum client_state {
+ STATE_NONE,
+ STATE_DNS_INPROGRESS,
+ STATE_DNS_TEMPFAIL,
+ STATE_DNS_DONE,
+ STATE_LDAP_FAIL,
+ STATE_LDAP_DONE
+};
+
+/*
+ * beck, djm, dlg: pay attention to the struct name
+ */
+struct idm {
+ TAILQ_ENTRY(idm) idm_entry;
+ u_int32_t idm_id;
+ char idm_name[HOST_NAME_MAX+1];
+#define F_SSL 0x00100000
+#define F_CONFIGURING 0x00200000
+#define F_NEEDAUTH 0x00400000
+#define F_FIXED_ATTR(n) (1<<n)
+#define F_LIST(n) (1<<n)
+ enum client_state idm_state;
+ u_int32_t idm_flags; /* lower 20 reserved */
+ u_int32_t idm_list;
+ struct ypldap_addr *idm_addr;
+ in_port_t idm_port;
+ char idm_binddn[LINE_WIDTH];
+ char idm_bindcred[LINE_WIDTH];
+ char idm_basedn[LINE_WIDTH];
+ char idm_groupdn[LINE_WIDTH];
+#define FILTER_USER 1
+#define FILTER_GROUP 0
+ char idm_filters[2][FILTER_WIDTH];
+#define ATTR_NAME 0
+#define ATTR_PASSWD 1
+#define ATTR_UID 2
+#define ATTR_GID 3
+#define ATTR_CLASS 4
+#define ATTR_CHANGE 5
+#define ATTR_EXPIRE 6
+#define ATTR_GECOS 7
+#define ATTR_DIR 8
+#define ATTR_SHELL 9
+#define ATTR_GR_NAME 10
+#define ATTR_GR_PASSWD 11
+#define ATTR_GR_GID 12
+#define ATTR_GR_MEMBERS 13
+#define ATTR_MAX 10
+#define ATTR_GR_MIN 10
+#define ATTR_GR_MAX 14
+ char idm_attrs[14][ATTR_WIDTH];
+ struct env *idm_env;
+ struct event idm_ev;
+#ifdef SSL
+ struct ssl *idm_ssl;
+#endif
+};
+
+struct idm_req {
+ union {
+ uid_t ik_uid;
+ uid_t ik_gid;
+ } ir_key;
+ char ir_line[LINE_WIDTH];
+};
+
+struct imsgev {
+ struct imsgbuf ibuf;
+ void (*handler)(int, short, void *);
+ struct event ev;
+ void *data;
+ short events;
+};
+
+struct env {
+#define YPLDAP_OPT_VERBOSE 0x01
+#define YPLDAP_OPT_NOACTION 0x02
+ u_int8_t sc_opts;
+#define YPMAP_PASSWD_BYNAME 0x00000001
+#define YPMAP_PASSWD_BYUID 0x00000002
+#define YPMAP_MASTER_PASSWD_BYNAME 0x00000004
+#define YPMAP_MASTER_PASSWD_BYUID 0x00000008
+#define YPMAP_GROUP_BYNAME 0x00000010
+#define YPMAP_GROUP_BYGID 0x00000020
+#define YPMAP_NETID_BYNAME 0x00000040
+ u_int32_t sc_flags;
+
+ u_int32_t sc_maxid;
+
+ char sc_domainname[HOST_NAME_MAX+1];
+ struct timeval sc_conf_tv;
+ struct event sc_conf_ev;
+ TAILQ_HEAD(idm_list, idm) sc_idms;
+ struct imsgev *sc_iev;
+ struct imsgev *sc_iev_dns;
+
+ RB_HEAD(user_name_tree,userent) *sc_user_names;
+ RB_HEAD(user_uid_tree,userent) sc_user_uids;
+ RB_HEAD(group_name_tree,groupent)*sc_group_names;
+ RB_HEAD(group_gid_tree,groupent) sc_group_gids;
+ struct user_name_tree *sc_user_names_t;
+ struct group_name_tree *sc_group_names_t;
+ size_t sc_user_line_len;
+ size_t sc_group_line_len;
+ char *sc_user_lines;
+ char *sc_group_lines;
+
+ struct yp_data *sc_yp;
+
+ int update_trashed;
+};
+
+/* log.c */
+void log_init(int);
+void log_warn(const char *, ...);
+void log_warnx(const char *, ...);
+void log_info(const char *, ...);
+void log_debug(const char *, ...);
+void logit(int, const char *, ...);
+void vlog(int, const char *, va_list);
+__dead void fatal(const char *);
+__dead void fatalx(const char *);
+
+/* parse.y */
+int parse_config(struct env *, const char *, int);
+int cmdline_symset(char *);
+
+/* ldapclient.c */
+pid_t ldapclient(int []);
+
+/* ypldap.c */
+void purge_config(struct env *);
+void imsg_event_add(struct imsgev *);
+int imsg_compose_event(struct imsgev *, u_int16_t, u_int32_t,
+ pid_t, int, void *, u_int16_t);
+
+/* entries.c */
+void flatten_entries(struct env *);
+int userent_name_cmp(struct userent *, struct userent *);
+int userent_uid_cmp(struct userent *, struct userent *);
+int groupent_name_cmp(struct groupent *, struct groupent *);
+int groupent_gid_cmp(struct groupent *, struct groupent *);
+RB_PROTOTYPE( user_name_tree, userent, ue_name_node, userent_name_cmp);
+RB_PROTOTYPE( user_uid_tree, userent, ue_uid_node, userent_uid_cmp);
+RB_PROTOTYPE( group_name_tree, groupent, ge_name_node, groupent_name_cmp);
+RB_PROTOTYPE( group_gid_tree, groupent, ge_gid_node, groupent_gid_cmp);
+
+/* yp.c */
+void yp_init(struct env *);
+void yp_enable_events(void);
+
+/* ypldap_dns.c */
+pid_t ypldap_dns(int[2], struct passwd *);
diff --git a/usr.sbin/ypldap/ypldap_dns.c b/usr.sbin/ypldap/ypldap_dns.c
new file mode 100644
index 0000000..145fff4
--- /dev/null
+++ b/usr.sbin/ypldap/ypldap_dns.c
@@ -0,0 +1,253 @@
+/* $OpenBSD: ypldap_dns.c,v 1.8 2015/01/16 06:40:22 deraadt Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2003-2008 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/tree.h>
+#include <sys/queue.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <netdb.h>
+#include <pwd.h>
+#include <errno.h>
+#include <event.h>
+#include <resolv.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "ypldap.h"
+
+volatile sig_atomic_t quit_dns = 0;
+struct imsgev *iev_dns;
+
+void dns_dispatch_imsg(int, short, void *);
+void dns_sig_handler(int, short, void *);
+void dns_shutdown(void);
+int host_dns(const char *s, struct ypldap_addr **hn);
+
+void
+dns_sig_handler(int sig, short event, void *p)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ dns_shutdown();
+ break;
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+void
+dns_shutdown(void)
+{
+ log_info("dns engine exiting");
+ _exit(0);
+}
+
+pid_t
+ypldap_dns(int pipe_ntp[2], struct passwd *pw)
+{
+ pid_t pid;
+ struct event ev_sigint;
+ struct event ev_sigterm;
+ struct event ev_sighup;
+ struct env env;
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("cannot fork");
+ break;
+ case 0:
+ break;
+ default:
+ return (pid);
+ }
+
+ setproctitle("dns engine");
+ close(pipe_ntp[0]);
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("can't drop privileges");
+ endservent();
+
+ event_init();
+ signal_set(&ev_sigint, SIGINT, dns_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, dns_sig_handler, NULL);
+ signal_set(&ev_sighup, SIGHUP, dns_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sighup, NULL);
+
+ if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
+ fatal(NULL);
+
+ env.sc_iev->events = EV_READ;
+ env.sc_iev->data = &env;
+ imsg_init(&env.sc_iev->ibuf, pipe_ntp[1]);
+ env.sc_iev->handler = dns_dispatch_imsg;
+ event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
+ env.sc_iev->handler, &env);
+ event_add(&env.sc_iev->ev, NULL);
+
+ event_dispatch();
+ dns_shutdown();
+
+ return (0);
+}
+
+void
+dns_dispatch_imsg(int fd, short events, void *p)
+{
+ struct imsg imsg;
+ int n, cnt;
+ char *name;
+ struct ypldap_addr *h, *hn;
+ struct ibuf *buf;
+ struct env *env = p;
+ struct imsgev *iev = env->sc_iev;
+ struct imsgbuf *ibuf = &iev->ibuf;
+ int shut = 0;
+
+ if ((events & (EV_READ | EV_WRITE)) == 0)
+ fatalx("unknown event");
+
+ if (events & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0)
+ shut = 1;
+ }
+ if (events & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0)
+ shut = 1;
+ goto done;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("client_dispatch_imsg: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_HOST_DNS:
+ name = imsg.data;
+ if (imsg.hdr.len < 1 + IMSG_HEADER_SIZE)
+ fatalx("invalid IMSG_HOST_DNS received");
+ imsg.hdr.len -= 1 + IMSG_HEADER_SIZE;
+ if (name[imsg.hdr.len] != '\0' ||
+ strlen(name) != imsg.hdr.len)
+ fatalx("invalid IMSG_HOST_DNS received");
+ if ((cnt = host_dns(name, &hn)) == -1)
+ break;
+ buf = imsg_create(ibuf, IMSG_HOST_DNS,
+ imsg.hdr.peerid, 0,
+ cnt * sizeof(struct sockaddr_storage));
+ if (buf == NULL)
+ break;
+ if (cnt > 0) {
+ h = hn;
+ while (h != NULL) {
+ imsg_add(buf, &h->ss, sizeof(h->ss));
+ hn = h->next;
+ free(h);
+ h = hn;
+ }
+ }
+
+ imsg_close(ibuf, buf);
+ break;
+ default:
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+done:
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+int
+host_dns(const char *s, struct ypldap_addr **hn)
+{
+ struct addrinfo hints, *res0, *res;
+ int error, cnt = 0;
+ struct sockaddr_in *sa_in;
+ struct sockaddr_in6 *sa_in6;
+ struct ypldap_addr *h, *hh = NULL;
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
+ error = getaddrinfo(s, NULL, &hints, &res0);
+ if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
+ return (0);
+ if (error) {
+ log_warnx("could not parse \"%s\": %s", s,
+ gai_strerror(error));
+ return (-1);
+ }
+
+ for (res = res0; res && cnt < MAX_SERVERS_DNS; res = res->ai_next) {
+ if (res->ai_family != AF_INET &&
+ res->ai_family != AF_INET6)
+ continue;
+ if ((h = calloc(1, sizeof(struct ypldap_addr))) == NULL)
+ fatal(NULL);
+ h->ss.ss_family = res->ai_family;
+ if (res->ai_family == AF_INET) {
+ sa_in = (struct sockaddr_in *)&h->ss;
+ sa_in->sin_len = sizeof(struct sockaddr_in);
+ sa_in->sin_addr.s_addr = ((struct sockaddr_in *)
+ res->ai_addr)->sin_addr.s_addr;
+ } else {
+ sa_in6 = (struct sockaddr_in6 *)&h->ss;
+ sa_in6->sin6_len = sizeof(struct sockaddr_in6);
+ memcpy(&sa_in6->sin6_addr, &((struct sockaddr_in6 *)
+ res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
+ }
+
+ h->next = hh;
+ hh = h;
+ cnt++;
+ }
+ freeaddrinfo(res0);
+
+ *hn = hh;
+ return (cnt);
+}
OpenPOWER on IntegriCloud