1 /* $OpenBSD: control.c,v 1.3 2024/03/22 19:14:28 bluhm 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/types.h> 20 #include <sys/queue.h> 21 #include <sys/stat.h> 22 #include <sys/socket.h> 23 #include <sys/un.h> 24 25 #include <errno.h> 26 #include <event.h> 27 #include <imsg.h> 28 #include <paths.h> 29 #include <pwd.h> 30 #include <signal.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <syslog.h> 34 #include <unistd.h> 35 36 #include "lpd.h" 37 38 #include "log.h" 39 #include "proc.h" 40 41 #define CONTROL_BACKLOG 5 42 43 static void control_init(const char *); 44 static void control_listen(void); 45 static void control_pause(void); 46 static void control_resume(void); 47 static void control_accept(int, short, void *); 48 static void control_close(struct imsgproc *); 49 static void control_dispatch_priv(struct imsgproc *, struct imsg *, void *); 50 static void control_dispatch_client(struct imsgproc *, struct imsg *, void *); 51 52 static struct { 53 struct event evt; 54 int fd; 55 int pause; 56 } ctl; 57 58 void 59 control(int debug, int verbose) 60 { 61 struct passwd *pw; 62 63 /* Early initialisation. */ 64 log_init(debug, LOG_DAEMON); 65 log_setverbose(verbose); 66 log_procinit("control"); 67 setproctitle("control"); 68 69 control_init(LPD_SOCKET); 70 71 /* Drop privileges. */ 72 if ((pw = getpwnam(LPD_USER)) == NULL) 73 fatalx("unknown user " LPD_USER); 74 75 if (chroot(_PATH_VAREMPTY) == -1) 76 fatal("%s: chroot", __func__); 77 if (chdir("/") == -1) 78 fatal("%s: chdir", __func__); 79 80 if (setgroups(1, &pw->pw_gid) || 81 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 82 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 83 fatal("cannot drop privileges"); 84 85 if (pledge("stdio unix recvfd sendfd", NULL) == -1) 86 fatal("%s: pledge", __func__); 87 88 event_init(); 89 90 signal(SIGPIPE, SIG_IGN); 91 92 /* Setup imsg socket with parent. */ 93 p_priv = proc_attach(PROC_PRIV, 3); 94 if (p_priv == NULL) 95 fatal("%s: proc_attach", __func__); 96 proc_setcallback(p_priv, control_dispatch_priv, NULL); 97 proc_enable(p_priv); 98 99 event_dispatch(); 100 101 exit(0); 102 } 103 104 static void 105 control_init(const char *path) 106 { 107 struct sockaddr_un sun; 108 mode_t old_umask; 109 int fd; 110 111 fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); 112 if (fd == -1) 113 fatal("%s: socket", __func__); 114 115 memset(&sun, 0, sizeof(sun)); 116 sun.sun_family = AF_UNIX; 117 strlcpy(sun.sun_path, LPD_SOCKET, sizeof(sun.sun_path)); 118 119 if ((unlink(path) == -1) && (errno != ENOENT)) 120 fatal("%s: unlink: %s", __func__, path); 121 122 old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); 123 if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) 124 fatal("%s: bind: %s", __func__, path); 125 umask(old_umask); 126 127 if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) 128 fatal("%s: chmod: %s", __func__, path); 129 130 ctl.fd = fd; 131 } 132 133 static void 134 control_listen(void) 135 { 136 if (listen(ctl.fd, CONTROL_BACKLOG) == -1) 137 fatal("%s: listen", __func__); 138 139 ctl.pause = 0; 140 control_resume(); 141 } 142 143 static void 144 control_pause(void) 145 { 146 struct timeval tv; 147 148 event_del(&ctl.evt); 149 150 tv.tv_sec = 1; 151 tv.tv_usec = 0; 152 153 evtimer_set(&ctl.evt, control_accept, NULL); 154 evtimer_add(&ctl.evt, &tv); 155 ctl.pause = 1; 156 } 157 158 static void 159 control_resume(void) 160 { 161 if (ctl.pause) { 162 evtimer_del(&ctl.evt); 163 ctl.pause = 0; 164 } 165 event_set(&ctl.evt, ctl.fd, EV_READ | EV_PERSIST, control_accept, NULL); 166 event_add(&ctl.evt, NULL); 167 } 168 169 static void 170 control_accept(int fd, short event, void *arg) 171 { 172 struct imsgproc *proc; 173 int sock; 174 175 if (ctl.pause) { 176 ctl.pause = 0; 177 control_resume(); 178 return; 179 } 180 181 sock = accept4(ctl.fd, NULL, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK); 182 if (sock == -1) { 183 if (errno == ENFILE || errno == EMFILE) 184 control_pause(); 185 else if (errno != EWOULDBLOCK && errno != EINTR && 186 errno != ECONNABORTED) 187 log_warn("%s: accept4", __func__); 188 return; 189 } 190 191 proc = proc_attach(PROC_CLIENT, sock); 192 if (proc == NULL) { 193 log_warn("%s: proc_attach", __func__); 194 close(sock); 195 return; 196 } 197 proc_setcallback(proc, control_dispatch_client, NULL); 198 proc_enable(proc); 199 } 200 201 static void 202 control_close(struct imsgproc *proc) 203 { 204 proc_free(proc); 205 206 if (ctl.pause) 207 control_resume(); 208 } 209 210 static void 211 control_dispatch_priv(struct imsgproc *proc, struct imsg *imsg, void *arg) 212 { 213 if (imsg == NULL) { 214 log_debug("%s: imsg connection lost", __func__); 215 event_loopexit(NULL); 216 return; 217 } 218 219 if (log_getverbose() > LOGLEVEL_IMSG) 220 log_imsg(proc, imsg); 221 222 switch (imsg->hdr.type) { 223 case IMSG_CONF_START: 224 m_end(proc); 225 break; 226 227 case IMSG_CONF_END: 228 m_end(proc); 229 control_listen(); 230 break; 231 232 default: 233 fatalx("%s: unexpected imsg %s", __func__, 234 log_fmt_imsgtype(imsg->hdr.type)); 235 } 236 } 237 238 static void 239 control_dispatch_client(struct imsgproc *proc, struct imsg *imsg, void *arg) 240 { 241 if (imsg == NULL) { 242 control_close(proc); 243 return; 244 } 245 246 if (log_getverbose() > LOGLEVEL_IMSG) 247 log_imsg(proc, imsg); 248 249 switch (imsg->hdr.type) { 250 default: 251 log_debug("%s: error handling imsg %d", __func__, 252 imsg->hdr.type); 253 } 254 } 255