diff options
-rw-r--r-- | usr.sbin/ypldap/Makefile | 18 | ||||
-rw-r--r-- | usr.sbin/ypldap/buffer.c | 214 | ||||
-rw-r--r-- | usr.sbin/ypldap/entries.c | 135 | ||||
-rw-r--r-- | usr.sbin/ypldap/imsg.c | 182 | ||||
-rw-r--r-- | usr.sbin/ypldap/ldapclient.c | 434 | ||||
-rw-r--r-- | usr.sbin/ypldap/log.c | 160 | ||||
-rw-r--r-- | usr.sbin/ypldap/parse.y | 792 | ||||
-rw-r--r-- | usr.sbin/ypldap/yp.c | 614 | ||||
-rw-r--r-- | usr.sbin/ypldap/ypldap.8 | 82 | ||||
-rw-r--r-- | usr.sbin/ypldap/ypldap.c | 504 | ||||
-rw-r--r-- | usr.sbin/ypldap/ypldap.conf.5 | 93 | ||||
-rw-r--r-- | usr.sbin/ypldap/ypldap.h | 281 |
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(¶ms, imsg.data, sizeof(params)); + log_debug("configuration starting"); + env->sc_flags |= F_CONFIGURING; + purge_config(env); + memcpy(&env->sc_conf_tv, ¶ms.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); |