Files
Flips/arlib/sandbox/linux-file-pass-sbx.cpp
2016-12-20 02:10:51 +01:00

157 lines
4.3 KiB
C++

#ifdef __linux__
#include "sandbox-internal.h"
#undef bind
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void sandbox_cross_init(int* pfd, int* cfd)
{
int fds[2];
socketpair(AF_LOCAL, SOCK_DGRAM, 0, fds);
*pfd = fds[0];
*cfd = fds[1];
}
//from http://blog.varunajayasiri.com/passing-file-descriptors-between-processes-using-sendmsg-and-recvmsg
//somewhat reformatted
void sandbox_cross_send(int socket, int fd_to_send, int errno_ret)
{
//need at least one byte of data, otherwise recvmsg gets angry
struct iovec iov = { &errno_ret, sizeof(errno_ret) };
char ctrl_buf[CMSG_SPACE(sizeof(int))] = {};
struct msghdr message = {
.msg_name = NULL, .msg_namelen = 0,
.msg_iov = &iov, .msg_iovlen = 1,
.msg_control = ctrl_buf, .msg_controllen = sizeof(ctrl_buf),
.msg_flags = 0,
};
cmsghdr* ctrl_msg = CMSG_FIRSTHDR(&message);
ctrl_msg->cmsg_level = SOL_SOCKET;
ctrl_msg->cmsg_type = SCM_RIGHTS;
ctrl_msg->cmsg_len = CMSG_LEN(sizeof(int));
*(int*)CMSG_DATA(ctrl_msg) = fd_to_send;
if (fd_to_send >= 0) errno = 0;
if (fd_to_send < 0) message.msg_controllen = 0;
sendmsg(socket, &message, 0);
}
int sandbox_cross_recv(int socket)
{
int errno_ret;
struct iovec iov = { &errno_ret, sizeof(int) };
char ctrl_buf[CMSG_SPACE(sizeof(int))] = {};
struct msghdr message = {
.msg_name = NULL, .msg_namelen = 0,
.msg_iov = &iov, .msg_iovlen = 1,
.msg_control = ctrl_buf, .msg_controllen = sizeof(ctrl_buf),
.msg_flags = 0,
};
if (recvmsg(socket, &message, 0) <= 0) return -1;
for (cmsghdr* ctrl_msg=CMSG_FIRSTHDR(&message);ctrl_msg!=NULL;ctrl_msg=CMSG_NXTHDR(&message, ctrl_msg))
{
if (ctrl_msg->cmsg_level == SOL_SOCKET && ctrl_msg->cmsg_type == SCM_RIGHTS)
{
return *(int*)CMSG_DATA(ctrl_msg);
}
}
errno = errno_ret;
return -1;
}
int sandbox_cross_request(int socket, const char * path, int flags, mode_t mode)
{
struct iovec iov[3] = { { &flags, sizeof(flags) }, { &mode, sizeof(mode) }, { (char*)path, strlen(path) } };
struct msghdr message = {
.msg_name = NULL, .msg_namelen = 0,
.msg_iov = iov, .msg_iovlen = 2,
.msg_control = NULL, .msg_controllen = 0,
.msg_flags = 0,
};
sendmsg(socket, &message, 0);
return sandbox_cross_recv(socket);
}
void sandbox_cross_serve_request_full(int socket,
int (*access)(const char * path, int flags, mode_t mode, void* userdata),
void* userdata)
{
struct {
int flags;
mode_t mode;
char path[1024+1];
} msg;
struct iovec iov = { &msg, sizeof(msg)-1 };
struct msghdr message = {
.msg_name = NULL, .msg_namelen = 0,
.msg_iov = &iov, .msg_iovlen = 1,
.msg_control = NULL, .msg_controllen = 0,
.msg_flags = 0,
};
int bytes = recvmsg(socket, &message, MSG_DONTWAIT);
if (bytes <= 0) return;
if (message.msg_flags & MSG_TRUNC)
{
sandbox_cross_send(socket, -1, ENAMETOOLONG);
return;
}
((char*)&msg)[bytes] = '\0';
int retfd = access(msg.path, msg.flags, msg.mode, userdata);
sandbox_cross_send(socket, retfd, errno);
}
struct req_dat {
bool (*access)(const char * path, bool write, void* userdata);
void* userdata;
};
static int req_sub(const char * path, int flags, mode_t mode, void* userdata)
{
req_dat* dat = (req_dat*)userdata;
if (flags & O_CREAT)
{
if (mode & ~0777) goto deny;
}
else mode=0;
static_assert(O_RDONLY==0);
static_assert(O_WRONLY==1);
static_assert(O_RDWR==2);
static const int flag_ignore = 0; // allow these flags, but ignore them
static const int flag_read = O_CLOEXEC|O_LARGEFILE; // allow these flags
static const int flag_write = O_WRONLY|O_RDWR|O_APPEND|O_CREAT|O_EXCL|O_TRUNC; // allow these flags, but only if write access is fine
flags &= ~flag_ignore;
if ((flags & (O_WRONLY|O_RDWR)) == (O_WRONLY|O_RDWR)) goto deny; // invalid open mode
if (flags & ~(flag_read | flag_write)) goto deny; // unacceptable flags
if (!dat->access(path, (flags & flag_write), dat->userdata)) goto deny;
return open(path, flags, mode);
deny:
errno = EACCES;
return -1;
}
void sandbox_cross_serve_request(int socket, bool (*access)(const char * path, bool write, void* userdata), void* userdata)
{
req_dat dat = { access, userdata };
sandbox_cross_serve_request_full(socket, req_sub, &dat);
}
#endif