diff options
author | YASUOKA Masahiko <yasuoka@cvs.openbsd.org> | 2010-01-31 05:49:52 +0000 |
---|---|---|
committer | YASUOKA Masahiko <yasuoka@cvs.openbsd.org> | 2010-01-31 05:49:52 +0000 |
commit | fceb6db87ac8ef9ad4b6724b1c49236e55988205 (patch) | |
tree | 82790a5a1cce47b6128d0f5239f9afa904ef5e7f /usr.sbin/npppd | |
parent | 0d8a83a403b2418bd67b9c3071d38eee519335e9 (diff) |
privilege separation of npppd.
- Drop privilege after daemon initializing.
- Some system calls that requires root privileges were replaced to
wrapper functions that communicate with a separated privileged
process via IPC. And the privileged process checks whether the
operations are acceptable.
Diffstat (limited to 'usr.sbin/npppd')
-rw-r--r-- | usr.sbin/npppd/npppd/Makefile | 1 | ||||
-rw-r--r-- | usr.sbin/npppd/npppd/npppd.c | 31 | ||||
-rw-r--r-- | usr.sbin/npppd/npppd/npppd.h | 1 | ||||
-rw-r--r-- | usr.sbin/npppd/npppd/npppd_auth.c | 4 | ||||
-rw-r--r-- | usr.sbin/npppd/npppd/npppd_config.c | 4 | ||||
-rw-r--r-- | usr.sbin/npppd/npppd/npppd_ctl.c | 10 | ||||
-rw-r--r-- | usr.sbin/npppd/npppd/npppd_defs.h | 3 | ||||
-rw-r--r-- | usr.sbin/npppd/npppd/npppd_local.h | 2 | ||||
-rw-r--r-- | usr.sbin/npppd/npppd/npppd_subr.c | 9 | ||||
-rw-r--r-- | usr.sbin/npppd/npppd/privsep.c | 582 | ||||
-rw-r--r-- | usr.sbin/npppd/npppd/privsep.h | 39 | ||||
-rw-r--r-- | usr.sbin/npppd/pppoe/pppoed.c | 5 | ||||
-rw-r--r-- | usr.sbin/npppd/pptp/pptpd.c | 5 |
13 files changed, 676 insertions, 20 deletions
diff --git a/usr.sbin/npppd/npppd/Makefile b/usr.sbin/npppd/npppd/Makefile index 577df4095dd..6083dfd8a70 100644 --- a/usr.sbin/npppd/npppd/Makefile +++ b/usr.sbin/npppd/npppd/Makefile @@ -14,6 +14,7 @@ SRCS+= rtev_libevent.c bytebuf.c debugutil.c csvreader.c net_utils.c SRCS+= radish.c time_utils.c npppd_pool.c addr_range.c SRCS+= radius+.cc SRCS+= recvfromto.c +SRCS+= privsep.c #SRCS+= ipsec_util.c CPPFLAGS+= -DUSE_NPPPD_NPPPD_CTL=1 diff --git a/usr.sbin/npppd/npppd/npppd.c b/usr.sbin/npppd/npppd/npppd.c index 9aaf068fe6d..edfe3e8007e 100644 --- a/usr.sbin/npppd/npppd/npppd.c +++ b/usr.sbin/npppd/npppd/npppd.c @@ -27,7 +27,7 @@ * Next pppd。npppd プロセスと npppdインスタンスの実装。 * * @author Yasuoka Masahiko - * $Id: npppd.c,v 1.2 2010/01/13 07:49:44 yasuoka Exp $ + * $Id: npppd.c,v 1.3 2010/01/31 05:49:51 yasuoka Exp $ */ #include <sys/cdefs.h> #include "version.h" @@ -66,6 +66,8 @@ __COPYRIGHT( #include <event.h> #include <errno.h> #include <ifaddrs.h> +#include <err.h> +#include <pwd.h> #include "pathnames.h" #include "debugutil.h" @@ -151,6 +153,7 @@ main(int argc, char *argv[]) int ch, retval = 0, ll_adjust = 0, runasdaemon = 0; extern char *optarg; const char *npppd_conf0 = DEFAULT_NPPPD_CONF; + struct passwd *pw; while ((ch = getopt(argc, argv, "Dc:dhs")) != -1) { switch (ch) { @@ -182,14 +185,38 @@ main(int argc, char *argv[]) daemon(0, 0); } + /* check for root privileges */ + if (geteuid()) + errx(1, "need root privileges"); + /* check for npppd user */ + if (getpwnam(NPPPD_USER) == NULL) + errx(1, "unknown user %s", NPPPD_USER); + + if (privsep_init() != 0) + err(1, "cannot drop privileges"); + if (npppd_init(&s_npppd, npppd_conf0) != 0) { retval = 1; goto reigai; } + + if ((pw = getpwnam(NPPPD_USER)) == NULL) + err(1, "gwpwnam"); + if (chroot(pw->pw_dir) == -1) + err(1, "chroot"); + if (chdir("/") == -1) + err(1, "chdir(\"/\")"); + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + err(1, "cannot drop privileges"); + /* privileges is dropped */ + npppd_start(&s_npppd); npppd_fini(&s_npppd); - // FALL THROUGH + /* FALLTHROUGH */ reigai: + privsep_fini(); log_printf(LOG_NOTICE, "Terminate npppd."); return retval; diff --git a/usr.sbin/npppd/npppd/npppd.h b/usr.sbin/npppd/npppd/npppd.h index 72b25b83a2a..11616deb786 100644 --- a/usr.sbin/npppd/npppd/npppd.h +++ b/usr.sbin/npppd/npppd/npppd.h @@ -26,6 +26,7 @@ #ifndef NPPPD_H #define NPPPD_H 1 +#define NPPPD_USER "_npppd" #ifndef NPPPD_DEFAULT_TUN_IFNAME #define NPPPD_DEFAULT_TUN_IFNAME "tun0" diff --git a/usr.sbin/npppd/npppd/npppd_auth.c b/usr.sbin/npppd/npppd/npppd_auth.c index 912841f574e..fa5ec6ef498 100644 --- a/usr.sbin/npppd/npppd/npppd_auth.c +++ b/usr.sbin/npppd/npppd/npppd_auth.c @@ -24,7 +24,7 @@ * SUCH DAMAGE. */ /**@file 認証レルム */ -/* $Id: npppd_auth.c,v 1.3 2010/01/14 23:35:39 yasuoka Exp $ */ +/* $Id: npppd_auth.c,v 1.4 2010/01/31 05:49:51 yasuoka Exp $ */ /* なるべく npppd に非依存で書いていきたいところ。*/ #include <sys/types.h> #include <sys/stat.h> @@ -475,7 +475,7 @@ npppd_auth_reload_acctlist(npppd_auth_base *base) slist_init(&users); csv = NULL; - if ((file = fopen(base->acctlist_path, "r")) == NULL) { + if ((file = priv_fopen(base->acctlist_path)) == NULL) { /* ファイルが存在しない場合は、空とする */ if (errno == ENOENT) hash_delete_all(base->users_hash, 1); diff --git a/usr.sbin/npppd/npppd/npppd_config.c b/usr.sbin/npppd/npppd/npppd_config.c index dfd0227e1b5..9c01ec2f347 100644 --- a/usr.sbin/npppd/npppd/npppd_config.c +++ b/usr.sbin/npppd/npppd/npppd_config.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -/* $Id: npppd_config.c,v 1.2 2010/01/13 07:49:44 yasuoka Exp $ */ +/* $Id: npppd_config.c,v 1.3 2010/01/31 05:49:51 yasuoka Exp $ */ /*@file * npppd 設定関連を操作する関数を格納するファイル。 */ @@ -105,7 +105,7 @@ npppd_reload_config(npppd *_this) FILE *conffp = NULL; struct properties *proptmp = NULL; - if ((conffp = fopen(_this->config_file, "r")) == NULL) { + if ((conffp = priv_fopen(_this->config_file)) == NULL) { log_printf(LOG_ERR, "Load configuration from='%s' failed: %m", _this->config_file); retval = -1; diff --git a/usr.sbin/npppd/npppd/npppd_ctl.c b/usr.sbin/npppd/npppd/npppd_ctl.c index 6126cb33527..b72a1804a25 100644 --- a/usr.sbin/npppd/npppd/npppd_ctl.c +++ b/usr.sbin/npppd/npppd/npppd_ctl.c @@ -27,7 +27,7 @@ * npppd 制御。UNIXドメインソケット /var/run/npppd_ctl を open して、 * npppdctlコマンドからのコマンドを受け付ける。 */ -/* $Id: npppd_ctl.c,v 1.2 2010/01/15 03:29:11 yasuoka Exp $ */ +/* $Id: npppd_ctl.c,v 1.3 2010/01/31 05:49:51 yasuoka Exp $ */ #include <sys/types.h> #include <sys/param.h> #include <sys/socket.h> @@ -57,6 +57,9 @@ #include "npppd_ctl.h" #include "net_utils.h" +#include "privsep.h" +#define sendto(_s, _msg, _len, _flags, _to, _tolen) \ + priv_sendto((_s), (_msg), (_len), (_flags), (_to), (_tolen)) #ifdef USE_NPPPD_PIPEX #if defined(__NetBSD__) @@ -140,13 +143,14 @@ npppd_ctl_start(npppd_ctl *_this) goto reigai; } - unlink(_this->pathname); + priv_unlink(_this->pathname); memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; sun.sun_len = sizeof(sun); strlcpy(sun.sun_path, _this->pathname, sizeof(sun.sun_path)); - if (bind(_this->sock, (struct sockaddr *)&sun, sizeof(sun)) != 0) { + if (priv_bind(_this->sock, (struct sockaddr *)&sun, sizeof(sun)) + != 0) { log_printf(LOG_ERR, "bind() failed in %s(): %m", __func__); goto reigai; } diff --git a/usr.sbin/npppd/npppd/npppd_defs.h b/usr.sbin/npppd/npppd/npppd_defs.h index 0c4e316b1b7..644e6f75d45 100644 --- a/usr.sbin/npppd/npppd/npppd_defs.h +++ b/usr.sbin/npppd/npppd/npppd_defs.h @@ -97,9 +97,6 @@ #define NPPPD_DEFAULT_IP_ASSIGN_RADIUS 0 #endif -/** rtev_write() を使う */ -#define NPPPD_USE_RTEV_WRITE 1 - #ifndef DEFAULT_RTSOCK_EVENT_DELAY /** Routing ソケットイベントを受けてから、処理を開始するまでの待ち時間(秒)*/ #define DEFAULT_RTSOCK_EVENT_DELAY 5 diff --git a/usr.sbin/npppd/npppd/npppd_local.h b/usr.sbin/npppd/npppd/npppd_local.h index 836b518a679..2a292084968 100644 --- a/usr.sbin/npppd/npppd/npppd_local.h +++ b/usr.sbin/npppd/npppd/npppd_local.h @@ -68,6 +68,8 @@ #include "npppd_iface.h" #include "npppd.h" +#include "privsep.h" + #ifdef USE_NPPPD_NPPPD_CTL typedef struct _npppd_ctl { /** イベントコンテキスト */ diff --git a/usr.sbin/npppd/npppd/npppd_subr.c b/usr.sbin/npppd/npppd/npppd_subr.c index 3427517fc65..43d2a365f15 100644 --- a/usr.sbin/npppd/npppd/npppd_subr.c +++ b/usr.sbin/npppd/npppd/npppd_subr.c @@ -26,7 +26,7 @@ /**@file * npppd の補助的な関数を提供します。 */ -/* $Id: npppd_subr.c,v 1.2 2010/01/13 07:49:44 yasuoka Exp $ */ +/* $Id: npppd_subr.c,v 1.3 2010/01/31 05:49:51 yasuoka Exp $ */ #include <sys/cdefs.h> #ifndef LINT __COPYRIGHT( @@ -68,6 +68,7 @@ __COPYRIGHT( #include <event.h> #include "rt_zebra.h" #endif +#include "privsep.h" static u_int16_t route_seq = 0; static int in_route0(int, struct in_addr *, struct in_addr *, struct in_addr *, int, const char *, uint32_t); @@ -100,7 +101,7 @@ load_resolv_conf(struct in_addr *pri, struct in_addr *sec) sec->s_addr = INADDR_NONE; filep = NULL; - if ((filep = fopen(_PATH_RESCONF, "r")) == NULL) + if ((filep = priv_fopen(_PATH_RESCONF)) == NULL) return 1; i = 0; @@ -258,7 +259,7 @@ in_route0(int type, struct in_addr *dest, struct in_addr *mask, log_printf(LOG_ERR, "rtev_write failed in %s: %m", __func__); #else - if ((sock = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) { + if ((sock = priv_socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) { log_printf(LOG_ERR, "socket() failed in %s() on %s : %m", __func__, strtype); goto reigai; @@ -277,7 +278,7 @@ in_route0(int type, struct in_addr *dest, struct in_addr *mask, goto reigai; } - if ((rval = write(sock, buf, rtm->rtm_msglen)) <= 0) { + if ((rval = priv_send(sock, buf, rtm->rtm_msglen, 0)) <= 0) { if (!(type == RTM_DELETE && errno == ESRCH) && !(type == RTM_ADD && errno == EEXIST)) { log_printf(LOG_DEBUG, diff --git a/usr.sbin/npppd/npppd/privsep.c b/usr.sbin/npppd/npppd/privsep.c new file mode 100644 index 00000000000..2fb474fb47c --- /dev/null +++ b/usr.sbin/npppd/npppd/privsep.c @@ -0,0 +1,582 @@ +/* $OpenBSD: privsep.c,v 1.1 2010/01/31 05:49:51 yasuoka Exp $ */ + +/* + * Copyright (c) 2010 Yasuoka Masahiko <yasuoka@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/time.h> +#include <sys/un.h> + +#include <netinet/in.h> + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#include "privsep.h" + +#undef nitems +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +enum PRIVSEP_CMD { + PRIVSEP_FINI, + PRIVSEP_OPEN, + PRIVSEP_SOCKET, + PRIVSEP_BIND, + PRIVSEP_SENDTO, + PRIVSEP_UNLINK +}; + +struct PRIVSEP_OPEN_ARG { + enum PRIVSEP_CMD cmd; + char path[PATH_MAX]; + int flags; + mode_t mode; +}; + +struct PRIVSEP_SOCKET_ARG { + enum PRIVSEP_CMD cmd; + int domain; + int type; + int protocol; +}; + +struct PRIVSEP_BIND_ARG { + enum PRIVSEP_CMD cmd; + struct sockaddr_storage name; + socklen_t namelen; +}; + +struct PRIVSEP_SENDTO_ARG { + enum PRIVSEP_CMD cmd; + size_t len; + int flags; + struct sockaddr_storage to; + socklen_t tolen; + u_char msg[0]; +}; + +struct PRIVSEP_UNLINK_ARG { + enum PRIVSEP_CMD cmd; + char path[PATH_MAX]; +}; + +struct PRIVSEP_COMMON_RESP { + int retval; + int rerrno; +}; + +static void privsep_priv_main (int); +static int privsep_recvfd (void); +static int privsep_common_resp (void); +static void privsep_sendfd(int, int, int); +static int privsep_npppd_check_open (struct PRIVSEP_OPEN_ARG *); +static int privsep_npppd_check_socket (struct PRIVSEP_SOCKET_ARG *); +static int privsep_npppd_check_bind (struct PRIVSEP_BIND_ARG *); +static int privsep_npppd_check_sendto (struct PRIVSEP_SENDTO_ARG *); +static int privsep_npppd_check_unlink (struct PRIVSEP_UNLINK_ARG *); + +static int privsep_sock = -1; + +int +privsep_init(void) +{ + pid_t pid; + int pairsock[2]; + + if (socketpair(AF_UNIX, SOCK_DGRAM, PF_UNSPEC, pairsock) == -1) + return -1; + if ((pid = fork()) < 0) + return -1; + + else if (pid == 0) { + setsid(); + /* privileged process */ + setproctitle("[priv]"); + close(pairsock[1]); + privsep_priv_main(pairsock[0]); + _exit(0); + /* NOTREACHED */ + } + close(pairsock[0]); + privsep_sock = pairsock[1]; + + return 0; +} + +void +privsep_fini(void) +{ + enum PRIVSEP_CMD cmd = PRIVSEP_FINI; + + if (privsep_sock >= 0) { + send(privsep_sock, &cmd, sizeof(cmd), 0); + close(privsep_sock); + privsep_sock = -1; + } +} + +/*********************************************************************** + * Functions for from jail + ***********************************************************************/ +int +priv_bind(int sock, const struct sockaddr *name, socklen_t namelen) +{ + int retval; + struct PRIVSEP_BIND_ARG a; + struct msghdr m; + struct iovec iov[1]; + struct cmsghdr *cm; + u_char cm_space[CMSG_LEN(sizeof(int))]; + + if (namelen > sizeof(a.name)) { + errno = EINVAL; + return -1; + } + memset(&m, 0, sizeof(m)); + + iov[0].iov_base = &a; + iov[0].iov_len = sizeof(a); + a.cmd = PRIVSEP_BIND; + memcpy(&a.name, name, namelen); + a.namelen = namelen; + + cm = (struct cmsghdr *)cm_space; + cm->cmsg_len = sizeof(cm_space); + cm->cmsg_level = SOL_SOCKET; + cm->cmsg_type = SCM_RIGHTS; + *(int *)CMSG_DATA(cm) = sock; + + m.msg_iov = iov; + m.msg_iovlen = nitems(iov); + m.msg_control = cm; + m.msg_controllen = sizeof(cm_space); + + if ((retval = sendmsg(privsep_sock, &m, 0)) < 0) + return retval; + + return privsep_common_resp(); +} + +int +priv_socket(int domain, int type, int protocol) +{ + int retval; + struct PRIVSEP_SOCKET_ARG a; + + a.cmd = PRIVSEP_SOCKET; + a.domain = domain; + a.type = type; + a.protocol = protocol; + if ((retval = send(privsep_sock, &a, sizeof(a), 0)) < 0) + return retval; + + return privsep_recvfd(); +} + +int +priv_open(const char *path, int flags, mode_t mode) +{ + int retval; + struct PRIVSEP_OPEN_ARG a; + + a.cmd = PRIVSEP_OPEN; + strlcpy(a.path, path, sizeof(a.path)); + a.flags = flags; + a.mode = mode; + if ((retval = send(privsep_sock, &a, sizeof(a), 0)) < 0) + return retval; + + return privsep_recvfd(); +} + +FILE * +priv_fopen(const char *path) +{ + int f; + + if ((f = priv_open(path, O_RDONLY, 0600)) < 0) + return NULL; + + return fdopen(f, "r"); +} + +int +priv_sendto(int s, const void *msg, int len, int flags, + const struct sockaddr *to, socklen_t tolen) +{ + struct PRIVSEP_SENDTO_ARG a; + struct msghdr m; + struct iovec iov[2]; + struct cmsghdr *cm; + u_char cm_space[CMSG_LEN(sizeof(int))]; + int retval; + + if (tolen > sizeof(a.to)) { + errno = EINVAL; + return -1; + } + memset(&m, 0, sizeof(m)); + + iov[0].iov_base = &a; + iov[0].iov_len = sizeof(a); + iov[1].iov_base = (void *)msg; + iov[1].iov_len = len; + + a.cmd = PRIVSEP_SENDTO; + a.len = len; + a.flags = flags; + if (tolen > 0) { + memcpy(&a.to, to, tolen); + a.tolen = tolen; + } + + cm = (struct cmsghdr *)cm_space; + cm->cmsg_len = sizeof(cm_space); + cm->cmsg_level = SOL_SOCKET; + cm->cmsg_type = SCM_RIGHTS; + *(int *)CMSG_DATA(cm) = s; + + m.msg_iov = iov; + m.msg_iovlen = nitems(iov); + m.msg_control = cm; + m.msg_controllen = sizeof(cm_space); + + if ((retval = sendmsg(privsep_sock, &m, 0)) < 0) + return retval; + + return privsep_common_resp(); +} + +int +priv_send(int s, const void *msg, int len, int flags) +{ + return priv_sendto(s, msg, len, flags, NULL, 0); +} + +int +priv_unlink(const char *path) +{ + int retval; + struct PRIVSEP_OPEN_ARG a; + + a.cmd = PRIVSEP_UNLINK; + strlcpy(a.path, path, sizeof(a.path)); + if ((retval = send(privsep_sock, &a, sizeof(a), 0)) < 0) + return retval; + + return privsep_common_resp(); +} + +static int +privsep_recvfd(void) +{ + struct PRIVSEP_COMMON_RESP r; + struct msghdr m; + struct cmsghdr *cm; + struct iovec iov[1]; + u_char cmsgbuf[256]; + + memset(&m, 0, sizeof(m)); + + iov[0].iov_base = &r; + iov[0].iov_len = sizeof(r); + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + m.msg_iov = iov; + m.msg_iovlen = nitems(iov); + m.msg_control = cmsgbuf; + m.msg_controllen = sizeof(cmsgbuf); + if (recvmsg(privsep_sock, &m, 0) < sizeof(struct PRIVSEP_COMMON_RESP)) + goto on_error; + if (r.retval < 0) { + errno = r.rerrno; + return r.retval; + } + for (cm = CMSG_FIRSTHDR(&m); m.msg_controllen != 0 && cm; + cm = CMSG_NXTHDR(&m, cm)) { + if (cm->cmsg_level == SOL_SOCKET && + cm->cmsg_type == SCM_RIGHTS && + cm->cmsg_len >= CMSG_LEN(sizeof(int))) { + return *(int *)CMSG_DATA(cm); + } else + break; + } + +on_error: + errno = EACCES; + return -1; +} + +static int +privsep_common_resp(void) +{ + struct PRIVSEP_COMMON_RESP r; + + if (recv(privsep_sock, &r, sizeof(r), 0) + < sizeof(struct PRIVSEP_COMMON_RESP)) { + errno = EACCES; + return -1; + } + if (r.retval != 0) + errno = r.rerrno; + + return r.retval; +} + +/*********************************************************************** + * privileged process + ***********************************************************************/ +static void +privsep_priv_main(int sock) +{ + int retval, fdesc; + u_char rbuf[2048], rcmsgbuf[128]; + struct iovec riov[1]; + struct msghdr rmsg; + struct cmsghdr *cm; + + for (;;) { + fdesc = -1; + + memset(&rmsg, 0, sizeof(rmsg)); + riov[0].iov_base = rbuf; + riov[0].iov_len = sizeof(rbuf); + rmsg.msg_iov = riov; + rmsg.msg_iovlen = nitems(riov); + rmsg.msg_control = rcmsgbuf; + rmsg.msg_controllen = sizeof(rcmsgbuf); + + if ((retval = recvmsg(sock, &rmsg, 0)) < + sizeof(enum PRIVSEP_CMD)) + break; + + for (cm = CMSG_FIRSTHDR(&rmsg); rmsg.msg_controllen != 0 && cm; + cm = CMSG_NXTHDR(&rmsg, cm)) { + if (cm->cmsg_level == SOL_SOCKET && + cm->cmsg_type == SCM_RIGHTS && + cm->cmsg_len >= CMSG_LEN(sizeof(int))) { + fdesc = *(int *)CMSG_DATA(cm); + } + } + + switch (*(enum PRIVSEP_CMD *)rbuf) { + case PRIVSEP_FINI: + goto loop_exit; + /* NOTREACHED */ + case PRIVSEP_OPEN: { + struct PRIVSEP_OPEN_ARG *a; + + a = (struct PRIVSEP_OPEN_ARG *)rbuf; + if (privsep_npppd_check_open(a)) + privsep_sendfd(sock, -1, EACCES); + else + privsep_sendfd(sock, + open(a->path, a->flags, a->mode), 0); + } + break; + case PRIVSEP_SOCKET: { + struct PRIVSEP_SOCKET_ARG *a; + + a = (struct PRIVSEP_SOCKET_ARG *)rbuf; + if (privsep_npppd_check_socket(a)) + privsep_sendfd(sock, -1, EACCES); + else + privsep_sendfd(sock, + socket(a->domain, a->type, a->protocol), 0); + } + break; + case PRIVSEP_UNLINK: { + struct PRIVSEP_UNLINK_ARG *a; + struct PRIVSEP_COMMON_RESP r; + + a = (struct PRIVSEP_UNLINK_ARG *)rbuf; + if (privsep_npppd_check_unlink(a)) { + r.retval = -1; + r.rerrno = EACCES; + } else { + if ((r.retval = unlink(a->path)) != 0) + r.rerrno = errno; + } + (void)send(sock, &r, sizeof(r), 0); + } + break; + case PRIVSEP_BIND: { + struct PRIVSEP_BIND_ARG *a; + struct PRIVSEP_COMMON_RESP r; + + a = (struct PRIVSEP_BIND_ARG *)rbuf; + if (fdesc < 0) { + r.rerrno = EINVAL; + r.retval = -1; + } else if (privsep_npppd_check_bind(a)) { + r.rerrno = EACCES; + r.retval = -1; + } else { + if ((r.retval = bind(fdesc, + (struct sockaddr *)&a->name, a->namelen)) + != 0) + r.rerrno = errno; + close(fdesc); + fdesc = -1; + } + (void)send(sock, &r, sizeof(r), 0); + } + break; + case PRIVSEP_SENDTO: { + struct PRIVSEP_SENDTO_ARG *a; + struct PRIVSEP_COMMON_RESP r; + + a = (struct PRIVSEP_SENDTO_ARG *)rbuf; + if (fdesc < 0) { + r.rerrno = EINVAL; + r.retval = -1; + } else if (privsep_npppd_check_sendto(a)) { + r.rerrno = EACCES; + r.retval = -1; + } else { + if (a->tolen > 0) + r.retval = sendto(fdesc, a->msg, a->len, + a->flags, (struct sockaddr *)&a->to, + a->tolen); + else + r.retval = send(fdesc, a->msg, a->len, + a->flags); + if (r.retval < 0) + r.rerrno = errno; + close(fdesc); + fdesc = -1; + } + (void)send(sock, &r, sizeof(r), 0); + } + break; + } + } +loop_exit: + + close(sock); +} + +static void +privsep_sendfd(int sock, int fdesc, int rerrno) +{ + struct PRIVSEP_COMMON_RESP r; + struct msghdr msg; + struct cmsghdr *cm; + struct iovec iov[1]; + u_char cm_space[CMSG_LEN(sizeof(int))]; + + memset(&msg, 0, sizeof(msg)); + cm = (struct cmsghdr *)cm_space; + iov[0].iov_base = &r; + iov[0].iov_len = sizeof(r); + r.retval = fdesc; + + if (fdesc < 0) { + msg.msg_control = NULL; + msg.msg_controllen = 0; + r.rerrno = rerrno; + } else { + cm->cmsg_len = sizeof(cm_space); + cm->cmsg_level = SOL_SOCKET; + cm->cmsg_type = SCM_RIGHTS; + *(int *)CMSG_DATA(cm) = r.retval; + msg.msg_control = cm; + msg.msg_controllen = sizeof(cm_space); + } + msg.msg_iov = iov; + msg.msg_iovlen = nitems(iov); + (void)sendmsg(sock, &msg, 0); + if (fdesc >= 0) + close(fdesc); +} + +static int +startswith(const char *str, const char *prefix) +{ + return (strncmp(str, prefix, strlen(prefix)) == 0)? 1 : 0; +} + +static int +privsep_npppd_check_open(struct PRIVSEP_OPEN_ARG *arg) +{ + /* BPF, configuration file or /etc/resolv.conf */ + if (startswith(arg->path, "/dev/bpf") || + (startswith(arg->path, "/etc/npppd/") && arg->flags == O_RDONLY) || + (strcmp(arg->path, "/etc/resolv.conf") == 0 && + arg->flags == O_RDONLY)) + return 0; + + return 1; +} + +static int +privsep_npppd_check_socket(struct PRIVSEP_SOCKET_ARG *arg) +{ + /* npppd uses routing socket */ + if (arg->domain == PF_ROUTE && arg->type == SOCK_RAW && + arg->protocol == AF_UNSPEC) + return 0; + + /* npppd uses raw ip socket for GRE */ + if (arg->domain == AF_INET && arg->type == SOCK_RAW && + arg->protocol == IPPROTO_GRE) + return 0; + + return 1; +} + +static int +privsep_npppd_check_bind(struct PRIVSEP_BIND_ARG *arg) +{ + /* npppd uses /var/run/npppd_ctl as UNIX domain socket */ + if (arg->name.ss_family == AF_UNIX && + startswith(((struct sockaddr_un *)&arg->name)->sun_path, + "/var/run/")) + return 0; + + return 1; +} + +static int +privsep_npppd_check_sendto(struct PRIVSEP_SENDTO_ARG *arg) +{ + /* for reply npppdctl's request */ + if (arg->flags == 0 && arg->tolen >= 0 && + arg->to.ss_family == AF_UNIX) + return 0; + + /* for sending a routing socket message. */ + if (arg->flags == 0 && arg->tolen == 0) + return 0; + + return 1; +} + +static int +privsep_npppd_check_unlink(struct PRIVSEP_UNLINK_ARG *arg) +{ + /* npppd unlink the /var/run/npppd_ctl */ + if (startswith(arg->path, "/var/run/")) + return 0; + + return 1; +} diff --git a/usr.sbin/npppd/npppd/privsep.h b/usr.sbin/npppd/npppd/privsep.h new file mode 100644 index 00000000000..2d25c6efea0 --- /dev/null +++ b/usr.sbin/npppd/npppd/privsep.h @@ -0,0 +1,39 @@ +/* $OpenBSD: privsep.h,v 1.1 2010/01/31 05:49:51 yasuoka Exp $ */ + +/* + * Copyright (c) 2010 Yasuoka Masahiko <yasuoka@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef PRIVSEP_H +#define PRIVSEP_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +int privsep_init (void); +void privsep_fini (void); +FILE *priv_fopen (const char *); +int priv_bind (int, const struct sockaddr *, socklen_t); +int priv_unlink (const char *); +int priv_socket (int, int, int); +int priv_open (const char *, int, mode_t); +int priv_send (int, const void *, int, int); +int priv_sendto (int, const void *, int, int, const struct sockaddr *, socklen_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usr.sbin/npppd/pppoe/pppoed.c b/usr.sbin/npppd/pppoe/pppoed.c index 35f75d1bb3b..9c9b52d9dc4 100644 --- a/usr.sbin/npppd/pppoe/pppoed.c +++ b/usr.sbin/npppd/pppoe/pppoed.c @@ -29,7 +29,7 @@ * <dt>RFC 2516</dt> * <dd>A Method for Transmitting PPP Over Ethernet (PPPoE)</dd> * </dl> - * $Id: pppoed.c,v 1.3 2010/01/27 07:27:02 yasuoka Exp $ + * $Id: pppoed.c,v 1.4 2010/01/31 05:49:51 yasuoka Exp $ */ #include <sys/types.h> #include <sys/param.h> @@ -68,6 +68,7 @@ #include "properties.h" #include "config_helper.h" #include "rtev.h" +#include "privsep.h" #include "pppoe.h" #include "pppoe_local.h" @@ -278,7 +279,7 @@ pppoed_listener_start(pppoed_listener *_this, int restart) /* FIXME: NetBSD 3.0 では、/dev/bpf 一つで何度も開けるらしい */ for (i = 0; i < 256; i++) { snprintf(buf, sizeof(buf), "/dev/bpf%d", i); - if ((_this->bpf = open(buf, O_RDWR, 0600)) >= 0) { + if ((_this->bpf = priv_open(buf, O_RDWR, 0600)) >= 0) { break; } else if (errno == ENXIO || errno == ENOENT) break; /* これ以上探してもみつからないはず */ diff --git a/usr.sbin/npppd/pptp/pptpd.c b/usr.sbin/npppd/pptp/pptpd.c index e73cb04d0ca..9d8c268cdbd 100644 --- a/usr.sbin/npppd/pptp/pptpd.c +++ b/usr.sbin/npppd/pptp/pptpd.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -/* $Id: pptpd.c,v 1.2 2010/01/13 07:49:44 yasuoka Exp $ */ +/* $Id: pptpd.c,v 1.3 2010/01/31 05:49:51 yasuoka Exp $ */ /**@file * PPTPデーモンの実装。現在は PAC(PPTP Access Concentrator) としての実装 * のみです。 @@ -65,6 +65,7 @@ #include "pptp.h" #include "pptp_local.h" +#include "privsep.h" static int pptpd_seqno = 0; @@ -375,7 +376,7 @@ pptpd_listener_start(pptpd_listener *_this) /* GRE */ bind_sin_gre.sin_port = 0; - if ((sock_gre = socket(AF_INET, SOCK_RAW, IPPROTO_GRE)) < 0) { + if ((sock_gre = priv_socket(AF_INET, SOCK_RAW, IPPROTO_GRE)) < 0) { pptpd_log(_this->self, LOG_ERR, "socket() failed at %s(): %m", __func__); goto reigai; |