File: | src/sbin/isakmpd/monitor.c |
Warning: | line 627, column 2 Value stored to 'sock' is never read |
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; |
Value stored to 'sock' is never read | |
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 | } |