summaryrefslogtreecommitdiff
path: root/usr.sbin/npppd
diff options
context:
space:
mode:
authorYASUOKA Masahiko <yasuoka@cvs.openbsd.org>2010-01-31 05:49:52 +0000
committerYASUOKA Masahiko <yasuoka@cvs.openbsd.org>2010-01-31 05:49:52 +0000
commitfceb6db87ac8ef9ad4b6724b1c49236e55988205 (patch)
tree82790a5a1cce47b6128d0f5239f9afa904ef5e7f /usr.sbin/npppd
parent0d8a83a403b2418bd67b9c3071d38eee519335e9 (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/Makefile1
-rw-r--r--usr.sbin/npppd/npppd/npppd.c31
-rw-r--r--usr.sbin/npppd/npppd/npppd.h1
-rw-r--r--usr.sbin/npppd/npppd/npppd_auth.c4
-rw-r--r--usr.sbin/npppd/npppd/npppd_config.c4
-rw-r--r--usr.sbin/npppd/npppd/npppd_ctl.c10
-rw-r--r--usr.sbin/npppd/npppd/npppd_defs.h3
-rw-r--r--usr.sbin/npppd/npppd/npppd_local.h2
-rw-r--r--usr.sbin/npppd/npppd/npppd_subr.c9
-rw-r--r--usr.sbin/npppd/npppd/privsep.c582
-rw-r--r--usr.sbin/npppd/npppd/privsep.h39
-rw-r--r--usr.sbin/npppd/pppoe/pppoed.c5
-rw-r--r--usr.sbin/npppd/pptp/pptpd.c5
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;