1 /* $OpenBSD: frontend.c,v 1.2 2018/09/05 17:32:56 eric Exp $ */ 2 3 /* 4 * Copyright (c) 2017 Eric Faurot <eric@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/tree.h> 20 21 #include <errno.h> 22 #include <paths.h> 23 #include <pwd.h> 24 #include <signal.h> 25 #include <stdlib.h> 26 #include <syslog.h> 27 #include <unistd.h> 28 29 #include "lpd.h" 30 31 #include "log.h" 32 #include "proc.h" 33 34 static void frontend_shutdown(void); 35 static void frontend_listen(struct listener *); 36 static void frontend_pause(struct listener *); 37 static void frontend_resume(struct listener *); 38 static void frontend_accept(int, short, void *); 39 static void frontend_dispatch_priv(struct imsgproc *, struct imsg *, void *); 40 static void frontend_dispatch_engine(struct imsgproc *, struct imsg *, void *); 41 42 struct conn { 43 SPLAY_ENTRY(conn) entry; 44 struct listener *listener; 45 uint32_t id; 46 }; 47 48 static int conn_cmp(struct conn *, struct conn *); 49 50 SPLAY_HEAD(conntree, conn); 51 SPLAY_PROTOTYPE(conntree, conn, entry, conn_cmp); 52 53 static struct conntree conns; 54 static struct lpd_conf *tmpconf; 55 56 static int 57 conn_cmp(struct conn *a, struct conn *b) 58 { 59 if (a->id < b->id) 60 return (-1); 61 if (a->id > b->id) 62 return (1); 63 return (0); 64 } 65 66 SPLAY_GENERATE(conntree, conn, entry, conn_cmp); 67 68 void 69 frontend(int debug, int verbose) 70 { 71 struct passwd *pw; 72 73 /* Early initialisation. */ 74 log_init(debug, LOG_LPR); 75 log_setverbose(verbose); 76 log_procinit("frontend"); 77 setproctitle("frontend"); 78 79 SPLAY_INIT(&conns); 80 lpr_init(); 81 82 /* Drop priviledges. */ 83 if ((pw = getpwnam(LPD_USER)) == NULL) 84 fatal("%s: getpwnam: %s", __func__, LPD_USER); 85 86 if (chroot(_PATH_VAREMPTY) == -1) 87 fatal("%s: chroot", __func__); 88 if (chdir("/") == -1) 89 fatal("%s: chdir", __func__); 90 91 if (setgroups(1, &pw->pw_gid) || 92 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 93 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 94 fatal("%s: cannot drop privileges", __func__); 95 96 if (pledge("stdio unix inet recvfd sendfd", NULL) == -1) 97 fatal("%s: pledge", __func__); 98 99 event_init(); 100 101 signal(SIGPIPE, SIG_IGN); 102 103 /* Setup parent imsg socket. */ 104 p_priv = proc_attach(PROC_PRIV, 3); 105 if (p_priv == NULL) 106 fatal("%s: proc_attach", __func__); 107 proc_setcallback(p_priv, frontend_dispatch_priv, NULL); 108 proc_enable(p_priv); 109 110 event_dispatch(); 111 112 frontend_shutdown(); 113 } 114 115 void 116 frontend_conn_closed(uint32_t connid) 117 { 118 struct listener *l; 119 struct conn key, *conn; 120 121 key.id = connid; 122 conn = SPLAY_FIND(conntree, &conns, &key); 123 if (conn == NULL) 124 fatalx("%s: %08x unknown connid", __func__, connid); 125 126 l = conn->listener; 127 128 if (log_getverbose() > LOGLEVEL_CONN) 129 log_debug("%08x close %s", conn->id, 130 log_fmt_proto(l->proto)); 131 132 SPLAY_REMOVE(conntree, &conns, conn); 133 free(conn); 134 135 if (l->pause) 136 frontend_resume(l); 137 } 138 139 static void 140 frontend_shutdown() 141 { 142 struct listener *l; 143 144 TAILQ_FOREACH(l, &env->listeners, entry) 145 close(l->sock); 146 147 log_debug("exiting"); 148 149 exit(0); 150 } 151 152 static void 153 frontend_listen(struct listener *l) 154 { 155 if (log_getverbose() > LOGLEVEL_CONN) 156 log_debug("listen %s %s", log_fmt_proto(l->proto), 157 log_fmt_sockaddr((struct sockaddr*)&l->ss)); 158 159 if (listen(l->sock, 5) == -1) 160 fatal("%s: listen", __func__); 161 162 frontend_resume(l); 163 } 164 165 static void 166 frontend_pause(struct listener *l) 167 { 168 struct timeval tv; 169 170 event_del(&l->ev); 171 172 tv.tv_sec = 2; 173 tv.tv_usec = 0; 174 175 evtimer_set(&l->ev, frontend_accept, l); 176 evtimer_add(&l->ev, &tv); 177 l->pause = 1; 178 } 179 180 static void 181 frontend_resume(struct listener *l) 182 { 183 if (l->pause) { 184 evtimer_del(&l->ev); 185 l->pause = 0; 186 } 187 event_set(&l->ev, l->sock, EV_READ | EV_PERSIST, frontend_accept, l); 188 event_add(&l->ev, NULL); 189 } 190 191 static void 192 frontend_accept(int sock, short ev, void *arg) 193 { 194 struct listener *l = arg; 195 struct sockaddr_storage ss; 196 struct sockaddr *sa; 197 struct conn *conn; 198 socklen_t len; 199 200 if (l->pause) { 201 l->pause = 0; 202 frontend_resume(l); 203 return; 204 } 205 206 conn = calloc(1, sizeof(*conn)); 207 if (conn == NULL) 208 log_warn("%s: calloc", __func__); 209 210 sa = (struct sockaddr *)&ss; 211 len = sizeof(ss); 212 sock = accept4(sock, sa, &len, SOCK_NONBLOCK); 213 if (sock == -1) { 214 if (errno == ENFILE || errno == EMFILE) 215 frontend_pause(l); 216 else if (errno != EWOULDBLOCK && errno != EINTR && 217 errno != ECONNABORTED) 218 log_warn("%s: accept4", __func__); 219 free(conn); 220 return; 221 } 222 223 if (conn == NULL) { 224 close(sock); 225 return; 226 } 227 228 while (conn->id == 0 || SPLAY_FIND(conntree, &conns, conn)) 229 conn->id = arc4random(); 230 SPLAY_INSERT(conntree, &conns, conn); 231 conn->listener = l; 232 233 if (log_getverbose() > LOGLEVEL_CONN) 234 log_debug("%08x accept %s %s", conn->id, 235 log_fmt_proto(conn->listener->proto), 236 log_fmt_sockaddr((struct sockaddr*)&ss)); 237 238 switch (l->proto) { 239 case PROTO_LPR: 240 lpr_conn(conn->id, l, sock, sa); 241 break; 242 default: 243 fatalx("%s: unexpected protocol %d", __func__, l->proto); 244 } 245 } 246 247 static void 248 frontend_dispatch_priv(struct imsgproc *proc, struct imsg *imsg, void *arg) 249 { 250 struct listener *l; 251 252 if (imsg == NULL) { 253 log_debug("%s: imsg connection lost", __func__); 254 event_loopexit(NULL); 255 return; 256 } 257 258 if (log_getverbose() > LOGLEVEL_IMSG) 259 log_imsg(proc, imsg); 260 261 switch (imsg->hdr.type) { 262 case IMSG_SOCK_ENGINE: 263 if (imsg->fd == -1) 264 fatalx("%s: engine socket not received", __func__); 265 m_end(proc); 266 p_engine = proc_attach(PROC_ENGINE, imsg->fd); 267 proc_setcallback(p_engine, frontend_dispatch_engine, NULL); 268 proc_enable(p_engine); 269 break; 270 271 case IMSG_CONF_START: 272 m_end(proc); 273 if ((tmpconf = calloc(1, sizeof(*tmpconf))) == NULL) 274 fatal("%s: calloc", __func__); 275 TAILQ_INIT(&tmpconf->listeners); 276 break; 277 278 case IMSG_CONF_LISTENER: 279 if (imsg->fd == -1) 280 fatalx("%s: listener socket not received", __func__); 281 if ((l = calloc(1, sizeof(*l))) == NULL) 282 fatal("%s: calloc", __func__); 283 m_get_int(proc, &l->proto); 284 m_get_sockaddr(proc, (struct sockaddr *)&l->ss); 285 m_end(proc); 286 l->sock = imsg->fd; 287 TAILQ_INSERT_TAIL(&tmpconf->listeners, l, entry); 288 break; 289 290 case IMSG_CONF_END: 291 m_end(proc); 292 TAILQ_FOREACH(l, &tmpconf->listeners, entry) 293 frontend_listen(l); 294 env = tmpconf; 295 break; 296 297 default: 298 fatalx("%s: unexpected imsg %s", __func__, 299 log_fmt_imsgtype(imsg->hdr.type)); 300 } 301 } 302 303 static void 304 frontend_dispatch_engine(struct imsgproc *proc, struct imsg *imsg, void *arg) 305 { 306 if (imsg == NULL) { 307 log_debug("%s: imsg connection lost", __func__); 308 event_loopexit(NULL); 309 return; 310 } 311 312 if (log_getverbose() > LOGLEVEL_IMSG) 313 log_imsg(proc, imsg); 314 315 switch (imsg->hdr.type) { 316 case IMSG_GETADDRINFO: 317 case IMSG_GETADDRINFO_END: 318 case IMSG_GETNAMEINFO: 319 resolver_dispatch_result(proc, imsg); 320 break; 321 322 case IMSG_LPR_ALLOWEDHOST: 323 case IMSG_LPR_DISPLAYQ: 324 case IMSG_LPR_RECVJOB: 325 case IMSG_LPR_RECVJOB_CF: 326 case IMSG_LPR_RECVJOB_DF: 327 case IMSG_LPR_RMJOB: 328 lpr_dispatch_engine(proc, imsg); 329 break; 330 331 default: 332 fatalx("%s: unexpected imsg %s", __func__, 333 log_fmt_imsgtype(imsg->hdr.type)); 334 } 335 } 336