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
control(int debug,int verbose)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
control_init(const char * path)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
control_listen(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
control_pause(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
control_resume(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
control_accept(int fd,short event,void * arg)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
control_close(struct imsgproc * proc)202 control_close(struct imsgproc *proc)
203 {
204 proc_free(proc);
205
206 if (ctl.pause)
207 control_resume();
208 }
209
210 static void
control_dispatch_priv(struct imsgproc * proc,struct imsg * imsg,void * arg)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
control_dispatch_client(struct imsgproc * proc,struct imsg * imsg,void * arg)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