diff options
author | eric <eric@openbsd.org> | 2018-04-27 16:14:35 +0000 |
---|---|---|
committer | eric <eric@openbsd.org> | 2018-04-27 16:14:35 +0000 |
commit | 3b188dab47094e37e38dcd2dcd35528ab7a95e4d (patch) | |
tree | 14225ea843a4fc0f15f460089bded21a34d912fe /usr.sbin/lpd/frontend.c | |
parent | use nitems() (diff) | |
download | wireguard-openbsd-3b188dab47094e37e38dcd2dcd35528ab7a95e4d.tar.xz wireguard-openbsd-3b188dab47094e37e38dcd2dcd35528ab7a95e4d.zip |
Import lpd, a re-implementation of the lpr daemon following the latest
OpenBSD coding practices (fork+exec/privsep/pledge/...). It is only
intended to replace the lpd(8) daemon for the moment, not the lpr(1),
lprm(1), lpq(1) and lpc(8) commands.
This is a work in progress. The server part should be fairly functionnal,
but the printer part is not complete: remote printers should work, for
local printers it depends on the setup. Anyway, at this point it's better
in the tree than rotting on my disk.
ok deraadt@
Diffstat (limited to 'usr.sbin/lpd/frontend.c')
-rw-r--r-- | usr.sbin/lpd/frontend.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/usr.sbin/lpd/frontend.c b/usr.sbin/lpd/frontend.c new file mode 100644 index 00000000000..9ea42183c49 --- /dev/null +++ b/usr.sbin/lpd/frontend.c @@ -0,0 +1,335 @@ +/* $OpenBSD: frontend.c,v 1.1.1.1 2018/04/27 16:14:35 eric Exp $ */ + +/* + * Copyright (c) 2017 Eric Faurot <eric@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/tree.h> + +#include <errno.h> +#include <paths.h> +#include <pwd.h> +#include <signal.h> +#include <stdlib.h> +#include <syslog.h> +#include <unistd.h> + +#include "lpd.h" + +#include "log.h" +#include "proc.h" + +static void frontend_shutdown(void); +static void frontend_listen(struct listener *); +static void frontend_pause(struct listener *); +static void frontend_resume(struct listener *); +static void frontend_accept(int, short, void *); +static void frontend_dispatch_priv(struct imsgproc *, struct imsg *, void *); +static void frontend_dispatch_engine(struct imsgproc *, struct imsg *, void *); + +struct conn { + SPLAY_ENTRY(conn) entry; + struct listener *listener; + uint32_t id; +}; + +static int conn_cmp(struct conn *, struct conn *); + +SPLAY_HEAD(conntree, conn); +SPLAY_PROTOTYPE(conntree, conn, entry, conn_cmp); + +static struct conntree conns; +static struct lpd_conf *tmpconf; + +static int +conn_cmp(struct conn *a, struct conn *b) +{ + if (a->id < b->id) + return (-1); + if (a->id > b->id) + return (1); + return (0); +} + +SPLAY_GENERATE(conntree, conn, entry, conn_cmp); + +void +frontend(int debug, int verbose) +{ + struct passwd *pw; + + /* Early initialisation. */ + log_init(debug, LOG_LPR); + log_setverbose(verbose); + log_procinit("frontend"); + setproctitle("frontend"); + + SPLAY_INIT(&conns); + lpr_init(); + + /* Drop priviledges. */ + if ((pw = getpwnam(LPD_USER)) == NULL) + fatal("%s: getpwnam: %s", __func__, LPD_USER); + + if (chroot(_PATH_VAREMPTY) == -1) + fatal("%s: chroot", __func__); + if (chdir("/") == -1) + fatal("%s: chdir", __func__); + + 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("%s: cannot drop privileges", __func__); + + if (pledge("stdio unix inet recvfd sendfd", NULL) == -1) + fatal("%s: pledge", __func__); + + event_init(); + + signal(SIGPIPE, SIG_IGN); + + /* Setup parent imsg socket. */ + p_priv = proc_attach(PROC_PRIV, 3); + if (p_priv == NULL) + fatal("%s: proc_attach", __func__); + proc_setcallback(p_priv, frontend_dispatch_priv, NULL); + proc_enable(p_priv); + + event_dispatch(); + + frontend_shutdown(); +} + +void +frontend_conn_closed(uint32_t connid) +{ + struct listener *l; + struct conn key, *conn; + + key.id = connid; + conn = SPLAY_FIND(conntree, &conns, &key); + if (conn == NULL) + fatalx("%s: %08x unknown connid", __func__, connid); + + l = conn->listener; + + if (log_getverbose() > LOGLEVEL_CONN) + log_debug("%08x close %s", conn->id, + log_fmt_proto(l->proto)); + + SPLAY_REMOVE(conntree, &conns, conn); + free(conn); + + if (l->pause) + frontend_resume(l); +} + +static void +frontend_shutdown() +{ + struct listener *l; + + TAILQ_FOREACH(l, &env->listeners, entry) + close(l->sock); + + log_debug("exiting"); + + exit(0); +} + +static void +frontend_listen(struct listener *l) +{ + if (log_getverbose() > LOGLEVEL_CONN) + log_debug("listen %s %s", log_fmt_proto(l->proto), + log_fmt_sockaddr((struct sockaddr*)&l->ss)); + + if (listen(l->sock, 5) == -1) + fatal("%s: listen", __func__); + + frontend_resume(l); +} + +static void +frontend_pause(struct listener *l) +{ + struct timeval tv; + + event_del(&l->ev); + + tv.tv_sec = 2; + tv.tv_usec = 0; + + evtimer_set(&l->ev, frontend_accept, l); + evtimer_add(&l->ev, &tv); + l->pause = 1; +} + +static void +frontend_resume(struct listener *l) +{ + if (l->pause) { + evtimer_del(&l->ev); + l->pause = 0; + } + event_set(&l->ev, l->sock, EV_READ | EV_PERSIST, frontend_accept, l); + event_add(&l->ev, NULL); +} + +static void +frontend_accept(int sock, short ev, void *arg) +{ + struct listener *l = arg; + struct sockaddr_storage ss; + struct sockaddr *sa; + struct conn *conn; + socklen_t len; + + if (l->pause) { + l->pause = 0; + frontend_resume(l); + return; + } + + conn = calloc(1, sizeof(*conn)); + if (conn == NULL) + log_warn("%s: calloc", __func__); + + sa = (struct sockaddr *)&ss; + len = sizeof(ss); + sock = accept4(sock, sa, &len, SOCK_NONBLOCK); + if (sock == -1) { + if (errno == ENFILE || errno == EMFILE) + frontend_pause(l); + else if (errno != EWOULDBLOCK && errno != EINTR && + errno != ECONNABORTED) + log_warn("%s: accept4", __func__); + free(conn); + return; + } + + if (conn == NULL) { + close(sock); + return; + } + + while (conn->id == 0 || SPLAY_FIND(conntree, &conns, conn)) + conn->id = arc4random(); + SPLAY_INSERT(conntree, &conns, conn); + conn->listener = l; + + if (log_getverbose() > LOGLEVEL_CONN) + log_debug("%08x accept %s %s", conn->id, + log_fmt_proto(conn->listener->proto), + log_fmt_sockaddr((struct sockaddr*)&ss)); + + switch (l->proto) { + case PROTO_LPR: + lpr_conn(conn->id, l, sock, sa); + break; + default: + fatalx("%s: unexpected protocol %d", __func__, l->proto); + } +} + +static void +frontend_dispatch_priv(struct imsgproc *proc, struct imsg *imsg, void *arg) +{ + struct listener *l; + + if (imsg == NULL) { + log_debug("%s: imsg connection lost", __func__); + event_loopexit(NULL); + return; + } + + if (log_getverbose() > LOGLEVEL_IMSG) + log_imsg(proc, imsg); + + switch (imsg->hdr.type) { + case IMSG_SOCK_ENGINE: + if (imsg->fd == -1) + fatalx("%s: engine socket not received", __func__); + m_end(proc); + p_engine = proc_attach(PROC_ENGINE, imsg->fd); + proc_setcallback(p_engine, frontend_dispatch_engine, NULL); + proc_enable(p_engine); + break; + + case IMSG_CONF_START: + m_end(proc); + if ((tmpconf = calloc(1, sizeof(*tmpconf))) == NULL) + fatal("%s: calloc", __func__); + TAILQ_INIT(&tmpconf->listeners); + break; + + case IMSG_CONF_LISTENER: + if (imsg->fd == -1) + fatalx("%s: listener socket not received", __func__); + if ((l = calloc(1, sizeof(*l))) == NULL) + fatal("%s: calloc", __func__); + m_get_int(proc, &l->proto); + m_get_sockaddr(proc, (struct sockaddr *)&l->ss); + m_end(proc); + l->sock = imsg->fd; + TAILQ_INSERT_TAIL(&tmpconf->listeners, l, entry); + break; + + case IMSG_CONF_END: + m_end(proc); + TAILQ_FOREACH(l, &tmpconf->listeners, entry) + frontend_listen(l); + env = tmpconf; + break; + + default: + fatalx("%s: unexpected imsg %s", __func__, + log_fmt_imsgtype(imsg->hdr.type)); + } +} + +static void +frontend_dispatch_engine(struct imsgproc *proc, struct imsg *imsg, void *arg) +{ + if (imsg == NULL) { + log_debug("%s: imsg connection lost", __func__); + event_loopexit(NULL); + return; + } + + if (log_getverbose() > LOGLEVEL_IMSG) + log_imsg(proc, imsg); + + switch (imsg->hdr.type) { + case IMSG_RES_GETADDRINFO: + case IMSG_RES_GETADDRINFO_END: + case IMSG_RES_GETNAMEINFO: + resolver_dispatch_result(proc, imsg); + break; + + case IMSG_LPR_ALLOWEDHOST: + case IMSG_LPR_DISPLAYQ: + case IMSG_LPR_RECVJOB: + case IMSG_LPR_RECVJOB_CF: + case IMSG_LPR_RECVJOB_DF: + case IMSG_LPR_RMJOB: + lpr_dispatch_engine(proc, imsg); + break; + + default: + fatalx("%s: unexpected imsg %s", __func__, + log_fmt_imsgtype(imsg->hdr.type)); + } +} |