summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/ypldap/Makefile18
-rw-r--r--usr.sbin/ypldap/buffer.c214
-rw-r--r--usr.sbin/ypldap/entries.c135
-rw-r--r--usr.sbin/ypldap/imsg.c182
-rw-r--r--usr.sbin/ypldap/ldapclient.c434
-rw-r--r--usr.sbin/ypldap/log.c160
-rw-r--r--usr.sbin/ypldap/parse.y792
-rw-r--r--usr.sbin/ypldap/yp.c614
-rw-r--r--usr.sbin/ypldap/ypldap.882
-rw-r--r--usr.sbin/ypldap/ypldap.c504
-rw-r--r--usr.sbin/ypldap/ypldap.conf.593
-rw-r--r--usr.sbin/ypldap/ypldap.h281
12 files changed, 3509 insertions, 0 deletions
diff --git a/usr.sbin/ypldap/Makefile b/usr.sbin/ypldap/Makefile
new file mode 100644
index 00000000000..f6992a3c8fc
--- /dev/null
+++ b/usr.sbin/ypldap/Makefile
@@ -0,0 +1,18 @@
+# $OpenBSD: Makefile,v 1.1 2008/06/26 15:10:01 pyr Exp $
+
+PROG= ypldap
+SRCS= parse.y ypldap.c imsg.c buffer.c log.c \
+ ldapclient.c entries.c yp.c
+MAN= ypldap.8 ypldap.conf.5
+
+LDFLAGS= -L/usr/local/lib
+LDADD= -levent -lldap
+DPADD= ${LIBEVENT}
+CFLAGS+= -Wall
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare -Wbounded
+CFLAGS+= -I/usr/local/include
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ypldap/buffer.c b/usr.sbin/ypldap/buffer.c
new file mode 100644
index 00000000000..7634ce6ac2f
--- /dev/null
+++ b/usr.sbin/ypldap/buffer.c
@@ -0,0 +1,214 @@
+/* $OpenBSD: buffer.c,v 1.1 2008/06/26 15:10:01 pyr Exp $ */
+
+/*
+ * 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 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/queue.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/tree.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+
+#include <event.h>
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ypldap.h"
+
+int buf_realloc(struct buf *, size_t);
+void buf_enqueue(struct msgbuf *, struct buf *);
+void buf_dequeue(struct msgbuf *, struct buf *);
+
+struct buf *
+buf_open(size_t len)
+{
+ struct buf *buf;
+
+ if ((buf = calloc(1, sizeof(struct buf))) == NULL)
+ return (NULL);
+ if ((buf->buf = malloc(len)) == NULL) {
+ free(buf);
+ return (NULL);
+ }
+ buf->size = buf->max = len;
+
+ return (buf);
+}
+
+struct buf *
+buf_dynamic(size_t len, size_t max)
+{
+ struct buf *buf;
+
+ if (max < len)
+ return (NULL);
+
+ if ((buf = buf_open(len)) == NULL)
+ return (NULL);
+
+ if (max > 0)
+ buf->max = max;
+
+ return (buf);
+}
+
+int
+buf_realloc(struct buf *buf, size_t len)
+{
+ u_char *b;
+
+ /* on static buffers max is eq size and so the following fails */
+ if (buf->wpos + len > buf->max) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ b = realloc(buf->buf, buf->wpos + len);
+ if (b == NULL)
+ return (-1);
+ buf->buf = b;
+ buf->size = buf->wpos + len;
+
+ return (0);
+}
+
+int
+buf_add(struct buf *buf, void *data, size_t len)
+{
+ if (buf->wpos + len > buf->size)
+ if (buf_realloc(buf, len) == -1)
+ return (-1);
+
+ memcpy(buf->buf + buf->wpos, data, len);
+ buf->wpos += len;
+ return (0);
+}
+
+void *
+buf_reserve(struct buf *buf, size_t len)
+{
+ void *b;
+
+ if (buf->wpos + len > buf->size)
+ if (buf_realloc(buf, len) == -1)
+ return (NULL);
+
+ b = buf->buf + buf->wpos;
+ buf->wpos += len;
+ return (b);
+}
+
+int
+buf_close(struct msgbuf *msgbuf, struct buf *buf)
+{
+ buf_enqueue(msgbuf, buf);
+ return (1);
+}
+
+void
+buf_free(struct buf *buf)
+{
+ free(buf->buf);
+ free(buf);
+}
+
+void
+msgbuf_init(struct msgbuf *msgbuf)
+{
+ msgbuf->queued = 0;
+ msgbuf->fd = -1;
+ TAILQ_INIT(&msgbuf->bufs);
+}
+
+void
+msgbuf_clear(struct msgbuf *msgbuf)
+{
+ struct buf *buf;
+
+ while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
+ buf_dequeue(msgbuf, buf);
+}
+
+int
+msgbuf_write(struct msgbuf *msgbuf)
+{
+ struct iovec iov[IOV_MAX];
+ struct buf *buf, *next;
+ unsigned int i = 0;
+ ssize_t n;
+ struct msghdr msg;
+
+ bzero(&iov, sizeof(iov));
+ bzero(&msg, sizeof(msg));
+ TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+ if (i >= IOV_MAX)
+ break;
+ iov[i].iov_base = buf->buf + buf->rpos;
+ iov[i].iov_len = buf->size - buf->rpos;
+ i++;
+ }
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = i;
+
+ if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
+ if (errno == EAGAIN || errno == ENOBUFS ||
+ errno == EINTR) /* try later */
+ return (0);
+ else
+ return (-1);
+ }
+
+ if (n == 0) { /* connection closed */
+ errno = 0;
+ return (-2);
+ }
+
+ for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
+ buf = next) {
+ next = TAILQ_NEXT(buf, entry);
+ if (buf->rpos + n >= buf->size) {
+ n -= buf->size - buf->rpos;
+ buf_dequeue(msgbuf, buf);
+ } else {
+ buf->rpos += n;
+ n = 0;
+ }
+ }
+
+ return (0);
+}
+
+void
+buf_enqueue(struct msgbuf *msgbuf, struct buf *buf)
+{
+ TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
+ msgbuf->queued++;
+}
+
+void
+buf_dequeue(struct msgbuf *msgbuf, struct buf *buf)
+{
+ TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
+ msgbuf->queued--;
+ buf_free(buf);
+}
diff --git a/usr.sbin/ypldap/entries.c b/usr.sbin/ypldap/entries.c
new file mode 100644
index 00000000000..662cfdacad7
--- /dev/null
+++ b/usr.sbin/ypldap/entries.c
@@ -0,0 +1,135 @@
+/* $OpenBSD: entries.c,v 1.1 2008/06/26 15:10:01 pyr Exp $ */
+/*
+ * 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 <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ypldap.h"
+
+void
+flatten_entries(struct env *env)
+{
+ size_t wrlen;
+ size_t len;
+ char *linep;
+ char *endp;
+ 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;
+ }
+ 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/imsg.c b/usr.sbin/ypldap/imsg.c
new file mode 100644
index 00000000000..d89e17b63fd
--- /dev/null
+++ b/usr.sbin/ypldap/imsg.c
@@ -0,0 +1,182 @@
+/* $OpenBSD: imsg.c,v 1.1 2008/06/26 15:10:01 pyr Exp $ */
+
+/*
+ * 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 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/param.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <event.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ypldap.h"
+
+void
+imsg_init(struct imsgbuf *ibuf, int fd, void (*handler)(int, short, void *))
+{
+ msgbuf_init(&ibuf->w);
+ bzero(&ibuf->r, sizeof(ibuf->r));
+ ibuf->fd = fd;
+ ibuf->w.fd = fd;
+ ibuf->pid = getpid();
+ ibuf->handler = handler;
+ TAILQ_INIT(&ibuf->fds);
+}
+
+ssize_t
+imsg_read(struct imsgbuf *ibuf)
+{
+ ssize_t n;
+
+ if ((n = recv(ibuf->fd, ibuf->r.buf + ibuf->r.wpos,
+ sizeof(ibuf->r.buf) - ibuf->r.wpos, 0)) == -1) {
+ if (errno != EINTR && errno != EAGAIN) {
+ log_warn("imsg_read: pipe read error");
+ return (-1);
+ }
+ return (-2);
+ }
+
+ ibuf->r.wpos += n;
+
+ return (n);
+}
+
+ssize_t
+imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
+{
+ size_t av, left, datalen;
+
+ av = ibuf->r.wpos;
+
+ if (IMSG_HEADER_SIZE > av)
+ return (0);
+
+ memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
+ if (imsg->hdr.len < IMSG_HEADER_SIZE ||
+ imsg->hdr.len > MAX_IMSGSIZE) {
+ log_warnx("imsg_get: imsg hdr len %u out of bounds, type=%u",
+ imsg->hdr.len, imsg->hdr.type);
+ return (-1);
+ }
+ if (imsg->hdr.len > av)
+ return (0);
+ datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
+ if ((imsg->data = malloc(datalen)) == NULL) {
+ log_warn("imsg_get");
+ return (-1);
+ }
+ memcpy(imsg->data, ibuf->r.rptr, datalen);
+
+ if (imsg->hdr.len < av) {
+ left = av - imsg->hdr.len;
+ memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
+ ibuf->r.wpos = left;
+ } else
+ ibuf->r.wpos = 0;
+
+ return (datalen + IMSG_HEADER_SIZE);
+}
+
+int
+imsg_compose(struct imsgbuf *ibuf, enum imsg_type type, u_int32_t peerid,
+ pid_t pid, void *data, u_int16_t datalen)
+{
+ struct buf *wbuf;
+ int n;
+
+ if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+ return (-1);
+
+ if (imsg_add(wbuf, data, datalen) == -1)
+ return (-1);
+
+ if ((n = imsg_close(ibuf, wbuf)) < 0)
+ return (-1);
+
+ return (n);
+}
+
+/* ARGSUSED */
+struct buf *
+imsg_create(struct imsgbuf *ibuf, enum imsg_type type, u_int32_t peerid,
+ pid_t pid, u_int16_t datalen)
+{
+ struct buf *wbuf;
+ struct imsg_hdr hdr;
+
+ if (datalen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
+ log_warnx("imsg_create: len %u > MAX_IMSGSIZE; "
+ "type %u peerid %lu", datalen + IMSG_HEADER_SIZE,
+ type, peerid);
+ return (NULL);
+ }
+
+ hdr.len = (u_int16_t)(datalen + IMSG_HEADER_SIZE);
+ hdr.type = type;
+ hdr.peerid = peerid;
+ if ((hdr.pid = pid) == 0)
+ hdr.pid = ibuf->pid;
+ if ((wbuf = buf_open(hdr.len)) == NULL) {
+ log_warn("imsg_create: buf_open");
+ return (NULL);
+ }
+ if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
+ return (NULL);
+
+ return (wbuf);
+}
+
+int
+imsg_add(struct buf *msg, void *data, u_int16_t datalen)
+{
+ if (datalen)
+ if (buf_add(msg, data, datalen) == -1) {
+ log_warnx("imsg_add: buf_add error");
+ buf_free(msg);
+ return (-1);
+ }
+ return (datalen);
+}
+
+int
+imsg_close(struct imsgbuf *ibuf, struct buf *msg)
+{
+ int n;
+
+ if ((n = buf_close(&ibuf->w, msg)) < 0) {
+ log_warnx("imsg_close: buf_close error");
+ buf_free(msg);
+ return (-1);
+ }
+ imsg_event_add(ibuf);
+
+ return (n);
+}
+
+void
+imsg_free(struct imsg *imsg)
+{
+ free(imsg->data);
+}
diff --git a/usr.sbin/ypldap/ldapclient.c b/usr.sbin/ypldap/ldapclient.c
new file mode 100644
index 00000000000..cc4b8bdd1d8
--- /dev/null
+++ b/usr.sbin/ypldap/ldapclient.c
@@ -0,0 +1,434 @@
+/* $OpenBSD: ldapclient.c,v 1.1 2008/06/26 15:10:01 pyr Exp $ */
+
+/*
+ * 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 <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LDAP_DEPRECATED 1
+#include <ldap.h>
+
+#include "ypldap.h"
+
+void client_sig_handler(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_configure_wrapper(int, short, void *);
+int client_try_idm(struct env *, struct idm *);
+void client_try_idm_wrapper(int, short, void *);
+void client_try_server_wrapper(int, short, void *);
+
+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_parent(int fd, short event, void *p)
+{
+ int n;
+ int shut = 0;
+ struct imsg imsg;
+ struct env *env = p;
+ struct imsgbuf *ibuf = env->sc_ibuf;
+
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0)
+ shut = 1;
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("client_dispatch_parent: imsg_read_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);
+ }
+ if (!shut)
+ imsg_event_add(ibuf);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&ibuf->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;
+ 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");
+
+#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");
+ lb_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_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_ibuf = calloc(1, sizeof(*env.sc_ibuf))) == NULL)
+ fatal(NULL);
+
+ env.sc_ibuf->events = EV_READ;
+ env.sc_ibuf->data = &env;
+ imsg_init(env.sc_ibuf, pipe_main2client[1], client_dispatch_parent);
+ event_set(&env.sc_ibuf->ev, env.sc_ibuf->fd, env.sc_ibuf->events,
+ env.sc_ibuf->handler, &env);
+ event_add(&env.sc_ibuf->ev, NULL);
+
+ event_dispatch();
+ client_shutdown();
+
+ return (0);
+
+}
+
+void
+client_configure_wrapper(int fd, short event, void *p)
+{
+ struct env *env = p;
+
+ client_configure(env);
+}
+
+int
+client_try_idm(struct env *env, struct idm *idm)
+{
+ int i;
+ int j;
+ int msgid;
+ size_t n;
+ LDAP *ld;
+ LDAPMessage *lm;
+ char **ldap_attrs;
+ char *attrs[ATTR_MAX+1];
+ const char *where;
+ struct idm_req ir;
+
+ log_debug("trying directory: %s", idm->idm_name);
+
+ bzero(&ir, sizeof(ir));
+ imsg_compose(env->sc_ibuf, IMSG_START_UPDATE, 0, 0, &ir, sizeof(ir));
+
+ where = "ldap_open";
+ if ((ld = ldap_open(idm->idm_name, LDAP_PORT)) == NULL)
+ goto bad;
+
+ where = "ldap_bind";
+ if (ldap_bind(ld, idm->idm_binddn,
+ idm->idm_bindcred, LDAP_AUTH_SIMPLE) < 0)
+ goto bad;
+
+ 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;
+
+ where = "ldap_search";
+ if ((msgid = ldap_search(ld, idm->idm_binddn, LDAP_SCOPE_SUBTREE,
+ idm->idm_filters[FILTER_USER], attrs, 0)) < 0)
+ goto bad;
+
+ where = "ldap_result";
+ if (ldap_result(ld, msgid, 1, NULL, &lm) == -1)
+ goto bad;
+
+ where = "ldap_result_message";
+ if (lm == NULL)
+ goto bad;
+ where = "ldap_first_message";
+ if ((lm = ldap_first_message(ld, lm)) == NULL)
+ goto bad;
+
+ /*
+ * build password line.
+ */
+ n = ldap_count_entries(ld, lm);
+ while (n-- > 0) {
+ bzero(&ir, sizeof(ir));
+ for (i = 0, j = 0; i < ATTR_MAX; 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.
+ */
+ goto next_entry;
+ if (i == ATTR_UID) {
+ ir.ir_key.ik_uid = strtonum(
+ idm->idm_attrs[i], 0,
+ UID_MAX, NULL);
+ }
+ } else {
+ ldap_attrs = (char **)ldap_get_values(ld,
+ lm, attrs[j++]);
+ if (ldap_attrs == NULL)
+ goto next_entry;
+ if (strlcat(ir.ir_line, ldap_attrs[0],
+ sizeof(ir.ir_line)) >= sizeof(ir.ir_line))
+ goto next_entry;
+ if (i == ATTR_UID) {
+ ir.ir_key.ik_uid = strtonum(
+ ldap_attrs[0], 0, UID_MAX, NULL);
+ }
+ ldap_value_free(ldap_attrs);
+ }
+ if (i != ATTR_SHELL)
+ if (strlcat(ir.ir_line, ":",
+ sizeof(ir.ir_line)) >= sizeof(ir.ir_line))
+ goto next_entry;
+
+ }
+ imsg_compose(env->sc_ibuf, IMSG_PW_ENTRY, 0, 0,
+ &ir, sizeof(ir));
+next_entry:
+ where = "ldap_next_message";
+ if ((lm = ldap_next_message(ld, lm)) == NULL)
+ goto bad;
+ }
+
+ /*
+ * exact same code but for groups.
+ */
+
+ 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;
+
+ where = "ldap_search";
+ if ((msgid = ldap_search(ld, idm->idm_binddn, LDAP_SCOPE_SUBTREE,
+ idm->idm_filters[FILTER_GROUP], attrs, 0)) < 0)
+ goto bad;
+
+ where = "ldap_result";
+ if (ldap_result(ld, msgid, 1, NULL, &lm) == -1)
+ goto bad;
+
+ where = "ldap_result_message";
+ if (lm == NULL)
+ goto bad;
+
+ where = "ldap_first_message";
+ if ((lm = ldap_first_message(ld, lm)) == NULL)
+ goto bad;
+
+ /*
+ * build group line.
+ */
+
+ n = ldap_count_entries(ld, lm);
+ while (n--) {
+ bzero(&ir, sizeof(ir));
+ for (i = ATTR_GR_MIN, j = 0; i < ATTR_GR_MAX; 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.
+ */
+ goto next_group_entry;
+ if (i == ATTR_GR_GID) {
+ ir.ir_key.ik_gid = strtonum(
+ idm->idm_attrs[i], 0,
+ GID_MAX, NULL);
+ }
+ } else {
+ ldap_attrs = (char **)ldap_get_values(ld,
+ lm, attrs[j++]);
+ if (ldap_attrs == NULL)
+ goto next_group_entry;
+ if (strlcat(ir.ir_line, ldap_attrs[0],
+ sizeof(ir.ir_line)) >= sizeof(ir.ir_line))
+ goto next_group_entry;
+ if (i == ATTR_GR_GID) {
+ ir.ir_key.ik_gid = strtonum(
+ ldap_attrs[0], 0, GID_MAX, NULL);
+ }
+ ldap_value_free(ldap_attrs);
+ }
+ if (i != ATTR_GR_MEMBERS)
+ if (strlcat(ir.ir_line, ":",
+ sizeof(ir.ir_line)) >= sizeof(ir.ir_line))
+ goto next_group_entry;
+
+ }
+ imsg_compose(env->sc_ibuf, IMSG_GRP_ENTRY, 0, 0,
+ &ir, sizeof(ir));
+next_group_entry:
+ where = "ldap_next_message";
+ if ((lm = ldap_next_message(ld, lm)) == NULL)
+ goto bad;
+ }
+
+ return (0);
+bad:
+ log_debug("directory %s errored out in %s", idm->idm_name, where);
+ return (-1);
+}
+
+void
+client_configure(struct env *env)
+{
+ enum imsg_type finish;
+ struct timeval tv;
+ struct idm *idm;
+
+ log_debug("connecting to directories");
+ finish = IMSG_END_UPDATE;
+ imsg_compose(env->sc_ibuf, IMSG_START_UPDATE, 0, 0, NULL, 0);
+ TAILQ_FOREACH(idm, &env->sc_idms, idm_entry)
+ if (client_try_idm(env, idm) == -1) {
+ finish = IMSG_TRASH_UPDATE;
+ break;
+ }
+ imsg_compose(env->sc_ibuf, finish, 0, 0, NULL, 0);
+ 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_configure_wrapper, 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 00000000000..5264552ac0c
--- /dev/null
+++ b/usr.sbin/ypldap/log.c
@@ -0,0 +1,160 @@
+/* $OpenBSD: log.c,v 1.1 2008/06/26 15:10:01 pyr Exp $ */
+
+/*
+ * 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 00000000000..290f71ea124
--- /dev/null
+++ b/usr.sbin/ypldap/parse.y
@@ -0,0 +1,792 @@
+/* $OpenBSD: parse.y,v 1.1 2008/06/26 15:10:01 pyr Exp $ */
+
+/*
+ * 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/param.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 <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 *, ...);
+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 BINDDN MAPS CHANGE DOMAIN PROVIDE
+%token USER GROUP TO EXPIRE HOME SHELL GECOS UID GID INTERVAL
+%token PASSWD NAME FIXED GROUPNAME GROUPPASSWD GROUPGID MAP
+%token INCLUDE DIRECTORY CLASS PORT SSL ERROR GROUPMEMBERS
+%token <v.string> STRING
+%token <v.number> NUMBER
+%type <v.number> opcode attribute port ssl
+
+%%
+
+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);
+ }
+ ;
+
+ssl : SSL { $$ = 1; }
+ | /*empty*/ { $$ = 0; }
+ ;
+
+port : PORT NUMBER { $$ = $2; }
+ | /*empty*/ { $$ = 0; }
+ ;
+
+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);
+ }
+ | 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);
+ }
+ ;
+
+directory : DIRECTORY STRING port ssl {
+ const char *service;
+ struct servent *srv;
+
+ if ((idm = calloc(1, sizeof(*idm))) == NULL)
+ fatal(NULL);
+
+ if (strlcpy(idm->idm_name, $2, sizeof(idm->idm_name)) >=
+ sizeof(idm->idm_name)) {
+ yyerror("directory name truncated");
+ free(idm);
+ free($2);
+ YYERROR;
+ }
+
+ if ($4) {
+ service = "ldaps";
+ } else
+ service = "ldap";
+ srv = getservbyname(service, "tcp");
+
+ if ($3)
+ idm->idm_port = $3;
+ else if (srv == NULL) {
+ if ($4)
+ idm->idm_port = 389;
+ else
+ idm->idm_port = 686;
+ } else
+ idm->idm_port = ntohs(srv->s_port);
+ 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 {
+ 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;
+
+ file->errors++;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d: ", file->name, yylval.lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ 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 },
+ { "binddn", BINDDN },
+ { "change", CHANGE },
+ { "class", CLASS },
+ { "directory", DIRECTORY },
+ { "domain", DOMAIN },
+ { "expire", EXPIRE },
+ { "filter", FILTER },
+ { "fixed", FIXED },
+ { "gecos", GECOS },
+ { "gid", GID },
+ { "group", GROUP },
+ { "groupgid", GROUPGID },
+ { "groupmembers", GROUPMEMBERS },
+ { "groupname", GROUPNAME },
+ { "grouppasswd", GROUPPASSWD },
+ { "home", HOME },
+ { "include", INCLUDE },
+ { "interval", INTERVAL },
+ { "map", MAP },
+ { "maps", MAPS },
+ { "name", NAME },
+ { "passwd", PASSWD },
+ { "port", PORT },
+ { "provide", PROVIDE },
+ { "server", SERVER },
+ { "shell", SHELL },
+ { "ssl", SSL },
+ { "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
+
+char *parsebuf;
+int parseindex;
+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;
+ pushback_index = 0;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ char buf[8096];
+ 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++ = (char)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')
+ continue;
+ else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = (char)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_IRWXG | S_IRWXO)) {
+ log_warnx("%s: group/world readable/writeable", fname);
+ return (-1);
+ }
+ return (0);
+}
+
+struct file *
+pushfile(const char *name, int secret)
+{
+ struct file *nfile;
+
+ if ((nfile = calloc(1, sizeof(struct file))) == NULL ||
+ (nfile->name = strdup(name)) == NULL) {
+ log_warn("malloc");
+ 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, 0)) == 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 00000000000..c882f80625f
--- /dev/null
+++ b/usr.sbin/ypldap/yp.c
@@ -0,0 +1,614 @@
+/* $OpenBSD: yp.c,v 1.1 2008/06/26 15:10:01 pyr Exp $ */
+/*
+ * 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/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 <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 *);
+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);
+ }
+ }
+}
+
+void
+yp_fd_event(int fd, short event, void *p)
+{
+ fd_set fdset;
+
+ FD_ZERO(&fdset);
+ FD_SET(fd, &fdset);
+ svc_getreqset(&fdset);
+ 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);
+ break;
+ 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 key[_PW_NAME_LEN+1];
+
+ 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 (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) {
+ res.stat = YP_NOKEY;
+ return (&res);
+ }
+
+ yp_make_val(&res, ue->ue_line);
+ return (&res);
+ } else if (strcmp(arg->map, "passwd.byuid") == 0) {
+ bzero(key, sizeof(key));
+ (void)strncpy(key, arg->key.keydat_val,
+ arg->key.keydat_len);
+ 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);
+ return (&res);
+ } else if (strcmp(arg->map, "group.bygid") == 0) {
+ bzero(key, sizeof(key));
+ (void)strncpy(key, arg->key.keydat_val,
+ arg->key.keydat_len);
+ 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);
+ 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) {
+ res.stat = YP_NOKEY;
+ return (&res);
+ }
+
+ yp_make_val(&res, ge->ge_line);
+ 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);
+ return (&res);
+ } 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);
+ return (&res);
+ } else {
+ log_debug("unknown map %s", arg->map);
+ res.stat = YP_NOMAP;
+ return (&res);
+ }
+ return (NULL);
+}
+
+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[_PW_NAME_LEN+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
+ * 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[6] = {
+ { "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 },
+ };
+ 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)
+{
+ static char buf[LINE_WIDTH];
+
+ bzero(buf, sizeof(buf));
+
+ line[strlen(line)] = ':';
+ (void)strlcpy(buf, line, sizeof(buf));
+ 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[_PW_NAME_LEN+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 00000000000..841c8aa30a4
--- /dev/null
+++ b/usr.sbin/ypldap/ypldap.8
@@ -0,0 +1,82 @@
+.\" $OpenBSD: ypldap.8,v 1.1 2008/06/26 15:10:01 pyr Exp $
+.\"
+.\" 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: June 26 2008 $
+.Dt YPLDAP 8
+.Os
+.Sh NAME
+.Nm ypldap
+.Nd "YP map server using LDAP backend"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dnv
+.Oo Xo
+.Fl D Ar macro Ns = Ns Ar value Oc
+.Xc
+.Op Fl f Ar file
+.Sh DESCRIPTION
+.Nm
+is a daemon providing YP maps using LDAP as a backend.
+Arbitrary LDAP schemas can be tied to the differet YP maps.
+.Nm
+has the same role than
+.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 /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 Pierre-Yves Ritschard.
diff --git a/usr.sbin/ypldap/ypldap.c b/usr.sbin/ypldap/ypldap.c
new file mode 100644
index 00000000000..7254e01ca41
--- /dev/null
+++ b/usr.sbin/ypldap/ypldap.c
@@ -0,0 +1,504 @@
+/* $OpenBSD: ypldap.c,v 1.1 2008/06/26 15:10:01 pyr Exp $ */
+
+/*
+ * 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 <sys/wait.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.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 "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 purge_config(struct env *);
+void reconfigure(struct env *);
+void set_nonblock(int);
+
+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)
+{
+ (void)unlink(_PATH_LDAPBIND_SOCK);
+ _exit(0);
+}
+
+void
+main_dispatch_client(int fd, short event, void *p)
+{
+ int n;
+ int shut = 0;
+ struct env *env = p;
+ struct imsgbuf *ibuf = env->sc_ibuf;
+ struct idm_req ir;
+ struct imsg imsg;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0)
+ shut = 1;
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("main_dispatch_client: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_START_UPDATE:
+ 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);
+ break;
+ case IMSG_PW_ENTRY: {
+ struct userent *ue;
+ size_t len;
+
+ (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_FIND(user_name_tree, env->sc_user_names_t,
+ ue) != NULL) { /* dup */
+ free(ue->ue_line);
+ free(ue);
+ break;
+ }
+ RB_INSERT(user_name_tree, env->sc_user_names_t, ue);
+ env->sc_user_line_len += len;
+ break;
+ }
+ case IMSG_GRP_ENTRY: {
+ struct groupent *ge;
+ size_t len;
+
+ (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_FIND(group_name_tree, env->sc_group_names_t,
+ ge) != NULL) { /* dup */
+ free(ge->ge_line);
+ free(ge);
+ break;
+ }
+ RB_INSERT(group_name_tree, env->sc_group_names_t, ge);
+ env->sc_group_line_len += len;
+ break;
+ }
+ case IMSG_TRASH_UPDATE: {
+ struct userent *ue;
+ struct groupent *ge;
+
+ 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);
+ }
+ 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;
+ break;
+ }
+ case IMSG_END_UPDATE: {
+ struct userent *ue;
+ struct groupent *ge;
+
+ log_debug("updates are over, cleaning up trees now");
+
+ 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);
+ }
+ 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 and gid 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);
+ break;
+ }
+ default:
+ log_debug("main_dispatch_client: unexpected imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(ibuf);
+ else {
+ log_debug("king bula sez: ran into dead pipe");
+ event_del(&ibuf->ev);
+ event_loopexit(NULL);
+ }
+}
+
+void
+main_configure_client(struct env *env)
+{
+ struct idm *idm;
+ struct imsgbuf *ibuf = env->sc_ibuf;
+
+ imsg_compose(ibuf, IMSG_CONF_START, 0, 0, env, sizeof(*env));
+ TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
+ imsg_compose(ibuf, IMSG_CONF_IDM, 0, 0, idm, sizeof(*idm));
+ }
+ imsg_compose(ibuf, IMSG_CONF_END, 0, 0, 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;
+ lb_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;
+ 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, PF_UNSPEC,
+ pipe_main2client) == -1)
+ fatal("socketpair");
+
+ set_nonblock(pipe_main2client[0]);
+ set_nonblock(pipe_main2client[1]);
+
+ 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_ibuf = calloc(1, sizeof(*env.sc_ibuf))) == NULL)
+ fatal(NULL);
+ imsg_init(env.sc_ibuf, pipe_main2client[0], main_dispatch_client);
+
+ env.sc_ibuf->events = EV_READ;
+ env.sc_ibuf->data = &env;
+ event_set(&env.sc_ibuf->ev, env.sc_ibuf->fd, env.sc_ibuf->events,
+ env.sc_ibuf->handler, &env);
+ event_add(&env.sc_ibuf->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 imsgbuf *ibuf)
+{
+ struct env *env = ibuf->data;
+
+ ibuf->events = EV_READ;
+ if (ibuf->w.queued)
+ ibuf->events |= EV_WRITE;
+
+ event_del(&ibuf->ev);
+ event_set(&ibuf->ev, ibuf->fd, ibuf->events, ibuf->handler, env);
+ event_add(&ibuf->ev, NULL);
+}
+
+void
+set_nonblock(int fd)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ fatal("fcntl F_GETFL");
+
+ flags |= O_NONBLOCK;
+
+ if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
+ fatal("fcntl F_SETFL");
+}
diff --git a/usr.sbin/ypldap/ypldap.conf.5 b/usr.sbin/ypldap/ypldap.conf.5
new file mode 100644
index 00000000000..a7b59e09487
--- /dev/null
+++ b/usr.sbin/ypldap/ypldap.conf.5
@@ -0,0 +1,93 @@
+.\" $OpenBSD: ypldap.conf.5,v 1.1 2008/06/26 15:10:01 pyr Exp $
+.\"
+.\" 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: June 26 2008 $
+.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, and may contain letters, digits,
+and underscores.
+Macro names may not be reserved words (for example,
+.Ic cost ) .
+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 concerns 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.byid,
+group.byname, group.byid.
+.El
+.Sh DIRECTORIES
+.Sh FILES
+.Bl -tag -width "/etc/ypldap.conf" -compact
+.It Pa /etc/ypldap.conf
+.Xr ypldap 8
+configuration file
+.El
+.Sh SEE ALSO
+.Xr ypldap 8 ,
+.Xr ypserv 8 ,
+.Xr ypbind 8
+.Rs
+.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 00000000000..ce772def31b
--- /dev/null
+++ b/usr.sbin/ypldap/ypldap.h
@@ -0,0 +1,281 @@
+/*
+ * 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.
+ */
+
+#define YPLDAP_USER "_ospfd"
+#define YPLDAP_CONF_FILE "/etc/ypldap.conf"
+#define DEFAULT_INTERVAL 600
+#define _PATH_LDAPBIND_SOCK "/var/run/ypldap.sock"
+#define LINE_WIDTH 1024
+#define FILTER_WIDTH 128
+#define ATTR_WIDTH 32
+
+/* buffer */
+struct buf {
+ TAILQ_ENTRY(buf) entry;
+ u_char *buf;
+ size_t size;
+ size_t max;
+ size_t wpos;
+ size_t rpos;
+ int fd;
+};
+
+struct msgbuf {
+ TAILQ_HEAD(, buf) bufs;
+ u_int32_t queued;
+ int fd;
+};
+
+#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
+#define MAX_IMSGSIZE 8192
+
+struct buf_read {
+ u_char buf[MAX_IMSGSIZE];
+ u_char *rptr;
+ size_t wpos;
+};
+
+struct imsg_fd {
+ TAILQ_ENTRY(imsg_fd) entry;
+ int fd;
+};
+
+struct imsgbuf {
+ TAILQ_HEAD(, imsg_fd) fds;
+ struct buf_read r;
+ struct msgbuf w;
+ struct event ev;
+ void (*handler)(int, short, void *);
+ int fd;
+ pid_t pid;
+ short events;
+ void *data;
+};
+
+enum imsg_type {
+ IMSG_NONE = 0,
+ IMSG_GETPWENT = 1, /* sends nothing expects nothing */
+ IMSG_SETPWENT = 2, /* sends nothing expects nothing */
+ IMSG_GETPWNAM = 3, /* sends a name expects a line */
+ IMSG_GETPWID = 4, /* sends a uid_t expects a line */
+ IMSG_GETGRENT = 5, /* sends nothing expects nothing */
+ IMSG_SETGRENT = 6, /* sends nothing expects nothing */
+ IMSG_GETGRNAM = 7, /* sends a name expects a line */
+ IMSG_GETGRID = 8, /* sends a uid_t expects a line */
+ IMSG_CONF_START,
+ IMSG_CONF_IDM,
+ IMSG_CONF_SERVER,
+ IMSG_CONF_END,
+ IMSG_START_UPDATE,
+ IMSG_END_UPDATE,
+ IMSG_TRASH_UPDATE,
+ IMSG_PW_ENTRY,
+ IMSG_GRP_ENTRY
+};
+
+struct imsg_hdr {
+ u_int16_t type;
+ u_int16_t len;
+ u_int32_t peerid;
+ pid_t pid;
+};
+
+struct imsg {
+ struct imsg_hdr hdr;
+ void *data;
+};
+
+enum blockmodes {
+ BM_NORMAL,
+ BM_NONBLOCK,
+};
+
+enum {
+ PROC_MAIN,
+ PROC_CLIENT
+} lb_process;
+
+union req {
+ uid_t uid;
+ gid_t gid;
+ char nam[_PW_NAME_LEN+1];
+};
+
+struct userent {
+ RB_ENTRY(userent) ue_name_node;
+ RB_ENTRY(userent) ue_uid_node;
+ uid_t ue_uid;
+ char *ue_line;
+};
+
+struct groupent {
+ RB_ENTRY(groupent) ge_name_node;
+ RB_ENTRY(groupent) ge_gid_node;
+ gid_t ge_gid;
+ char *ge_line;
+};
+
+/*
+ * beck, djm, dlg: pay attention to the struct name
+ */
+struct idm {
+ TAILQ_ENTRY(idm) idm_entry;
+ char idm_name[MAXHOSTNAMELEN];
+#define F_SSL 0x00100000
+#define F_CONFIGURING 0x00200000
+#define F_NEEDAUTH 0x00400000
+#define F_FIXED_ATTR(n) (1<<n)
+ u_int32_t idm_flags; /* lower 20 reserved */
+ in_port_t idm_port;
+ char idm_binddn[LINE_WIDTH];
+ char idm_bindcred[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 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
+ u_int32_t sc_flags;
+
+ char sc_domainname[MAXHOSTNAMELEN];
+ struct timeval sc_conf_tv;
+ struct event sc_conf_ev;
+ TAILQ_HEAD(idm_list, idm) sc_idms;
+ struct imsgbuf *sc_ibuf;
+
+ 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;
+};
+
+/* buffer.c */
+struct buf *buf_open(size_t);
+struct buf *buf_dynamic(size_t, size_t);
+int buf_add(struct buf *, void *, size_t);
+void *buf_reserve(struct buf *, size_t);
+int buf_close(struct msgbuf *, struct buf *);
+void buf_free(struct buf *);
+void msgbuf_init(struct msgbuf *);
+void msgbuf_clear(struct msgbuf *);
+int msgbuf_write(struct msgbuf *);
+
+/* imsg.c */
+void imsg_init(struct imsgbuf *, int, void (*)(int, short, void *));
+ssize_t imsg_read(struct imsgbuf *);
+ssize_t imsg_get(struct imsgbuf *, struct imsg *);
+int imsg_compose(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t,
+ void *, u_int16_t);
+#if 0
+int imsg_get_fd(struct imsgbuf *);
+int imsg_composev(struct imsgbuf *, enum imsg_type, u_int32_t,
+ pid_t, const struct iovec *, int);
+int imsg_flush(struct imsgbuf *);
+#endif
+struct buf *imsg_create(struct imsgbuf *, enum imsg_type, u_int32_t, pid_t,
+ u_int16_t);
+int imsg_add(struct buf *, void *, u_int16_t);
+int imsg_close(struct imsgbuf *, struct buf *);
+void imsg_free(struct imsg *);
+void imsg_event_add(struct imsgbuf *); /* needs to be provided externally */
+void imsg_clear(struct imsgbuf *);
+
+/* 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 *, ...);
+__dead void fatal(const char *);
+__dead void fatalx(const char *);
+
+/* parse.y */
+int parse_config(struct env *, const char *, int);
+int cmdline_symset(char *);
+
+/* listener.c */
+void listener_setup(struct env *);
+void listener_init(struct env *);
+
+/* ldapclient.c */
+pid_t ldapclient(int []);
+
+/* ypldap.c */
+void purge_config(struct env *);
+
+/* 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);