File: | src/sbin/isakmpd/monitor.c |
Warning: | line 101, column 24 Access to field 'pw_dir' results in a dereference of a null pointer (loaded from variable 'pw') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: monitor.c,v 1.83 2023/02/08 08:03:11 tb Exp $ */ | |||
2 | ||||
3 | /* | |||
4 | * Copyright (c) 2003 Håkan Olsson. All rights reserved. | |||
5 | * | |||
6 | * Redistribution and use in source and binary forms, with or without | |||
7 | * modification, are permitted provided that the following conditions | |||
8 | * are met: | |||
9 | * 1. Redistributions of source code must retain the above copyright | |||
10 | * notice, this list of conditions and the following disclaimer. | |||
11 | * 2. Redistributions in binary form must reproduce the above copyright | |||
12 | * notice, this list of conditions and the following disclaimer in the | |||
13 | * documentation and/or other materials provided with the distribution. | |||
14 | * | |||
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |||
16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |||
18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |||
19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||
20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |||
24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
25 | */ | |||
26 | ||||
27 | #include <sys/types.h> | |||
28 | #include <sys/socket.h> | |||
29 | #include <sys/ioctl.h> | |||
30 | #include <sys/stat.h> | |||
31 | #include <sys/wait.h> | |||
32 | #include <netinet/in.h> | |||
33 | ||||
34 | #include <dirent.h> | |||
35 | #include <errno(*__errno()).h> | |||
36 | #include <fcntl.h> | |||
37 | #include <pwd.h> | |||
38 | #include <signal.h> | |||
39 | #include <stdlib.h> | |||
40 | #include <string.h> | |||
41 | #include <unistd.h> | |||
42 | #include <limits.h> | |||
43 | ||||
44 | #include <regex.h> | |||
45 | #include <keynote.h> | |||
46 | ||||
47 | #include "conf.h" | |||
48 | #include "log.h" | |||
49 | #include "monitor.h" | |||
50 | #include "policy.h" | |||
51 | #include "ui.h" | |||
52 | #include "util.h" | |||
53 | #include "pf_key_v2.h" | |||
54 | ||||
55 | struct monitor_state { | |||
56 | pid_t pid; | |||
57 | int s; | |||
58 | char root[PATH_MAX1024]; | |||
59 | } m_state; | |||
60 | ||||
61 | extern char *pid_file; | |||
62 | ||||
63 | extern void set_slave_signals(void); | |||
64 | ||||
65 | /* Private functions. */ | |||
66 | static void must_read(void *, size_t); | |||
67 | static void must_write(const void *, size_t); | |||
68 | ||||
69 | static void m_priv_getfd(void); | |||
70 | static void m_priv_setsockopt(void); | |||
71 | static void m_priv_req_readdir(void); | |||
72 | static void m_priv_bind(void); | |||
73 | static void m_priv_pfkey_open(void); | |||
74 | static int m_priv_local_sanitize_path(const char *, size_t, int); | |||
75 | static int m_priv_check_sockopt(int, int); | |||
76 | static int m_priv_check_bind(const struct sockaddr *, socklen_t); | |||
77 | ||||
78 | static void set_monitor_signals(void); | |||
79 | static void sig_pass_to_chld(int); | |||
80 | ||||
81 | /* | |||
82 | * Public functions, unprivileged. | |||
83 | */ | |||
84 | ||||
85 | /* Setup monitor context, fork, drop child privs. */ | |||
86 | pid_t | |||
87 | monitor_init(int debug) | |||
88 | { | |||
89 | struct passwd *pw; | |||
90 | int p[2]; | |||
91 | ||||
92 | bzero(&m_state, sizeof m_state); | |||
93 | ||||
94 | if (socketpair(AF_UNIX1, SOCK_STREAM1, PF_UNSPEC0, p) != 0) | |||
| ||||
95 | log_fatal("monitor_init: socketpair() failed"); | |||
96 | ||||
97 | pw = getpwnam(ISAKMPD_PRIVSEP_USER"_isakmpd"); | |||
98 | if (pw == NULL((void *)0)) | |||
99 | log_fatalx("monitor_init: getpwnam(\"%s\") failed", | |||
100 | ISAKMPD_PRIVSEP_USER"_isakmpd"); | |||
101 | strlcpy(m_state.root, pw->pw_dir, sizeof m_state.root); | |||
| ||||
102 | ||||
103 | set_monitor_signals(); | |||
104 | m_state.pid = fork(); | |||
105 | ||||
106 | if (m_state.pid == -1) | |||
107 | log_fatal("monitor_init: fork of unprivileged child failed"); | |||
108 | if (m_state.pid == 0) { | |||
109 | /* The child process drops privileges. */ | |||
110 | set_slave_signals(); | |||
111 | ||||
112 | if (chroot(pw->pw_dir) != 0 || chdir("/") != 0) | |||
113 | log_fatal("monitor_init: chroot failed"); | |||
114 | ||||
115 | if (setgroups(1, &pw->pw_gid) == -1 || | |||
116 | setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || | |||
117 | setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) | |||
118 | log_fatal("monitor_init: can't drop privileges"); | |||
119 | ||||
120 | m_state.s = p[0]; | |||
121 | close(p[1]); | |||
122 | ||||
123 | LOG_DBG((LOG_MISC, 10,log_debug (LOG_MISC, 10, "monitor_init: privileges dropped for child process" ) | |||
124 | "monitor_init: privileges dropped for child process"))log_debug (LOG_MISC, 10, "monitor_init: privileges dropped for child process" ); | |||
125 | } else { | |||
126 | /* Privileged monitor. */ | |||
127 | setproctitle("monitor [priv]"); | |||
128 | ||||
129 | m_state.s = p[1]; | |||
130 | close(p[0]); | |||
131 | } | |||
132 | ||||
133 | /* With "-dd", stop and wait here. For gdb "attach" etc. */ | |||
134 | if (debug > 1) { | |||
135 | log_print("monitor_init: stopped %s PID %d fd %d%s", | |||
136 | m_state.pid ? "priv" : "child", getpid(), m_state.s, | |||
137 | m_state.pid ? ", waiting for SIGCONT" : ""); | |||
138 | kill(getpid(), SIGSTOP17); /* Wait here for SIGCONT. */ | |||
139 | if (m_state.pid) | |||
140 | kill(m_state.pid, SIGCONT19); /* Continue child. */ | |||
141 | } | |||
142 | ||||
143 | return m_state.pid; | |||
144 | } | |||
145 | ||||
146 | void | |||
147 | monitor_exit(int code) | |||
148 | { | |||
149 | int status = 0, gotstatus = 0; | |||
150 | pid_t pid; | |||
151 | ||||
152 | if (m_state.pid != 0) { | |||
153 | /* When called from the monitor, kill slave and wait for it */ | |||
154 | kill(m_state.pid, SIGTERM15); | |||
155 | ||||
156 | do { | |||
157 | pid = waitpid(m_state.pid, &status, 0); | |||
158 | } while (pid == -1 && errno(*__errno()) == EINTR4); | |||
159 | if (pid != -1) | |||
160 | gotstatus = 1; | |||
161 | ||||
162 | /* Remove FIFO and pid files. */ | |||
163 | unlink(ui_fifo); | |||
164 | unlink(pid_file); | |||
165 | } | |||
166 | ||||
167 | close(m_state.s); | |||
168 | if (code == 0 && gotstatus) | |||
169 | exit(WIFEXITED(status)(((status) & 0177) == 0)? WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff) : 1); | |||
170 | else | |||
171 | exit(code); | |||
172 | } | |||
173 | ||||
174 | int | |||
175 | monitor_pf_key_v2_open(void) | |||
176 | { | |||
177 | int err, cmd; | |||
178 | ||||
179 | cmd = MONITOR_PFKEY_OPEN; | |||
180 | must_write(&cmd, sizeof cmd); | |||
181 | ||||
182 | must_read(&err, sizeof err); | |||
183 | if (err < 0) { | |||
184 | log_error("monitor_pf_key_v2_open: parent could not create " | |||
185 | "PF_KEY socket"); | |||
186 | return -1; | |||
187 | } | |||
188 | pf_key_v2_socket = mm_receive_fd(m_state.s); | |||
189 | if (pf_key_v2_socket < 0) { | |||
190 | log_error("monitor_pf_key_v2_open: mm_receive_fd() failed"); | |||
191 | return -1; | |||
192 | } | |||
193 | ||||
194 | return pf_key_v2_socket; | |||
195 | } | |||
196 | ||||
197 | int | |||
198 | monitor_open(const char *path, int flags, mode_t mode) | |||
199 | { | |||
200 | size_t len; | |||
201 | int fd, err, cmd; | |||
202 | char pathreal[PATH_MAX1024]; | |||
203 | ||||
204 | if (path[0] == '/') | |||
205 | strlcpy(pathreal, path, sizeof pathreal); | |||
206 | else | |||
207 | snprintf(pathreal, sizeof pathreal, "%s/%s", m_state.root, | |||
208 | path); | |||
209 | ||||
210 | cmd = MONITOR_GET_FD; | |||
211 | must_write(&cmd, sizeof cmd); | |||
212 | ||||
213 | len = strlen(pathreal); | |||
214 | must_write(&len, sizeof len); | |||
215 | must_write(&pathreal, len); | |||
216 | ||||
217 | must_write(&flags, sizeof flags); | |||
218 | must_write(&mode, sizeof mode); | |||
219 | ||||
220 | must_read(&err, sizeof err); | |||
221 | if (err != 0) { | |||
222 | errno(*__errno()) = err; | |||
223 | return -1; | |||
224 | } | |||
225 | ||||
226 | fd = mm_receive_fd(m_state.s); | |||
227 | if (fd < 0) { | |||
228 | log_error("monitor_open: mm_receive_fd () failed"); | |||
229 | return -1; | |||
230 | } | |||
231 | ||||
232 | return fd; | |||
233 | } | |||
234 | ||||
235 | FILE * | |||
236 | monitor_fopen(const char *path, const char *mode) | |||
237 | { | |||
238 | FILE *fp; | |||
239 | int fd, flags = 0, saved_errno; | |||
240 | mode_t mask, cur_umask; | |||
241 | ||||
242 | /* Only the child process is supposed to run this. */ | |||
243 | if (m_state.pid) | |||
244 | log_fatal("[priv] bad call to monitor_fopen"); | |||
245 | ||||
246 | switch (mode[0]) { | |||
247 | case 'r': | |||
248 | flags = (mode[1] == '+' ? O_RDWR0x0002 : O_RDONLY0x0000); | |||
249 | break; | |||
250 | case 'w': | |||
251 | flags = (mode[1] == '+' ? O_RDWR0x0002 : O_WRONLY0x0001) | O_CREAT0x0200 | | |||
252 | O_TRUNC0x0400; | |||
253 | break; | |||
254 | case 'a': | |||
255 | flags = (mode[1] == '+' ? O_RDWR0x0002 : O_WRONLY0x0001) | O_CREAT0x0200 | | |||
256 | O_APPEND0x0008; | |||
257 | break; | |||
258 | default: | |||
259 | log_fatal("monitor_fopen: bad call"); | |||
260 | } | |||
261 | ||||
262 | cur_umask = umask(0); | |||
263 | (void)umask(cur_umask); | |||
264 | mask = S_IRUSR0000400 | S_IWUSR0000200 | S_IRGRP0000040 | S_IWGRP0000020 | S_IROTH0000004 | S_IWOTH0000002; | |||
265 | mask &= ~cur_umask; | |||
266 | ||||
267 | fd = monitor_open(path, flags, mask); | |||
268 | if (fd < 0) | |||
269 | return NULL((void *)0); | |||
270 | ||||
271 | /* Got the fd, attach a FILE * to it. */ | |||
272 | fp = fdopen(fd, mode); | |||
273 | if (!fp) { | |||
274 | log_error("monitor_fopen: fdopen() failed"); | |||
275 | saved_errno = errno(*__errno()); | |||
276 | close(fd); | |||
277 | errno(*__errno()) = saved_errno; | |||
278 | return NULL((void *)0); | |||
279 | } | |||
280 | return fp; | |||
281 | } | |||
282 | ||||
283 | int | |||
284 | monitor_stat(const char *path, struct stat *sb) | |||
285 | { | |||
286 | int fd, r, saved_errno; | |||
287 | ||||
288 | /* O_NONBLOCK is needed for stat'ing fifos. */ | |||
289 | fd = monitor_open(path, O_RDONLY0x0000 | O_NONBLOCK0x0004, 0); | |||
290 | if (fd < 0) | |||
291 | return -1; | |||
292 | ||||
293 | r = fstat(fd, sb); | |||
294 | saved_errno = errno(*__errno()); | |||
295 | close(fd); | |||
296 | errno(*__errno()) = saved_errno; | |||
297 | return r; | |||
298 | } | |||
299 | ||||
300 | int | |||
301 | monitor_setsockopt(int s, int level, int optname, const void *optval, | |||
302 | socklen_t optlen) | |||
303 | { | |||
304 | int ret, err, cmd; | |||
305 | ||||
306 | cmd = MONITOR_SETSOCKOPT; | |||
307 | must_write(&cmd, sizeof cmd); | |||
308 | if (mm_send_fd(m_state.s, s)) { | |||
309 | log_print("monitor_setsockopt: read/write error"); | |||
310 | return -1; | |||
311 | } | |||
312 | ||||
313 | must_write(&level, sizeof level); | |||
314 | must_write(&optname, sizeof optname); | |||
315 | must_write(&optlen, sizeof optlen); | |||
316 | must_write(optval, optlen); | |||
317 | ||||
318 | must_read(&err, sizeof err); | |||
319 | must_read(&ret, sizeof ret); | |||
320 | if (err != 0) | |||
321 | errno(*__errno()) = err; | |||
322 | return ret; | |||
323 | } | |||
324 | ||||
325 | int | |||
326 | monitor_bind(int s, const struct sockaddr *name, socklen_t namelen) | |||
327 | { | |||
328 | int ret, err, cmd; | |||
329 | ||||
330 | cmd = MONITOR_BIND; | |||
331 | must_write(&cmd, sizeof cmd); | |||
332 | if (mm_send_fd(m_state.s, s)) { | |||
333 | log_print("monitor_bind: read/write error"); | |||
334 | return -1; | |||
335 | } | |||
336 | ||||
337 | must_write(&namelen, sizeof namelen); | |||
338 | must_write(name, namelen); | |||
339 | ||||
340 | must_read(&err, sizeof err); | |||
341 | must_read(&ret, sizeof ret); | |||
342 | if (err != 0) | |||
343 | errno(*__errno()) = err; | |||
344 | return ret; | |||
345 | } | |||
346 | ||||
347 | int | |||
348 | monitor_req_readdir(const char *filename) | |||
349 | { | |||
350 | int cmd, err; | |||
351 | size_t len; | |||
352 | ||||
353 | cmd = MONITOR_REQ_READDIR; | |||
354 | must_write(&cmd, sizeof cmd); | |||
355 | ||||
356 | len = strlen(filename); | |||
357 | must_write(&len, sizeof len); | |||
358 | must_write(filename, len); | |||
359 | ||||
360 | must_read(&err, sizeof err); | |||
361 | if (err == -1) | |||
362 | must_read(&errno(*__errno()), sizeof errno(*__errno())); | |||
363 | ||||
364 | return err; | |||
365 | } | |||
366 | ||||
367 | int | |||
368 | monitor_readdir(char *file, size_t size) | |||
369 | { | |||
370 | int fd; | |||
371 | size_t len; | |||
372 | ||||
373 | must_read(&len, sizeof len); | |||
374 | if (len == 0) | |||
375 | return -1; | |||
376 | if (len >= size) | |||
377 | log_fatal("monitor_readdir: received bad length from monitor"); | |||
378 | must_read(file, len); | |||
379 | file[len] = '\0'; | |||
380 | fd = mm_receive_fd(m_state.s); | |||
381 | return fd; | |||
382 | } | |||
383 | ||||
384 | void | |||
385 | monitor_init_done(void) | |||
386 | { | |||
387 | int cmd; | |||
388 | ||||
389 | cmd = MONITOR_INIT_DONE; | |||
390 | must_write(&cmd, sizeof cmd); | |||
391 | } | |||
392 | ||||
393 | /* | |||
394 | * Start of code running with privileges (the monitor process). | |||
395 | */ | |||
396 | ||||
397 | static void | |||
398 | set_monitor_signals(void) | |||
399 | { | |||
400 | int n; | |||
401 | ||||
402 | for (n = 1; n < _NSIG33; n++) | |||
403 | signal(n, SIG_DFL(void (*)(int))0); | |||
404 | ||||
405 | /* Forward some signals to the child. */ | |||
406 | signal(SIGTERM15, sig_pass_to_chld); | |||
407 | signal(SIGHUP1, sig_pass_to_chld); | |||
408 | signal(SIGUSR130, sig_pass_to_chld); | |||
409 | } | |||
410 | ||||
411 | static void | |||
412 | sig_pass_to_chld(int sig) | |||
413 | { | |||
414 | int oerrno = errno(*__errno()); | |||
415 | ||||
416 | if (m_state.pid > 0) | |||
417 | kill(m_state.pid, sig); | |||
418 | errno(*__errno()) = oerrno; | |||
419 | } | |||
420 | ||||
421 | /* This function is where the privileged process waits(loops) indefinitely. */ | |||
422 | void | |||
423 | monitor_loop(int debug) | |||
424 | { | |||
425 | int msgcode; | |||
426 | ||||
427 | if (!debug) | |||
428 | log_to(0); | |||
429 | ||||
430 | for (;;) { | |||
431 | must_read(&msgcode, sizeof msgcode); | |||
432 | ||||
433 | switch (msgcode) { | |||
434 | case MONITOR_GET_FD: | |||
435 | m_priv_getfd(); | |||
436 | break; | |||
437 | ||||
438 | case MONITOR_PFKEY_OPEN: | |||
439 | LOG_DBG((LOG_MISC, 80,log_debug (LOG_MISC, 80, "monitor_loop: MONITOR_PFKEY_OPEN") | |||
440 | "monitor_loop: MONITOR_PFKEY_OPEN"))log_debug (LOG_MISC, 80, "monitor_loop: MONITOR_PFKEY_OPEN"); | |||
441 | m_priv_pfkey_open(); | |||
442 | break; | |||
443 | ||||
444 | case MONITOR_SETSOCKOPT: | |||
445 | LOG_DBG((LOG_MISC, 80,log_debug (LOG_MISC, 80, "monitor_loop: MONITOR_SETSOCKOPT") | |||
446 | "monitor_loop: MONITOR_SETSOCKOPT"))log_debug (LOG_MISC, 80, "monitor_loop: MONITOR_SETSOCKOPT"); | |||
447 | m_priv_setsockopt(); | |||
448 | break; | |||
449 | ||||
450 | case MONITOR_BIND: | |||
451 | LOG_DBG((LOG_MISC, 80,log_debug (LOG_MISC, 80, "monitor_loop: MONITOR_BIND") | |||
452 | "monitor_loop: MONITOR_BIND"))log_debug (LOG_MISC, 80, "monitor_loop: MONITOR_BIND"); | |||
453 | m_priv_bind(); | |||
454 | break; | |||
455 | ||||
456 | case MONITOR_REQ_READDIR: | |||
457 | LOG_DBG((LOG_MISC, 80,log_debug (LOG_MISC, 80, "monitor_loop: MONITOR_REQ_READDIR") | |||
458 | "monitor_loop: MONITOR_REQ_READDIR"))log_debug (LOG_MISC, 80, "monitor_loop: MONITOR_REQ_READDIR"); | |||
459 | m_priv_req_readdir(); | |||
460 | break; | |||
461 | ||||
462 | case MONITOR_INIT_DONE: | |||
463 | LOG_DBG((LOG_MISC, 80,log_debug (LOG_MISC, 80, "monitor_loop: MONITOR_INIT_DONE") | |||
464 | "monitor_loop: MONITOR_INIT_DONE"))log_debug (LOG_MISC, 80, "monitor_loop: MONITOR_INIT_DONE"); | |||
465 | break; | |||
466 | ||||
467 | case MONITOR_SHUTDOWN: | |||
468 | LOG_DBG((LOG_MISC, 80,log_debug (LOG_MISC, 80, "monitor_loop: MONITOR_SHUTDOWN") | |||
469 | "monitor_loop: MONITOR_SHUTDOWN"))log_debug (LOG_MISC, 80, "monitor_loop: MONITOR_SHUTDOWN"); | |||
470 | break; | |||
471 | ||||
472 | default: | |||
473 | log_print("monitor_loop: got unknown code %d", | |||
474 | msgcode); | |||
475 | } | |||
476 | } | |||
477 | ||||
478 | exit(0); | |||
479 | } | |||
480 | ||||
481 | ||||
482 | /* Privileged: called by monitor_loop. */ | |||
483 | static void | |||
484 | m_priv_pfkey_open(void) | |||
485 | { | |||
486 | int fd, err = 0; | |||
487 | ||||
488 | fd = pf_key_v2_open(); | |||
489 | if (fd < 0) | |||
490 | err = -1; | |||
491 | ||||
492 | must_write(&err, sizeof err); | |||
493 | ||||
494 | if (fd > 0 && mm_send_fd(m_state.s, fd)) { | |||
495 | log_error("m_priv_pfkey_open: read/write operation failed"); | |||
496 | close(fd); | |||
497 | return; | |||
498 | } | |||
499 | close(fd); | |||
500 | } | |||
501 | ||||
502 | /* Privileged: called by monitor_loop. */ | |||
503 | static void | |||
504 | m_priv_getfd(void) | |||
505 | { | |||
506 | char path[PATH_MAX1024]; | |||
507 | size_t len; | |||
508 | int v, flags, ret; | |||
509 | int err = 0; | |||
510 | mode_t mode; | |||
511 | ||||
512 | must_read(&len, sizeof len); | |||
513 | if (len == 0 || len >= sizeof path) | |||
514 | log_fatal("m_priv_getfd: invalid pathname length"); | |||
515 | ||||
516 | must_read(path, len); | |||
517 | path[len] = '\0'; | |||
518 | if (strlen(path) != len) | |||
519 | log_fatal("m_priv_getfd: invalid pathname"); | |||
520 | ||||
521 | must_read(&flags, sizeof flags); | |||
522 | must_read(&mode, sizeof mode); | |||
523 | ||||
524 | if ((ret = m_priv_local_sanitize_path(path, sizeof path, flags)) | |||
525 | != 0) { | |||
526 | if (errno(*__errno()) != ENOENT2) | |||
527 | log_print("m_priv_getfd: illegal path \"%s\"", path); | |||
528 | err = errno(*__errno()); | |||
529 | v = -1; | |||
530 | } else { | |||
531 | if ((v = open(path, flags, mode)) == -1) | |||
532 | err = errno(*__errno()); | |||
533 | } | |||
534 | ||||
535 | must_write(&err, sizeof err); | |||
536 | ||||
537 | if (v != -1) { | |||
538 | if (mm_send_fd(m_state.s, v) == -1) | |||
539 | log_error("m_priv_getfd: sending fd failed"); | |||
540 | close(v); | |||
541 | } | |||
542 | } | |||
543 | ||||
544 | /* Privileged: called by monitor_loop. */ | |||
545 | static void | |||
546 | m_priv_setsockopt(void) | |||
547 | { | |||
548 | int sock, level, optname, v; | |||
549 | int err = 0; | |||
550 | char *optval = 0; | |||
551 | socklen_t optlen; | |||
552 | ||||
553 | sock = mm_receive_fd(m_state.s); | |||
554 | if (sock < 0) { | |||
555 | log_print("m_priv_setsockopt: read/write error"); | |||
556 | return; | |||
557 | } | |||
558 | ||||
559 | must_read(&level, sizeof level); | |||
560 | must_read(&optname, sizeof optname); | |||
561 | must_read(&optlen, sizeof optlen); | |||
562 | ||||
563 | optval = malloc(optlen); | |||
564 | if (!optval) { | |||
565 | log_print("m_priv_setsockopt: malloc failed"); | |||
566 | close(sock); | |||
567 | return; | |||
568 | } | |||
569 | ||||
570 | must_read(optval, optlen); | |||
571 | ||||
572 | if (m_priv_check_sockopt(level, optname) != 0) { | |||
573 | err = EACCES13; | |||
574 | v = -1; | |||
575 | } else { | |||
576 | v = setsockopt(sock, level, optname, optval, optlen); | |||
577 | if (v < 0) | |||
578 | err = errno(*__errno()); | |||
579 | } | |||
580 | ||||
581 | close(sock); | |||
582 | sock = -1; | |||
583 | ||||
584 | must_write(&err, sizeof err); | |||
585 | must_write(&v, sizeof v); | |||
586 | ||||
587 | free(optval); | |||
588 | return; | |||
589 | } | |||
590 | ||||
591 | /* Privileged: called by monitor_loop. */ | |||
592 | static void | |||
593 | m_priv_bind(void) | |||
594 | { | |||
595 | int sock, v, err = 0; | |||
596 | struct sockaddr *name = 0; | |||
597 | socklen_t namelen; | |||
598 | ||||
599 | sock = mm_receive_fd(m_state.s); | |||
600 | if (sock < 0) { | |||
601 | log_print("m_priv_bind: read/write error"); | |||
602 | return; | |||
603 | } | |||
604 | ||||
605 | must_read(&namelen, sizeof namelen); | |||
606 | name = malloc(namelen); | |||
607 | if (!name) { | |||
608 | log_print("m_priv_bind: malloc failed"); | |||
609 | close(sock); | |||
610 | return; | |||
611 | } | |||
612 | must_read((char *)name, namelen); | |||
613 | ||||
614 | if (m_priv_check_bind(name, namelen) != 0) { | |||
615 | err = EACCES13; | |||
616 | v = -1; | |||
617 | } else { | |||
618 | v = bind(sock, name, namelen); | |||
619 | if (v == -1) { | |||
620 | log_error("m_priv_bind: bind(%d,%p,%d) returned %d", | |||
621 | sock, name, namelen, v); | |||
622 | err = errno(*__errno()); | |||
623 | } | |||
624 | } | |||
625 | ||||
626 | close(sock); | |||
627 | sock = -1; | |||
628 | ||||
629 | must_write(&err, sizeof err); | |||
630 | must_write(&v, sizeof v); | |||
631 | ||||
632 | free(name); | |||
633 | return; | |||
634 | } | |||
635 | ||||
636 | /* | |||
637 | * Help functions, used by both privileged and unprivileged code | |||
638 | */ | |||
639 | ||||
640 | /* | |||
641 | * Read data with the assertion that it all must come through, or else abort | |||
642 | * the process. Based on atomicio() from openssh. | |||
643 | */ | |||
644 | static void | |||
645 | must_read(void *buf, size_t n) | |||
646 | { | |||
647 | char *s = buf; | |||
648 | size_t pos = 0; | |||
649 | ssize_t res; | |||
650 | ||||
651 | while (n > pos) { | |||
652 | res = read(m_state.s, s + pos, n - pos); | |||
653 | switch (res) { | |||
654 | case -1: | |||
655 | if (errno(*__errno()) == EINTR4 || errno(*__errno()) == EAGAIN35) | |||
656 | continue; | |||
657 | case 0: | |||
658 | monitor_exit(0); | |||
659 | default: | |||
660 | pos += res; | |||
661 | } | |||
662 | } | |||
663 | } | |||
664 | ||||
665 | /* | |||
666 | * Write data with the assertion that it all has to be written, or else abort | |||
667 | * the process. Based on atomicio() from openssh. | |||
668 | */ | |||
669 | static void | |||
670 | must_write(const void *buf, size_t n) | |||
671 | { | |||
672 | const char *s = buf; | |||
673 | size_t pos = 0; | |||
674 | ssize_t res; | |||
675 | ||||
676 | while (n > pos) { | |||
677 | res = write(m_state.s, s + pos, n - pos); | |||
678 | switch (res) { | |||
679 | case -1: | |||
680 | if (errno(*__errno()) == EINTR4 || errno(*__errno()) == EAGAIN35) | |||
681 | continue; | |||
682 | case 0: | |||
683 | monitor_exit(0); | |||
684 | default: | |||
685 | pos += res; | |||
686 | } | |||
687 | } | |||
688 | } | |||
689 | ||||
690 | /* Check that path/mode is permitted. */ | |||
691 | static int | |||
692 | m_priv_local_sanitize_path(const char *path, size_t pmax, int flags) | |||
693 | { | |||
694 | char new_path[PATH_MAX1024], var_run[PATH_MAX1024], *enddir; | |||
695 | ||||
696 | /* | |||
697 | * We only permit paths starting with | |||
698 | * /etc/isakmpd/ (read only) | |||
699 | * /var/run/ (rw) | |||
700 | */ | |||
701 | ||||
702 | if (realpath(path, new_path) == NULL((void *)0)) { | |||
703 | if (errno(*__errno()) != ENOENT2) | |||
704 | return 1; | |||
705 | /* | |||
706 | * It is ok if the directory exists, | |||
707 | * but the file should be created. | |||
708 | */ | |||
709 | if (strlcpy(new_path, path, sizeof(new_path)) >= | |||
710 | sizeof(new_path)) | |||
711 | return 1; | |||
712 | enddir = strrchr(new_path, '/'); | |||
713 | if (enddir == NULL((void *)0) || enddir[1] == '\0') | |||
714 | return 1; | |||
715 | enddir[1] = '\0'; | |||
716 | if (realpath(new_path, new_path) == NULL((void *)0)) { | |||
717 | errno(*__errno()) = ENOENT2; | |||
718 | return 1; | |||
719 | } | |||
720 | enddir = strrchr(path, '/'); | |||
721 | strlcat(new_path, enddir, sizeof(new_path)); | |||
722 | } | |||
723 | ||||
724 | if (realpath("/var/run/", var_run) == NULL((void *)0)) | |||
725 | return 1; | |||
726 | strlcat(var_run, "/", sizeof(var_run)); | |||
727 | ||||
728 | if (strncmp(var_run, new_path, strlen(var_run)) == 0) | |||
729 | return 0; | |||
730 | ||||
731 | if (strncmp(ISAKMPD_ROOT"/etc/isakmpd/", new_path, strlen(ISAKMPD_ROOT"/etc/isakmpd/")) == 0 && | |||
732 | (flags & O_ACCMODE0x0003) == O_RDONLY0x0000) | |||
733 | return 0; | |||
734 | ||||
735 | errno(*__errno()) = EACCES13; | |||
736 | return 1; | |||
737 | } | |||
738 | ||||
739 | /* Check setsockopt */ | |||
740 | static int | |||
741 | m_priv_check_sockopt(int level, int name) | |||
742 | { | |||
743 | switch (level) { | |||
744 | /* These are allowed */ | |||
745 | case SOL_SOCKET0xffff: | |||
746 | case IPPROTO_IP0: | |||
747 | case IPPROTO_IPV641: | |||
748 | break; | |||
749 | ||||
750 | default: | |||
751 | log_print("m_priv_check_sockopt: Illegal level %d", level); | |||
752 | return 1; | |||
753 | } | |||
754 | ||||
755 | switch (name) { | |||
756 | /* These are allowed */ | |||
757 | case SO_REUSEPORT0x0200: | |||
758 | case SO_REUSEADDR0x0004: | |||
759 | case IP_AUTH_LEVEL20: | |||
760 | case IP_ESP_TRANS_LEVEL21: | |||
761 | case IP_ESP_NETWORK_LEVEL22: | |||
762 | case IP_IPCOMP_LEVEL29: | |||
763 | case IPV6_AUTH_LEVEL53: | |||
764 | case IPV6_ESP_TRANS_LEVEL54: | |||
765 | case IPV6_ESP_NETWORK_LEVEL55: | |||
766 | case IPV6_IPCOMP_LEVEL60: | |||
767 | break; | |||
768 | ||||
769 | default: | |||
770 | log_print("m_priv_check_sockopt: Illegal option name %d", | |||
771 | name); | |||
772 | return 1; | |||
773 | } | |||
774 | ||||
775 | return 0; | |||
776 | } | |||
777 | ||||
778 | /* Check bind */ | |||
779 | static int | |||
780 | m_priv_check_bind(const struct sockaddr *sa, socklen_t salen) | |||
781 | { | |||
782 | in_port_t port; | |||
783 | ||||
784 | if (sa == NULL((void *)0)) { | |||
785 | log_print("NULL address"); | |||
786 | return 1; | |||
787 | } | |||
788 | if (SA_LEN(sa)((sa)->sa_len) != salen) { | |||
789 | log_print("Length mismatch: %lu %lu", (unsigned long)sa->sa_len, | |||
790 | (unsigned long)salen); | |||
791 | return 1; | |||
792 | } | |||
793 | switch (sa->sa_family) { | |||
794 | case AF_INET2: | |||
795 | if (salen != sizeof(struct sockaddr_in)) { | |||
796 | log_print("Invalid inet address length"); | |||
797 | return 1; | |||
798 | } | |||
799 | port = ((const struct sockaddr_in *)sa)->sin_port; | |||
800 | break; | |||
801 | case AF_INET624: | |||
802 | if (salen != sizeof(struct sockaddr_in6)) { | |||
803 | log_print("Invalid inet6 address length"); | |||
804 | return 1; | |||
805 | } | |||
806 | port = ((const struct sockaddr_in6 *)sa)->sin6_port; | |||
807 | break; | |||
808 | default: | |||
809 | log_print("Unknown address family"); | |||
810 | return 1; | |||
811 | } | |||
812 | ||||
813 | port = ntohs(port)(__uint16_t)(__builtin_constant_p(port) ? (__uint16_t)(((__uint16_t )(port) & 0xffU) << 8 | ((__uint16_t)(port) & 0xff00U ) >> 8) : __swap16md(port)); | |||
814 | ||||
815 | if (port != ISAKMP_PORT_DEFAULT500 && port < 1024) { | |||
816 | log_print("Disallowed port %u", port); | |||
817 | return 1; | |||
818 | } | |||
819 | return 0; | |||
820 | } | |||
821 | ||||
822 | static void | |||
823 | m_priv_req_readdir(void) | |||
824 | { | |||
825 | size_t len; | |||
826 | char path[PATH_MAX1024]; | |||
827 | DIR *dp; | |||
828 | struct dirent *file; | |||
829 | struct stat sb; | |||
830 | int off, size, fd, ret, serrno; | |||
831 | ||||
832 | must_read(&len, sizeof len); | |||
833 | if (len == 0 || len >= sizeof path) | |||
834 | log_fatal("m_priv_req_readdir: invalid pathname length"); | |||
835 | must_read(path, len); | |||
836 | path[len] = '\0'; | |||
837 | if (strlen(path) != len) | |||
838 | log_fatal("m_priv_req_readdir: invalid pathname"); | |||
839 | ||||
840 | off = strlen(path); | |||
841 | size = sizeof path - off; | |||
842 | ||||
843 | if ((dp = opendir(path)) == NULL((void *)0)) { | |||
844 | serrno = errno(*__errno()); | |||
845 | ret = -1; | |||
846 | must_write(&ret, sizeof ret); | |||
847 | must_write(&serrno, sizeof serrno); | |||
848 | return; | |||
849 | } | |||
850 | ||||
851 | /* report opendir() success */ | |||
852 | ret = 0; | |||
853 | must_write(&ret, sizeof ret); | |||
854 | ||||
855 | while ((file = readdir(dp)) != NULL((void *)0)) { | |||
856 | strlcpy(path + off, file->d_name, size); | |||
857 | ||||
858 | if (m_priv_local_sanitize_path(path, sizeof path, O_RDONLY0x0000) | |||
859 | != 0) | |||
860 | continue; | |||
861 | fd = open(path, O_RDONLY0x0000); | |||
862 | if (fd == -1) { | |||
863 | log_error("m_priv_req_readdir: open " | |||
864 | "(\"%s\", O_RDONLY, 0) failed", path); | |||
865 | continue; | |||
866 | } | |||
867 | if ((fstat(fd, &sb) == -1) || | |||
868 | !(S_ISREG(sb.st_mode)((sb.st_mode & 0170000) == 0100000) || S_ISLNK(sb.st_mode)((sb.st_mode & 0170000) == 0120000))) { | |||
869 | close(fd); | |||
870 | continue; | |||
871 | } | |||
872 | ||||
873 | len = strlen(path); | |||
874 | must_write(&len, sizeof len); | |||
875 | must_write(path, len); | |||
876 | ||||
877 | mm_send_fd(m_state.s, fd); | |||
878 | close(fd); | |||
879 | } | |||
880 | closedir(dp); | |||
881 | ||||
882 | len = 0; | |||
883 | must_write(&len, sizeof len); | |||
884 | } |