1*ff9f425cStb /* $OpenBSD: monitor.c,v 1.83 2023/02/08 08:03:11 tb Exp $ */
2da35d433Sho
3da35d433Sho /*
4da35d433Sho * Copyright (c) 2003 H�kan Olsson. All rights reserved.
5da35d433Sho *
6da35d433Sho * Redistribution and use in source and binary forms, with or without
7da35d433Sho * modification, are permitted provided that the following conditions
8da35d433Sho * are met:
9da35d433Sho * 1. Redistributions of source code must retain the above copyright
10da35d433Sho * notice, this list of conditions and the following disclaimer.
11da35d433Sho * 2. Redistributions in binary form must reproduce the above copyright
12da35d433Sho * notice, this list of conditions and the following disclaimer in the
13da35d433Sho * documentation and/or other materials provided with the distribution.
14da35d433Sho *
15da35d433Sho * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16da35d433Sho * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17da35d433Sho * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18da35d433Sho * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19da35d433Sho * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20da35d433Sho * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21da35d433Sho * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22da35d433Sho * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23da35d433Sho * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24da35d433Sho * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25da35d433Sho */
26da35d433Sho
27da35d433Sho #include <sys/types.h>
28da35d433Sho #include <sys/socket.h>
29da35d433Sho #include <sys/ioctl.h>
30da35d433Sho #include <sys/stat.h>
310191409fSho #include <sys/wait.h>
3231516eecShshoexer #include <netinet/in.h>
3398777286Shshoexer
3498777286Shshoexer #include <dirent.h>
35da35d433Sho #include <errno.h>
36da35d433Sho #include <fcntl.h>
37da35d433Sho #include <pwd.h>
38da35d433Sho #include <signal.h>
39da35d433Sho #include <stdlib.h>
40da35d433Sho #include <string.h>
41da35d433Sho #include <unistd.h>
42b9fc9a72Sderaadt #include <limits.h>
43da35d433Sho
44ea1948caSho #include <regex.h>
45ea1948caSho #include <keynote.h>
46ea1948caSho
47ea1948caSho #include "conf.h"
48da35d433Sho #include "log.h"
49da35d433Sho #include "monitor.h"
50ea1948caSho #include "policy.h"
51b09056b7Shshoexer #include "ui.h"
52ea1948caSho #include "util.h"
531ec19420Shshoexer #include "pf_key_v2.h"
54da35d433Sho
55fb9475d6Sderaadt struct monitor_state {
56da35d433Sho pid_t pid;
57da35d433Sho int s;
58b9fc9a72Sderaadt char root[PATH_MAX];
59da35d433Sho } m_state;
60da35d433Sho
61e8a27e95Smoritz extern char *pid_file;
6231516eecShshoexer
63b8909177Smoritz extern void set_slave_signals(void);
64b8909177Smoritz
65da35d433Sho /* Private functions. */
666c5cc4d7Smoritz static void must_read(void *, size_t);
676c5cc4d7Smoritz static void must_write(const void *, size_t);
68da35d433Sho
696c5cc4d7Smoritz static void m_priv_getfd(void);
706c5cc4d7Smoritz static void m_priv_setsockopt(void);
7177033115Smoritz static void m_priv_req_readdir(void);
726c5cc4d7Smoritz static void m_priv_bind(void);
736c5cc4d7Smoritz static void m_priv_pfkey_open(void);
74811d2842Sbluhm static int m_priv_local_sanitize_path(const char *, size_t, int);
7531516eecShshoexer static int m_priv_check_sockopt(int, int);
7631516eecShshoexer static int m_priv_check_bind(const struct sockaddr *, socklen_t);
77ea1948caSho
78b8909177Smoritz static void set_monitor_signals(void);
79b8909177Smoritz static void sig_pass_to_chld(int);
80b8909177Smoritz
81da35d433Sho /*
82da35d433Sho * Public functions, unprivileged.
83da35d433Sho */
84da35d433Sho
85da35d433Sho /* Setup monitor context, fork, drop child privs. */
86da35d433Sho pid_t
monitor_init(int debug)87db6e54dbSho monitor_init(int debug)
88da35d433Sho {
89da35d433Sho struct passwd *pw;
90da35d433Sho int p[2];
91fb9475d6Sderaadt
920dc10397Shshoexer bzero(&m_state, sizeof m_state);
93da35d433Sho
94da35d433Sho if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) != 0)
95da35d433Sho log_fatal("monitor_init: socketpair() failed");
96da35d433Sho
97da35d433Sho pw = getpwnam(ISAKMPD_PRIVSEP_USER);
98da35d433Sho if (pw == NULL)
995695480dShshoexer log_fatalx("monitor_init: getpwnam(\"%s\") failed",
100da35d433Sho ISAKMPD_PRIVSEP_USER);
1015695480dShshoexer strlcpy(m_state.root, pw->pw_dir, sizeof m_state.root);
102da35d433Sho
103b8909177Smoritz set_monitor_signals();
104da35d433Sho m_state.pid = fork();
105da35d433Sho
1065695480dShshoexer if (m_state.pid == -1)
10737b1005cSmoritz log_fatal("monitor_init: fork of unprivileged child failed");
1085695480dShshoexer if (m_state.pid == 0) {
1095695480dShshoexer /* The child process drops privileges. */
110b8909177Smoritz set_slave_signals();
1115695480dShshoexer
112c4a356f1Shshoexer if (chroot(pw->pw_dir) != 0 || chdir("/") != 0)
113c4a356f1Shshoexer log_fatal("monitor_init: chroot failed");
114da35d433Sho
1155695480dShshoexer if (setgroups(1, &pw->pw_gid) == -1 ||
1165695480dShshoexer setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
1175695480dShshoexer setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
1185695480dShshoexer log_fatal("monitor_init: can't drop privileges");
1195695480dShshoexer
1205695480dShshoexer m_state.s = p[0];
1215695480dShshoexer close(p[1]);
122da35d433Sho
123da35d433Sho LOG_DBG((LOG_MISC, 10,
124da35d433Sho "monitor_init: privileges dropped for child process"));
125fb9475d6Sderaadt } else {
1265695480dShshoexer /* Privileged monitor. */
127da35d433Sho setproctitle("monitor [priv]");
1285695480dShshoexer
1295695480dShshoexer m_state.s = p[1];
1305695480dShshoexer close(p[0]);
131da35d433Sho }
132da35d433Sho
133db6e54dbSho /* With "-dd", stop and wait here. For gdb "attach" etc. */
134db6e54dbSho if (debug > 1) {
135db6e54dbSho log_print("monitor_init: stopped %s PID %d fd %d%s",
136db6e54dbSho m_state.pid ? "priv" : "child", getpid(), m_state.s,
137db6e54dbSho m_state.pid ? ", waiting for SIGCONT" : "");
138db6e54dbSho kill(getpid(), SIGSTOP); /* Wait here for SIGCONT. */
139db6e54dbSho if (m_state.pid)
140db6e54dbSho kill(m_state.pid, SIGCONT); /* Continue child. */
141db6e54dbSho }
142db6e54dbSho
143da35d433Sho return m_state.pid;
144da35d433Sho }
145da35d433Sho
146b09056b7Shshoexer void
monitor_exit(int code)147b09056b7Shshoexer monitor_exit(int code)
148b09056b7Shshoexer {
149356e1c1cSyasuoka int status = 0, gotstatus = 0;
150e8a27e95Smoritz pid_t pid;
151e8a27e95Smoritz
152e8a27e95Smoritz if (m_state.pid != 0) {
153e8a27e95Smoritz /* When called from the monitor, kill slave and wait for it */
154e8a27e95Smoritz kill(m_state.pid, SIGTERM);
155e8a27e95Smoritz
156e8a27e95Smoritz do {
157e8a27e95Smoritz pid = waitpid(m_state.pid, &status, 0);
158e8a27e95Smoritz } while (pid == -1 && errno == EINTR);
159356e1c1cSyasuoka if (pid != -1)
160356e1c1cSyasuoka gotstatus = 1;
161e8a27e95Smoritz
162e8a27e95Smoritz /* Remove FIFO and pid files. */
163e8a27e95Smoritz unlink(ui_fifo);
164e8a27e95Smoritz unlink(pid_file);
165e8a27e95Smoritz }
166b09056b7Shshoexer
167366b3c74Shshoexer close(m_state.s);
168356e1c1cSyasuoka if (code == 0 && gotstatus)
169356e1c1cSyasuoka exit(WIFEXITED(status)? WEXITSTATUS(status) : 1);
170356e1c1cSyasuoka else
171b09056b7Shshoexer exit(code);
172b09056b7Shshoexer }
173b09056b7Shshoexer
174da35d433Sho int
monitor_pf_key_v2_open(void)1751ec19420Shshoexer monitor_pf_key_v2_open(void)
1761ec19420Shshoexer {
177ca544a24Shshoexer int err, cmd;
1781ec19420Shshoexer
179ca544a24Shshoexer cmd = MONITOR_PFKEY_OPEN;
1806c5cc4d7Smoritz must_write(&cmd, sizeof cmd);
1811ec19420Shshoexer
1826c5cc4d7Smoritz must_read(&err, sizeof err);
1831ec19420Shshoexer if (err < 0) {
1841ec19420Shshoexer log_error("monitor_pf_key_v2_open: parent could not create "
1851ec19420Shshoexer "PF_KEY socket");
1861ec19420Shshoexer return -1;
1871ec19420Shshoexer }
1881ec19420Shshoexer pf_key_v2_socket = mm_receive_fd(m_state.s);
1891ec19420Shshoexer if (pf_key_v2_socket < 0) {
1906eb09906Smoritz log_error("monitor_pf_key_v2_open: mm_receive_fd() failed");
1911ec19420Shshoexer return -1;
1921ec19420Shshoexer }
1931ec19420Shshoexer
194ca544a24Shshoexer return pf_key_v2_socket;
1951ec19420Shshoexer }
1961ec19420Shshoexer
1971ec19420Shshoexer int
monitor_open(const char * path,int flags,mode_t mode)198da35d433Sho monitor_open(const char *path, int flags, mode_t mode)
199da35d433Sho {
200a8f2fa08Scloder size_t len;
201a8f2fa08Scloder int fd, err, cmd;
202b9fc9a72Sderaadt char pathreal[PATH_MAX];
203da35d433Sho
204da35d433Sho if (path[0] == '/')
20525337b8eShshoexer strlcpy(pathreal, path, sizeof pathreal);
206da35d433Sho else
20725337b8eShshoexer snprintf(pathreal, sizeof pathreal, "%s/%s", m_state.root,
20812f43dabShshoexer path);
209da35d433Sho
210ca544a24Shshoexer cmd = MONITOR_GET_FD;
2116c5cc4d7Smoritz must_write(&cmd, sizeof cmd);
212da35d433Sho
213d0f6fb84Smoritz len = strlen(pathreal);
2146c5cc4d7Smoritz must_write(&len, sizeof len);
2156c5cc4d7Smoritz must_write(&pathreal, len);
216da35d433Sho
2176c5cc4d7Smoritz must_write(&flags, sizeof flags);
2186c5cc4d7Smoritz must_write(&mode, sizeof mode);
219da35d433Sho
2206c5cc4d7Smoritz must_read(&err, sizeof err);
221fb9475d6Sderaadt if (err != 0) {
222ca544a24Shshoexer errno = err;
22331516eecShshoexer return -1;
22431516eecShshoexer }
225ca544a24Shshoexer
226da35d433Sho fd = mm_receive_fd(m_state.s);
227fb9475d6Sderaadt if (fd < 0) {
228aff085cdSmoritz log_error("monitor_open: mm_receive_fd () failed");
229da35d433Sho return -1;
230da35d433Sho }
231da35d433Sho
232ca544a24Shshoexer return fd;
233da35d433Sho }
234da35d433Sho
235da35d433Sho FILE *
monitor_fopen(const char * path,const char * mode)236da35d433Sho monitor_fopen(const char *path, const char *mode)
237da35d433Sho {
238da35d433Sho FILE *fp;
239be6714aaSho int fd, flags = 0, saved_errno;
240be6714aaSho mode_t mask, cur_umask;
241da35d433Sho
242da35d433Sho /* Only the child process is supposed to run this. */
243da35d433Sho if (m_state.pid)
244da35d433Sho log_fatal("[priv] bad call to monitor_fopen");
245da35d433Sho
246fb9475d6Sderaadt switch (mode[0]) {
247da35d433Sho case 'r':
248da35d433Sho flags = (mode[1] == '+' ? O_RDWR : O_RDONLY);
249da35d433Sho break;
250da35d433Sho case 'w':
25112f43dabShshoexer flags = (mode[1] == '+' ? O_RDWR : O_WRONLY) | O_CREAT |
25212f43dabShshoexer O_TRUNC;
253da35d433Sho break;
254da35d433Sho case 'a':
25512f43dabShshoexer flags = (mode[1] == '+' ? O_RDWR : O_WRONLY) | O_CREAT |
25612f43dabShshoexer O_APPEND;
257da35d433Sho break;
258da35d433Sho default:
259da35d433Sho log_fatal("monitor_fopen: bad call");
260da35d433Sho }
261da35d433Sho
262be6714aaSho cur_umask = umask(0);
263be6714aaSho (void)umask(cur_umask);
26431516eecShshoexer mask = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
265be6714aaSho mask &= ~cur_umask;
26631516eecShshoexer
26731516eecShshoexer fd = monitor_open(path, flags, mask);
268da35d433Sho if (fd < 0)
269da35d433Sho return NULL;
270da35d433Sho
271da35d433Sho /* Got the fd, attach a FILE * to it. */
272da35d433Sho fp = fdopen(fd, mode);
273fb9475d6Sderaadt if (!fp) {
274da35d433Sho log_error("monitor_fopen: fdopen() failed");
27531516eecShshoexer saved_errno = errno;
276da35d433Sho close(fd);
27731516eecShshoexer errno = saved_errno;
278da35d433Sho return NULL;
279da35d433Sho }
280da35d433Sho return fp;
281da35d433Sho }
282da35d433Sho
283da35d433Sho int
monitor_stat(const char * path,struct stat * sb)284da35d433Sho monitor_stat(const char *path, struct stat *sb)
285da35d433Sho {
286da35d433Sho int fd, r, saved_errno;
287da35d433Sho
28831516eecShshoexer /* O_NONBLOCK is needed for stat'ing fifos. */
28931516eecShshoexer fd = monitor_open(path, O_RDONLY | O_NONBLOCK, 0);
290da35d433Sho if (fd < 0)
291da35d433Sho return -1;
292da35d433Sho
293da35d433Sho r = fstat(fd, sb);
294da35d433Sho saved_errno = errno;
295ea1948caSho close(fd);
296da35d433Sho errno = saved_errno;
297da35d433Sho return r;
298da35d433Sho }
299da35d433Sho
300da35d433Sho int
monitor_setsockopt(int s,int level,int optname,const void * optval,socklen_t optlen)301da35d433Sho monitor_setsockopt(int s, int level, int optname, const void *optval,
302da35d433Sho socklen_t optlen)
303da35d433Sho {
304ca544a24Shshoexer int ret, err, cmd;
305da35d433Sho
306ca544a24Shshoexer cmd = MONITOR_SETSOCKOPT;
3076c5cc4d7Smoritz must_write(&cmd, sizeof cmd);
308be428636Shshoexer if (mm_send_fd(m_state.s, s)) {
309be428636Shshoexer log_print("monitor_setsockopt: read/write error");
310be428636Shshoexer return -1;
311be428636Shshoexer }
312da35d433Sho
3136c5cc4d7Smoritz must_write(&level, sizeof level);
3146c5cc4d7Smoritz must_write(&optname, sizeof optname);
3156c5cc4d7Smoritz must_write(&optlen, sizeof optlen);
316a8f2fa08Scloder must_write(optval, optlen);
317da35d433Sho
3186c5cc4d7Smoritz must_read(&err, sizeof err);
3196c5cc4d7Smoritz must_read(&ret, sizeof ret);
32031516eecShshoexer if (err != 0)
321ca544a24Shshoexer errno = err;
322ca544a24Shshoexer return ret;
323da35d433Sho }
324da35d433Sho
325da35d433Sho int
monitor_bind(int s,const struct sockaddr * name,socklen_t namelen)326da35d433Sho monitor_bind(int s, const struct sockaddr *name, socklen_t namelen)
327da35d433Sho {
328ca544a24Shshoexer int ret, err, cmd;
329da35d433Sho
330ca544a24Shshoexer cmd = MONITOR_BIND;
3316c5cc4d7Smoritz must_write(&cmd, sizeof cmd);
332be428636Shshoexer if (mm_send_fd(m_state.s, s)) {
333be428636Shshoexer log_print("monitor_bind: read/write error");
334be428636Shshoexer return -1;
335be428636Shshoexer }
336da35d433Sho
3376c5cc4d7Smoritz must_write(&namelen, sizeof namelen);
338a8f2fa08Scloder must_write(name, namelen);
339da35d433Sho
3406c5cc4d7Smoritz must_read(&err, sizeof err);
3416c5cc4d7Smoritz must_read(&ret, sizeof ret);
34231516eecShshoexer if (err != 0)
343ca544a24Shshoexer errno = err;
344ca544a24Shshoexer return ret;
345da35d433Sho }
346da35d433Sho
34777033115Smoritz int
monitor_req_readdir(const char * filename)34877033115Smoritz monitor_req_readdir(const char *filename)
34931516eecShshoexer {
35077033115Smoritz int cmd, err;
35177033115Smoritz size_t len;
35231516eecShshoexer
35377033115Smoritz cmd = MONITOR_REQ_READDIR;
35477033115Smoritz must_write(&cmd, sizeof cmd);
35531516eecShshoexer
35677033115Smoritz len = strlen(filename);
35777033115Smoritz must_write(&len, sizeof len);
35877033115Smoritz must_write(filename, len);
35931516eecShshoexer
36077033115Smoritz must_read(&err, sizeof err);
36177033115Smoritz if (err == -1)
36277033115Smoritz must_read(&errno, sizeof errno);
36377033115Smoritz
364151ead9aSho return err;
36531516eecShshoexer }
36631516eecShshoexer
36777033115Smoritz int
monitor_readdir(char * file,size_t size)36877033115Smoritz monitor_readdir(char *file, size_t size)
36931516eecShshoexer {
37077033115Smoritz int fd;
37177033115Smoritz size_t len;
37231516eecShshoexer
37377033115Smoritz must_read(&len, sizeof len);
37477033115Smoritz if (len == 0)
37577033115Smoritz return -1;
37677033115Smoritz if (len >= size)
37777033115Smoritz log_fatal("monitor_readdir: received bad length from monitor");
37877033115Smoritz must_read(file, len);
37977033115Smoritz file[len] = '\0';
38077033115Smoritz fd = mm_receive_fd(m_state.s);
38177033115Smoritz return fd;
38231516eecShshoexer }
38331516eecShshoexer
38431516eecShshoexer void
monitor_init_done(void)38531516eecShshoexer monitor_init_done(void)
38631516eecShshoexer {
387ca544a24Shshoexer int cmd;
388ca544a24Shshoexer
389ca544a24Shshoexer cmd = MONITOR_INIT_DONE;
3906c5cc4d7Smoritz must_write(&cmd, sizeof cmd);
39131516eecShshoexer }
39231516eecShshoexer
393da35d433Sho /*
394da35d433Sho * Start of code running with privileges (the monitor process).
395da35d433Sho */
396da35d433Sho
397b8909177Smoritz static void
set_monitor_signals(void)398b8909177Smoritz set_monitor_signals(void)
399b8909177Smoritz {
400b8909177Smoritz int n;
401b8909177Smoritz
40208b252c1Smoritz for (n = 1; n < _NSIG; n++)
403b8909177Smoritz signal(n, SIG_DFL);
404b8909177Smoritz
405b8909177Smoritz /* Forward some signals to the child. */
406b8909177Smoritz signal(SIGTERM, sig_pass_to_chld);
407b8909177Smoritz signal(SIGHUP, sig_pass_to_chld);
408b8909177Smoritz signal(SIGUSR1, sig_pass_to_chld);
409b8909177Smoritz }
410b8909177Smoritz
41131516eecShshoexer static void
sig_pass_to_chld(int sig)41231516eecShshoexer sig_pass_to_chld(int sig)
41331516eecShshoexer {
41431516eecShshoexer int oerrno = errno;
41531516eecShshoexer
4164d4ad54fSmoritz if (m_state.pid > 0)
41731516eecShshoexer kill(m_state.pid, sig);
41831516eecShshoexer errno = oerrno;
41931516eecShshoexer }
42031516eecShshoexer
421da35d433Sho /* This function is where the privileged process waits(loops) indefinitely. */
422da35d433Sho void
monitor_loop(int debug)423db6e54dbSho monitor_loop(int debug)
424da35d433Sho {
425e8a27e95Smoritz int msgcode;
426da35d433Sho
427db6e54dbSho if (!debug)
428da35d433Sho log_to(0);
429da35d433Sho
430f952d032Shshoexer for (;;) {
4316c5cc4d7Smoritz must_read(&msgcode, sizeof msgcode);
432da35d433Sho
433fb9475d6Sderaadt switch (msgcode) {
434da35d433Sho case MONITOR_GET_FD:
4356c5cc4d7Smoritz m_priv_getfd();
436da35d433Sho break;
437da35d433Sho
4381ec19420Shshoexer case MONITOR_PFKEY_OPEN:
4391ec19420Shshoexer LOG_DBG((LOG_MISC, 80,
440097b81a0Shshoexer "monitor_loop: MONITOR_PFKEY_OPEN"));
4416c5cc4d7Smoritz m_priv_pfkey_open();
4421ec19420Shshoexer break;
4431ec19420Shshoexer
444da35d433Sho case MONITOR_SETSOCKOPT:
44550eea14cSho LOG_DBG((LOG_MISC, 80,
446097b81a0Shshoexer "monitor_loop: MONITOR_SETSOCKOPT"));
4476c5cc4d7Smoritz m_priv_setsockopt();
448da35d433Sho break;
449da35d433Sho
450da35d433Sho case MONITOR_BIND:
45150eea14cSho LOG_DBG((LOG_MISC, 80,
452097b81a0Shshoexer "monitor_loop: MONITOR_BIND"));
4536c5cc4d7Smoritz m_priv_bind();
454da35d433Sho break;
455da35d433Sho
45677033115Smoritz case MONITOR_REQ_READDIR:
45777033115Smoritz LOG_DBG((LOG_MISC, 80,
45877033115Smoritz "monitor_loop: MONITOR_REQ_READDIR"));
45977033115Smoritz m_priv_req_readdir();
46077033115Smoritz break;
46177033115Smoritz
46231516eecShshoexer case MONITOR_INIT_DONE:
46350eea14cSho LOG_DBG((LOG_MISC, 80,
464097b81a0Shshoexer "monitor_loop: MONITOR_INIT_DONE"));
46531516eecShshoexer break;
46631516eecShshoexer
467da35d433Sho case MONITOR_SHUTDOWN:
46850eea14cSho LOG_DBG((LOG_MISC, 80,
469097b81a0Shshoexer "monitor_loop: MONITOR_SHUTDOWN"));
470da35d433Sho break;
471da35d433Sho
472da35d433Sho default:
473097b81a0Shshoexer log_print("monitor_loop: got unknown code %d",
47450eea14cSho msgcode);
475da35d433Sho }
476da35d433Sho }
477da35d433Sho
478da35d433Sho exit(0);
479da35d433Sho }
480da35d433Sho
481b09056b7Shshoexer
482b09056b7Shshoexer /* Privileged: called by monitor_loop. */
483b09056b7Shshoexer static void
m_priv_pfkey_open(void)4846c5cc4d7Smoritz m_priv_pfkey_open(void)
4851ec19420Shshoexer {
486ca544a24Shshoexer int fd, err = 0;
4871ec19420Shshoexer
4881ec19420Shshoexer fd = pf_key_v2_open();
4891ec19420Shshoexer if (fd < 0)
4901ec19420Shshoexer err = -1;
4911ec19420Shshoexer
4926c5cc4d7Smoritz must_write(&err, sizeof err);
4931ec19420Shshoexer
4946c5cc4d7Smoritz if (fd > 0 && mm_send_fd(m_state.s, fd)) {
495be428636Shshoexer log_error("m_priv_pfkey_open: read/write operation failed");
4961ec19420Shshoexer close(fd);
497be428636Shshoexer return;
4981ec19420Shshoexer }
4991ec19420Shshoexer close(fd);
5001ec19420Shshoexer }
5011ec19420Shshoexer
5021ec19420Shshoexer /* Privileged: called by monitor_loop. */
5031ec19420Shshoexer static void
m_priv_getfd(void)5046c5cc4d7Smoritz m_priv_getfd(void)
505da35d433Sho {
506b9fc9a72Sderaadt char path[PATH_MAX];
507a8f2fa08Scloder size_t len;
508e8022babShshoexer int v, flags, ret;
509ca544a24Shshoexer int err = 0;
510da35d433Sho mode_t mode;
511da35d433Sho
5126c5cc4d7Smoritz must_read(&len, sizeof len);
513a8f2fa08Scloder if (len == 0 || len >= sizeof path)
514ca544a24Shshoexer log_fatal("m_priv_getfd: invalid pathname length");
515da35d433Sho
5166c5cc4d7Smoritz must_read(path, len);
517d0f6fb84Smoritz path[len] = '\0';
518882b2dfeScloder if (strlen(path) != len)
519882b2dfeScloder log_fatal("m_priv_getfd: invalid pathname");
520882b2dfeScloder
5216c5cc4d7Smoritz must_read(&flags, sizeof flags);
5226c5cc4d7Smoritz must_read(&mode, sizeof mode);
523da35d433Sho
524e8022babShshoexer if ((ret = m_priv_local_sanitize_path(path, sizeof path, flags))
525811d2842Sbluhm != 0) {
526811d2842Sbluhm if (errno != ENOENT)
527bef23cb1Smarkus log_print("m_priv_getfd: illegal path \"%s\"", path);
528f9fe46d2Sbluhm err = errno;
52931516eecShshoexer v = -1;
530fb9475d6Sderaadt } else {
531aff085cdSmoritz if ((v = open(path, flags, mode)) == -1)
532ca544a24Shshoexer err = errno;
53331516eecShshoexer }
53431516eecShshoexer
5356c5cc4d7Smoritz must_write(&err, sizeof err);
53631516eecShshoexer
537aff085cdSmoritz if (v != -1) {
538aff085cdSmoritz if (mm_send_fd(m_state.s, v) == -1)
539aff085cdSmoritz log_error("m_priv_getfd: sending fd failed");
540ea1948caSho close(v);
541be428636Shshoexer }
542da35d433Sho }
543da35d433Sho
544da35d433Sho /* Privileged: called by monitor_loop. */
54531516eecShshoexer static void
m_priv_setsockopt(void)5466c5cc4d7Smoritz m_priv_setsockopt(void)
547da35d433Sho {
548ca544a24Shshoexer int sock, level, optname, v;
549ca544a24Shshoexer int err = 0;
550da35d433Sho char *optval = 0;
551da35d433Sho socklen_t optlen;
552da35d433Sho
5536c5cc4d7Smoritz sock = mm_receive_fd(m_state.s);
554a4e97817Shshoexer if (sock < 0) {
555a4e97817Shshoexer log_print("m_priv_setsockopt: read/write error");
556a4e97817Shshoexer return;
557a4e97817Shshoexer }
558da35d433Sho
5596c5cc4d7Smoritz must_read(&level, sizeof level);
5606c5cc4d7Smoritz must_read(&optname, sizeof optname);
5616c5cc4d7Smoritz must_read(&optlen, sizeof optlen);
562da35d433Sho
5635ae94ef8Sderaadt optval = malloc(optlen);
564a4e97817Shshoexer if (!optval) {
565a4e97817Shshoexer log_print("m_priv_setsockopt: malloc failed");
566a4e97817Shshoexer close(sock);
567a4e97817Shshoexer return;
568a4e97817Shshoexer }
569da35d433Sho
570a8f2fa08Scloder must_read(optval, optlen);
571da35d433Sho
572fb9475d6Sderaadt if (m_priv_check_sockopt(level, optname) != 0) {
57331516eecShshoexer err = EACCES;
57431516eecShshoexer v = -1;
575fb9475d6Sderaadt } else {
576ca544a24Shshoexer v = setsockopt(sock, level, optname, optval, optlen);
57731516eecShshoexer if (v < 0)
578ca544a24Shshoexer err = errno;
57931516eecShshoexer }
58031516eecShshoexer
581ea1948caSho close(sock);
582fcdca22eStedu sock = -1;
58331516eecShshoexer
5846c5cc4d7Smoritz must_write(&err, sizeof err);
5856c5cc4d7Smoritz must_write(&v, sizeof v);
586da35d433Sho
587da35d433Sho free(optval);
588da35d433Sho return;
589da35d433Sho }
590da35d433Sho
591da35d433Sho /* Privileged: called by monitor_loop. */
59231516eecShshoexer static void
m_priv_bind(void)5936c5cc4d7Smoritz m_priv_bind(void)
594da35d433Sho {
595ca544a24Shshoexer int sock, v, err = 0;
596da35d433Sho struct sockaddr *name = 0;
597da35d433Sho socklen_t namelen;
598da35d433Sho
5996c5cc4d7Smoritz sock = mm_receive_fd(m_state.s);
600a4e97817Shshoexer if (sock < 0) {
601a4e97817Shshoexer log_print("m_priv_bind: read/write error");
602a4e97817Shshoexer return;
603a4e97817Shshoexer }
604da35d433Sho
605a8f2fa08Scloder must_read(&namelen, sizeof namelen);
6065ae94ef8Sderaadt name = malloc(namelen);
607a4e97817Shshoexer if (!name) {
608a4e97817Shshoexer log_print("m_priv_bind: malloc failed");
609a4e97817Shshoexer close(sock);
610a4e97817Shshoexer return;
611a4e97817Shshoexer }
612a8f2fa08Scloder must_read((char *)name, namelen);
613da35d433Sho
614fb9475d6Sderaadt if (m_priv_check_bind(name, namelen) != 0) {
61531516eecShshoexer err = EACCES;
61631516eecShshoexer v = -1;
617fb9475d6Sderaadt } else {
618ca544a24Shshoexer v = bind(sock, name, namelen);
619df69c215Sderaadt if (v == -1) {
620ea1948caSho log_error("m_priv_bind: bind(%d,%p,%d) returned %d",
621ea1948caSho sock, name, namelen, v);
622ca544a24Shshoexer err = errno;
62331516eecShshoexer }
62431516eecShshoexer }
625ea1948caSho
626ea1948caSho close(sock);
627fcdca22eStedu sock = -1;
62831516eecShshoexer
6296c5cc4d7Smoritz must_write(&err, sizeof err);
6306c5cc4d7Smoritz must_write(&v, sizeof v);
631da35d433Sho
632da35d433Sho free(name);
633da35d433Sho return;
634da35d433Sho }
635da35d433Sho
636da35d433Sho /*
637da35d433Sho * Help functions, used by both privileged and unprivileged code
638da35d433Sho */
639da35d433Sho
640ca544a24Shshoexer /*
641ca544a24Shshoexer * Read data with the assertion that it all must come through, or else abort
642ca544a24Shshoexer * the process. Based on atomicio() from openssh.
643ca544a24Shshoexer */
644ca544a24Shshoexer static void
must_read(void * buf,size_t n)6456c5cc4d7Smoritz must_read(void *buf, size_t n)
646da35d433Sho {
647ca544a24Shshoexer char *s = buf;
648ca544a24Shshoexer size_t pos = 0;
649ca544a24Shshoexer ssize_t res;
65012f43dabShshoexer
651ca544a24Shshoexer while (n > pos) {
6526c5cc4d7Smoritz res = read(m_state.s, s + pos, n - pos);
653ca544a24Shshoexer switch (res) {
654ca544a24Shshoexer case -1:
655ca544a24Shshoexer if (errno == EINTR || errno == EAGAIN)
656ca544a24Shshoexer continue;
657ca544a24Shshoexer case 0:
658e8a27e95Smoritz monitor_exit(0);
659ca544a24Shshoexer default:
660ca544a24Shshoexer pos += res;
661ca544a24Shshoexer }
662ca544a24Shshoexer }
663da35d433Sho }
664da35d433Sho
665ca544a24Shshoexer /*
666ca544a24Shshoexer * Write data with the assertion that it all has to be written, or else abort
667ca544a24Shshoexer * the process. Based on atomicio() from openssh.
668ca544a24Shshoexer */
669ca544a24Shshoexer static void
must_write(const void * buf,size_t n)6706c5cc4d7Smoritz must_write(const void *buf, size_t n)
671da35d433Sho {
672ca544a24Shshoexer const char *s = buf;
673a8f2fa08Scloder size_t pos = 0;
674a8f2fa08Scloder ssize_t res;
675ca544a24Shshoexer
676ca544a24Shshoexer while (n > pos) {
6776c5cc4d7Smoritz res = write(m_state.s, s + pos, n - pos);
678ca544a24Shshoexer switch (res) {
679ca544a24Shshoexer case -1:
680ca544a24Shshoexer if (errno == EINTR || errno == EAGAIN)
681ca544a24Shshoexer continue;
682ca544a24Shshoexer case 0:
683e8a27e95Smoritz monitor_exit(0);
684ca544a24Shshoexer default:
685ca544a24Shshoexer pos += res;
686da35d433Sho }
687da35d433Sho }
688da35d433Sho }
689ea1948caSho
690112b090cSho /* Check that path/mode is permitted. */
69131516eecShshoexer static int
m_priv_local_sanitize_path(const char * path,size_t pmax,int flags)692811d2842Sbluhm m_priv_local_sanitize_path(const char *path, size_t pmax, int flags)
693112b090cSho {
694811d2842Sbluhm char new_path[PATH_MAX], var_run[PATH_MAX], *enddir;
695112b090cSho
696112b090cSho /*
69731516eecShshoexer * We only permit paths starting with
698112b090cSho * /etc/isakmpd/ (read only)
69931516eecShshoexer * /var/run/ (rw)
700112b090cSho */
701112b090cSho
702811d2842Sbluhm if (realpath(path, new_path) == NULL) {
703811d2842Sbluhm if (errno != ENOENT)
704811d2842Sbluhm return 1;
705811d2842Sbluhm /*
706811d2842Sbluhm * It is ok if the directory exists,
707811d2842Sbluhm * but the file should be created.
708811d2842Sbluhm */
709811d2842Sbluhm if (strlcpy(new_path, path, sizeof(new_path)) >=
710811d2842Sbluhm sizeof(new_path))
711811d2842Sbluhm return 1;
712811d2842Sbluhm enddir = strrchr(new_path, '/');
713811d2842Sbluhm if (enddir == NULL || enddir[1] == '\0')
714811d2842Sbluhm return 1;
715811d2842Sbluhm enddir[1] = '\0';
716811d2842Sbluhm if (realpath(new_path, new_path) == NULL) {
717811d2842Sbluhm errno = ENOENT;
718811d2842Sbluhm return 1;
719811d2842Sbluhm }
720811d2842Sbluhm enddir = strrchr(path, '/');
721811d2842Sbluhm strlcat(new_path, enddir, sizeof(new_path));
722811d2842Sbluhm }
723811d2842Sbluhm
724811d2842Sbluhm if (realpath("/var/run/", var_run) == NULL)
725f9fe46d2Sbluhm return 1;
726c7b82f9eSmillert strlcat(var_run, "/", sizeof(var_run));
727112b090cSho
728c7b82f9eSmillert if (strncmp(var_run, new_path, strlen(var_run)) == 0)
72931516eecShshoexer return 0;
7302455151dSmoritz
7312455151dSmoritz if (strncmp(ISAKMPD_ROOT, new_path, strlen(ISAKMPD_ROOT)) == 0 &&
7322455151dSmoritz (flags & O_ACCMODE) == O_RDONLY)
73331516eecShshoexer return 0;
734112b090cSho
735f9fe46d2Sbluhm errno = EACCES;
73631516eecShshoexer return 1;
737112b090cSho }
738112b090cSho
73931516eecShshoexer /* Check setsockopt */
74031516eecShshoexer static int
m_priv_check_sockopt(int level,int name)74131516eecShshoexer m_priv_check_sockopt(int level, int name)
74231516eecShshoexer {
743fb9475d6Sderaadt switch (level) {
74431516eecShshoexer /* These are allowed */
74531516eecShshoexer case SOL_SOCKET:
74631516eecShshoexer case IPPROTO_IP:
74731516eecShshoexer case IPPROTO_IPV6:
74831516eecShshoexer break;
74931516eecShshoexer
75031516eecShshoexer default:
75131516eecShshoexer log_print("m_priv_check_sockopt: Illegal level %d", level);
75231516eecShshoexer return 1;
75331516eecShshoexer }
75431516eecShshoexer
755fb9475d6Sderaadt switch (name) {
75631516eecShshoexer /* These are allowed */
75731516eecShshoexer case SO_REUSEPORT:
75831516eecShshoexer case SO_REUSEADDR:
75931516eecShshoexer case IP_AUTH_LEVEL:
76031516eecShshoexer case IP_ESP_TRANS_LEVEL:
76131516eecShshoexer case IP_ESP_NETWORK_LEVEL:
76231516eecShshoexer case IP_IPCOMP_LEVEL:
76331516eecShshoexer case IPV6_AUTH_LEVEL:
76431516eecShshoexer case IPV6_ESP_TRANS_LEVEL:
76531516eecShshoexer case IPV6_ESP_NETWORK_LEVEL:
76631516eecShshoexer case IPV6_IPCOMP_LEVEL:
76731516eecShshoexer break;
76831516eecShshoexer
76931516eecShshoexer default:
77050eea14cSho log_print("m_priv_check_sockopt: Illegal option name %d",
77150eea14cSho name);
77231516eecShshoexer return 1;
77331516eecShshoexer }
77431516eecShshoexer
77531516eecShshoexer return 0;
77631516eecShshoexer }
77731516eecShshoexer
77831516eecShshoexer /* Check bind */
77931516eecShshoexer static int
m_priv_check_bind(const struct sockaddr * sa,socklen_t salen)78031516eecShshoexer m_priv_check_bind(const struct sockaddr *sa, socklen_t salen)
78131516eecShshoexer {
78231516eecShshoexer in_port_t port;
78331516eecShshoexer
784fb9475d6Sderaadt if (sa == NULL) {
78531516eecShshoexer log_print("NULL address");
78631516eecShshoexer return 1;
78731516eecShshoexer }
788c506f982Shshoexer if (SA_LEN(sa) != salen) {
789c506f982Shshoexer log_print("Length mismatch: %lu %lu", (unsigned long)sa->sa_len,
790c506f982Shshoexer (unsigned long)salen);
79131516eecShshoexer return 1;
79231516eecShshoexer }
793fb9475d6Sderaadt switch (sa->sa_family) {
79431516eecShshoexer case AF_INET:
795fb9475d6Sderaadt if (salen != sizeof(struct sockaddr_in)) {
79631516eecShshoexer log_print("Invalid inet address length");
79731516eecShshoexer return 1;
79831516eecShshoexer }
79931516eecShshoexer port = ((const struct sockaddr_in *)sa)->sin_port;
80031516eecShshoexer break;
80131516eecShshoexer case AF_INET6:
802fb9475d6Sderaadt if (salen != sizeof(struct sockaddr_in6)) {
80331516eecShshoexer log_print("Invalid inet6 address length");
80431516eecShshoexer return 1;
80531516eecShshoexer }
80631516eecShshoexer port = ((const struct sockaddr_in6 *)sa)->sin6_port;
80731516eecShshoexer break;
80831516eecShshoexer default:
80931516eecShshoexer log_print("Unknown address family");
81031516eecShshoexer return 1;
81131516eecShshoexer }
81231516eecShshoexer
81331516eecShshoexer port = ntohs(port);
81431516eecShshoexer
815fb9475d6Sderaadt if (port != ISAKMP_PORT_DEFAULT && port < 1024) {
81631516eecShshoexer log_print("Disallowed port %u", port);
81731516eecShshoexer return 1;
81831516eecShshoexer }
81931516eecShshoexer return 0;
82031516eecShshoexer }
82131516eecShshoexer
82277033115Smoritz static void
m_priv_req_readdir(void)823*ff9f425cStb m_priv_req_readdir(void)
82477033115Smoritz {
82577033115Smoritz size_t len;
826b9fc9a72Sderaadt char path[PATH_MAX];
82777033115Smoritz DIR *dp;
82877033115Smoritz struct dirent *file;
829bef23cb1Smarkus struct stat sb;
83077033115Smoritz int off, size, fd, ret, serrno;
83177033115Smoritz
83277033115Smoritz must_read(&len, sizeof len);
83377033115Smoritz if (len == 0 || len >= sizeof path)
83477033115Smoritz log_fatal("m_priv_req_readdir: invalid pathname length");
83577033115Smoritz must_read(path, len);
83677033115Smoritz path[len] = '\0';
83777033115Smoritz if (strlen(path) != len)
83877033115Smoritz log_fatal("m_priv_req_readdir: invalid pathname");
83977033115Smoritz
84077033115Smoritz off = strlen(path);
84177033115Smoritz size = sizeof path - off;
84277033115Smoritz
84377033115Smoritz if ((dp = opendir(path)) == NULL) {
84477033115Smoritz serrno = errno;
84577033115Smoritz ret = -1;
84677033115Smoritz must_write(&ret, sizeof ret);
84777033115Smoritz must_write(&serrno, sizeof serrno);
84877033115Smoritz return;
84977033115Smoritz }
85077033115Smoritz
85177033115Smoritz /* report opendir() success */
85277033115Smoritz ret = 0;
85377033115Smoritz must_write(&ret, sizeof ret);
85477033115Smoritz
85577033115Smoritz while ((file = readdir(dp)) != NULL) {
85677033115Smoritz strlcpy(path + off, file->d_name, size);
85777033115Smoritz
858280ff4feSmoritz if (m_priv_local_sanitize_path(path, sizeof path, O_RDONLY)
859bef23cb1Smarkus != 0)
860280ff4feSmoritz continue;
861b7041c07Sderaadt fd = open(path, O_RDONLY);
86277033115Smoritz if (fd == -1) {
86377033115Smoritz log_error("m_priv_req_readdir: open "
86477033115Smoritz "(\"%s\", O_RDONLY, 0) failed", path);
86577033115Smoritz continue;
86677033115Smoritz }
867bef23cb1Smarkus if ((fstat(fd, &sb) == -1) ||
868bef23cb1Smarkus !(S_ISREG(sb.st_mode) || S_ISLNK(sb.st_mode))) {
869bef23cb1Smarkus close(fd);
870bef23cb1Smarkus continue;
871bef23cb1Smarkus }
87277033115Smoritz
87377033115Smoritz len = strlen(path);
87477033115Smoritz must_write(&len, sizeof len);
87577033115Smoritz must_write(path, len);
87677033115Smoritz
87777033115Smoritz mm_send_fd(m_state.s, fd);
87877033115Smoritz close(fd);
87977033115Smoritz }
88077033115Smoritz closedir(dp);
88177033115Smoritz
88277033115Smoritz len = 0;
88377033115Smoritz must_write(&len, sizeof len);
88477033115Smoritz }
885