bubblewrap.c (flatpak-1.15.1.tar.xz) | : | bubblewrap.c (flatpak-1.15.2.tar.xz) | ||
---|---|---|---|---|
skipping to change at line 26 | skipping to change at line 26 | |||
* License along with this library. If not, see <http://www.gnu.org/licenses/>. | * License along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
* | * | |||
*/ | */ | |||
#include "config.h" | #include "config.h" | |||
#include <poll.h> | #include <poll.h> | |||
#include <sched.h> | #include <sched.h> | |||
#include <pwd.h> | #include <pwd.h> | |||
#include <grp.h> | #include <grp.h> | |||
#include <ctype.h> | ||||
#include <sys/mount.h> | #include <sys/mount.h> | |||
#include <sys/socket.h> | #include <sys/socket.h> | |||
#include <sys/wait.h> | #include <sys/wait.h> | |||
#include <sys/eventfd.h> | #include <sys/eventfd.h> | |||
#include <sys/fsuid.h> | #include <sys/fsuid.h> | |||
#include <sys/signalfd.h> | #include <sys/signalfd.h> | |||
#include <sys/capability.h> | #include <sys/capability.h> | |||
#include <sys/prctl.h> | #include <sys/prctl.h> | |||
#include <linux/sched.h> | #include <linux/sched.h> | |||
#include <linux/seccomp.h> | #include <linux/seccomp.h> | |||
skipping to change at line 55 | skipping to change at line 56 | |||
#ifndef TEMP_FAILURE_RETRY | #ifndef TEMP_FAILURE_RETRY | |||
#define TEMP_FAILURE_RETRY(expression) \ | #define TEMP_FAILURE_RETRY(expression) \ | |||
(__extension__ \ | (__extension__ \ | |||
({ long int __result; \ | ({ long int __result; \ | |||
do __result = (long int) (expression); \ | do __result = (long int) (expression); \ | |||
while (__result == -1L && errno == EINTR); \ | while (__result == -1L && errno == EINTR); \ | |||
__result; })) | __result; })) | |||
#endif | #endif | |||
/* We limit the size of a tmpfs to half the architecture's address space, | ||||
* to avoid hitting arbitrary limits in the kernel. | ||||
* For example, on at least one x86_64 machine, the actual limit seems to be | ||||
* 2^64 - 2^12. */ | ||||
#define MAX_TMPFS_BYTES ((size_t) (SIZE_MAX >> 1)) | ||||
/* Globals to avoid having to use getuid(), since the uid/gid changes during run time */ | /* Globals to avoid having to use getuid(), since the uid/gid changes during run time */ | |||
static uid_t real_uid; | static uid_t real_uid; | |||
static gid_t real_gid; | static gid_t real_gid; | |||
static uid_t overflow_uid; | static uid_t overflow_uid; | |||
static gid_t overflow_gid; | static gid_t overflow_gid; | |||
static bool is_privileged; /* See acquire_privs() */ | static bool is_privileged; /* See acquire_privs() */ | |||
static const char *argv0; | static const char *argv0; | |||
static const char *host_tty_dev; | static const char *host_tty_dev; | |||
static int proc_fd = -1; | static int proc_fd = -1; | |||
static const char *opt_exec_label = NULL; | static const char *opt_exec_label = NULL; | |||
skipping to change at line 94 | skipping to change at line 101 | |||
int opt_userns_block_fd = -1; | int opt_userns_block_fd = -1; | |||
int opt_info_fd = -1; | int opt_info_fd = -1; | |||
int opt_json_status_fd = -1; | int opt_json_status_fd = -1; | |||
int opt_seccomp_fd = -1; | int opt_seccomp_fd = -1; | |||
const char *opt_sandbox_hostname = NULL; | const char *opt_sandbox_hostname = NULL; | |||
char *opt_args_data = NULL; /* owned */ | char *opt_args_data = NULL; /* owned */ | |||
int opt_userns_fd = -1; | int opt_userns_fd = -1; | |||
int opt_userns2_fd = -1; | int opt_userns2_fd = -1; | |||
int opt_pidns_fd = -1; | int opt_pidns_fd = -1; | |||
int next_perms = -1; | int next_perms = -1; | |||
size_t next_size_arg = 0; | ||||
#define CAP_TO_MASK_0(x) (1L << ((x) & 31)) | #define CAP_TO_MASK_0(x) (1L << ((x) & 31)) | |||
#define CAP_TO_MASK_1(x) CAP_TO_MASK_0(x - 32) | #define CAP_TO_MASK_1(x) CAP_TO_MASK_0(x - 32) | |||
typedef struct _NsInfo NsInfo; | typedef struct _NsInfo NsInfo; | |||
struct _NsInfo { | struct _NsInfo { | |||
const char *name; | const char *name; | |||
bool *do_unshare; | bool *do_unshare; | |||
ino_t id; | ino_t id; | |||
skipping to change at line 152 | skipping to change at line 160 | |||
typedef struct _SetupOp SetupOp; | typedef struct _SetupOp SetupOp; | |||
struct _SetupOp | struct _SetupOp | |||
{ | { | |||
SetupOpType type; | SetupOpType type; | |||
const char *source; | const char *source; | |||
const char *dest; | const char *dest; | |||
int fd; | int fd; | |||
SetupOpFlag flags; | SetupOpFlag flags; | |||
int perms; | int perms; | |||
size_t size; /* number of bytes, zero means unset/default */ | ||||
SetupOp *next; | SetupOp *next; | |||
}; | }; | |||
typedef struct _LockFile LockFile; | typedef struct _LockFile LockFile; | |||
struct _LockFile | struct _LockFile | |||
{ | { | |||
const char *path; | const char *path; | |||
int fd; | int fd; | |||
LockFile *next; | LockFile *next; | |||
skipping to change at line 180 | skipping to change at line 189 | |||
PRIV_SEP_OP_MQUEUE_MOUNT, | PRIV_SEP_OP_MQUEUE_MOUNT, | |||
PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE, | PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE, | |||
PRIV_SEP_OP_SET_HOSTNAME, | PRIV_SEP_OP_SET_HOSTNAME, | |||
}; | }; | |||
typedef struct | typedef struct | |||
{ | { | |||
uint32_t op; | uint32_t op; | |||
uint32_t flags; | uint32_t flags; | |||
uint32_t perms; | uint32_t perms; | |||
size_t size_arg; | ||||
uint32_t arg1_offset; | uint32_t arg1_offset; | |||
uint32_t arg2_offset; | uint32_t arg2_offset; | |||
} PrivSepOp; | } PrivSepOp; | |||
/* | /* | |||
* DEFINE_LINKED_LIST: | * DEFINE_LINKED_LIST: | |||
* @Type: A struct with a `Type *next` member | * @Type: A struct with a `Type *next` member | |||
* @name: Used to form the names of variables and functions | * @name: Used to form the names of variables and functions | |||
* | * | |||
* Define a global linked list of @Type structures, with pointers | * Define a global linked list of @Type structures, with pointers | |||
skipping to change at line 304 | skipping to change at line 314 | |||
" --unshare-user Create new user namespace (may be a utomatically implied if not setuid)\n" | " --unshare-user Create new user namespace (may be a utomatically implied if not setuid)\n" | |||
" --unshare-user-try Create new user namespace if possib le else continue by skipping it\n" | " --unshare-user-try Create new user namespace if possib le else continue by skipping it\n" | |||
" --unshare-ipc Create new ipc namespace\n" | " --unshare-ipc Create new ipc namespace\n" | |||
" --unshare-pid Create new pid namespace\n" | " --unshare-pid Create new pid namespace\n" | |||
" --unshare-net Create new network namespace\n" | " --unshare-net Create new network namespace\n" | |||
" --unshare-uts Create new uts namespace\n" | " --unshare-uts Create new uts namespace\n" | |||
" --unshare-cgroup Create new cgroup namespace\n" | " --unshare-cgroup Create new cgroup namespace\n" | |||
" --unshare-cgroup-try Create new cgroup namespace if poss ible else continue by skipping it\n" | " --unshare-cgroup-try Create new cgroup namespace if poss ible else continue by skipping it\n" | |||
" --userns FD Use this user namespace (cannot com bine with --unshare-user)\n" | " --userns FD Use this user namespace (cannot com bine with --unshare-user)\n" | |||
" --userns2 FD After setup switch to this user nam espace, only useful with --userns\n" | " --userns2 FD After setup switch to this user nam espace, only useful with --userns\n" | |||
" --pidns FD Use this user namespace (as parent namespace if using --unshare-pid)\n" | " --pidns FD Use this pid namespace (as parent n amespace if using --unshare-pid)\n" | |||
" --uid UID Custom uid in the sandbox (requires --unshare-user or --userns)\n" | " --uid UID Custom uid in the sandbox (requires --unshare-user or --userns)\n" | |||
" --gid GID Custom gid in the sandbox (requires --unshare-user or --userns)\n" | " --gid GID Custom gid in the sandbox (requires --unshare-user or --userns)\n" | |||
" --hostname NAME Custom hostname in the sandbox (req uires --unshare-uts)\n" | " --hostname NAME Custom hostname in the sandbox (req uires --unshare-uts)\n" | |||
" --chdir DIR Change directory to DIR\n" | " --chdir DIR Change directory to DIR\n" | |||
" --clearenv Unset all environment variables\n" | " --clearenv Unset all environment variables\n" | |||
" --setenv VAR VALUE Set an environment variable\n" | " --setenv VAR VALUE Set an environment variable\n" | |||
" --unsetenv VAR Unset an environment variable\n" | " --unsetenv VAR Unset an environment variable\n" | |||
" --lock-file DEST Take a lock on DEST while sandbox i s running\n" | " --lock-file DEST Take a lock on DEST while sandbox i s running\n" | |||
" --sync-fd FD Keep this fd open while sandbox is running\n" | " --sync-fd FD Keep this fd open while sandbox is running\n" | |||
" --bind SRC DEST Bind mount the host path SRC on DES T\n" | " --bind SRC DEST Bind mount the host path SRC on DES T\n" | |||
skipping to change at line 344 | skipping to change at line 354 | |||
" --block-fd FD Block on FD until some data to read is available\n" | " --block-fd FD Block on FD until some data to read is available\n" | |||
" --userns-block-fd FD Block on FD until the user namespac e is ready\n" | " --userns-block-fd FD Block on FD until the user namespac e is ready\n" | |||
" --info-fd FD Write information about the running container to FD\n" | " --info-fd FD Write information about the running container to FD\n" | |||
" --json-status-fd FD Write container status to FD as mul tiple JSON documents\n" | " --json-status-fd FD Write container status to FD as mul tiple JSON documents\n" | |||
" --new-session Create a new terminal session\n" | " --new-session Create a new terminal session\n" | |||
" --die-with-parent Kills with SIGKILL child process (C OMMAND) when bwrap or bwrap's parent dies.\n" | " --die-with-parent Kills with SIGKILL child process (C OMMAND) when bwrap or bwrap's parent dies.\n" | |||
" --as-pid-1 Do not install a reaper process wit h PID=1\n" | " --as-pid-1 Do not install a reaper process wit h PID=1\n" | |||
" --cap-add CAP Add cap CAP when running as privile ged user\n" | " --cap-add CAP Add cap CAP when running as privile ged user\n" | |||
" --cap-drop CAP Drop cap CAP when running as privil eged user\n" | " --cap-drop CAP Drop cap CAP when running as privil eged user\n" | |||
" --perms OCTAL Set permissions of next argument (- -bind-data, --file, etc.)\n" | " --perms OCTAL Set permissions of next argument (- -bind-data, --file, etc.)\n" | |||
" --size BYTES Set size of next argument (only for --tmpfs)\n" | ||||
" --chmod OCTAL PATH Change permissions of PATH (must al ready exist)\n" | " --chmod OCTAL PATH Change permissions of PATH (must al ready exist)\n" | |||
); | ); | |||
exit (ecode); | exit (ecode); | |||
} | } | |||
/* If --die-with-parent was specified, use PDEATHSIG to ensure SIGKILL | /* If --die-with-parent was specified, use PDEATHSIG to ensure SIGKILL | |||
* is sent to the current process when our parent dies. | * is sent to the current process when our parent dies. | |||
*/ | */ | |||
static void | static void | |||
handle_die_with_parent (void) | handle_die_with_parent (void) | |||
skipping to change at line 1004 | skipping to change at line 1015 | |||
if (setfsuid (-1) != real_uid) | if (setfsuid (-1) != real_uid) | |||
die ("Unable to re-set fsuid"); | die ("Unable to re-set fsuid"); | |||
} | } | |||
} | } | |||
static void | static void | |||
privileged_op (int privileged_op_socket, | privileged_op (int privileged_op_socket, | |||
uint32_t op, | uint32_t op, | |||
uint32_t flags, | uint32_t flags, | |||
uint32_t perms, | uint32_t perms, | |||
size_t size_arg, | ||||
const char *arg1, | const char *arg1, | |||
const char *arg2) | const char *arg2) | |||
{ | { | |||
bind_mount_result bind_result; | bind_mount_result bind_result; | |||
char *failing_path = NULL; | ||||
if (privileged_op_socket != -1) | if (privileged_op_socket != -1) | |||
{ | { | |||
uint32_t buffer[2048]; /* 8k, but is int32 to guarantee nice alignment */ | uint32_t buffer[2048]; /* 8k, but is int32 to guarantee nice alignment */ | |||
PrivSepOp *op_buffer = (PrivSepOp *) buffer; | PrivSepOp *op_buffer = (PrivSepOp *) buffer; | |||
size_t buffer_size = sizeof (PrivSepOp); | size_t buffer_size = sizeof (PrivSepOp); | |||
uint32_t arg1_offset = 0, arg2_offset = 0; | uint32_t arg1_offset = 0, arg2_offset = 0; | |||
/* We're unprivileged, send this request to the privileged part */ | /* We're unprivileged, send this request to the privileged part */ | |||
skipping to change at line 1035 | skipping to change at line 1048 | |||
arg2_offset = buffer_size; | arg2_offset = buffer_size; | |||
buffer_size += strlen (arg2) + 1; | buffer_size += strlen (arg2) + 1; | |||
} | } | |||
if (buffer_size >= sizeof (buffer)) | if (buffer_size >= sizeof (buffer)) | |||
die ("privilege separation operation to large"); | die ("privilege separation operation to large"); | |||
op_buffer->op = op; | op_buffer->op = op; | |||
op_buffer->flags = flags; | op_buffer->flags = flags; | |||
op_buffer->perms = perms; | op_buffer->perms = perms; | |||
op_buffer->size_arg = size_arg; | ||||
op_buffer->arg1_offset = arg1_offset; | op_buffer->arg1_offset = arg1_offset; | |||
op_buffer->arg2_offset = arg2_offset; | op_buffer->arg2_offset = arg2_offset; | |||
if (arg1 != NULL) | if (arg1 != NULL) | |||
strcpy ((char *) buffer + arg1_offset, arg1); | strcpy ((char *) buffer + arg1_offset, arg1); | |||
if (arg2 != NULL) | if (arg2 != NULL) | |||
strcpy ((char *) buffer + arg2_offset, arg2); | strcpy ((char *) buffer + arg2_offset, arg2); | |||
if (write (privileged_op_socket, buffer, buffer_size) != buffer_size) | if (write (privileged_op_socket, buffer, buffer_size) != buffer_size) | |||
die ("Can't write to privileged_op_socket"); | die ("Can't write to privileged_op_socket"); | |||
skipping to change at line 1073 | skipping to change at line 1087 | |||
* be safe. | * be safe. | |||
* * Remounting RO (even non-recursive) is safe because it decreases privileg es. | * * Remounting RO (even non-recursive) is safe because it decreases privileg es. | |||
* * sethostname() is safe only if we set up a UTS namespace | * * sethostname() is safe only if we set up a UTS namespace | |||
*/ | */ | |||
switch (op) | switch (op) | |||
{ | { | |||
case PRIV_SEP_OP_DONE: | case PRIV_SEP_OP_DONE: | |||
break; | break; | |||
case PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE: | case PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE: | |||
bind_result = bind_mount (proc_fd, NULL, arg2, BIND_READONLY); | bind_result = bind_mount (proc_fd, NULL, arg2, BIND_READONLY, &failing_pat h); | |||
if (bind_result != BIND_MOUNT_SUCCESS) | if (bind_result != BIND_MOUNT_SUCCESS) | |||
die_with_bind_result (bind_result, errno, | die_with_bind_result (bind_result, errno, failing_path, | |||
"Can't remount readonly on %s", arg2); | "Can't remount readonly on %s", arg2); | |||
assert (failing_path == NULL); /* otherwise we would have died */ | ||||
break; | break; | |||
case PRIV_SEP_OP_BIND_MOUNT: | case PRIV_SEP_OP_BIND_MOUNT: | |||
/* We always bind directories recursively, otherwise this would let us | /* We always bind directories recursively, otherwise this would let us | |||
access files that are otherwise covered on the host */ | access files that are otherwise covered on the host */ | |||
bind_result = bind_mount (proc_fd, arg1, arg2, BIND_RECURSIVE | flags); | bind_result = bind_mount (proc_fd, arg1, arg2, BIND_RECURSIVE | flags, &fa iling_path); | |||
if (bind_result != BIND_MOUNT_SUCCESS) | if (bind_result != BIND_MOUNT_SUCCESS) | |||
die_with_bind_result (bind_result, errno, | die_with_bind_result (bind_result, errno, failing_path, | |||
"Can't bind mount %s on %s", arg1, arg2); | "Can't bind mount %s on %s", arg1, arg2); | |||
assert (failing_path == NULL); /* otherwise we would have died */ | ||||
break; | break; | |||
case PRIV_SEP_OP_PROC_MOUNT: | case PRIV_SEP_OP_PROC_MOUNT: | |||
if (mount ("proc", arg1, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) ! = 0) | if (mount ("proc", arg1, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) ! = 0) | |||
die_with_error ("Can't mount proc on %s", arg1); | die_with_error ("Can't mount proc on %s", arg1); | |||
break; | break; | |||
case PRIV_SEP_OP_TMPFS_MOUNT: | case PRIV_SEP_OP_TMPFS_MOUNT: | |||
{ | { | |||
cleanup_free char *mode = xasprintf ("mode=%#o", perms); | cleanup_free char *mode = NULL; | |||
/* This check should be unnecessary since we checked this when parsing | ||||
* the --size option as well. However, better be safe than sorry. */ | ||||
if (size_arg > MAX_TMPFS_BYTES) | ||||
die_with_error ("Specified tmpfs size too large (%zu > %zu)", size_arg | ||||
, MAX_TMPFS_BYTES); | ||||
if (size_arg != 0) | ||||
mode = xasprintf ("mode=%#o,size=%zu", perms, size_arg); | ||||
else | ||||
mode = xasprintf ("mode=%#o", perms); | ||||
cleanup_free char *opt = label_mount (mode, opt_file_label); | cleanup_free char *opt = label_mount (mode, opt_file_label); | |||
if (mount ("tmpfs", arg1, "tmpfs", MS_NOSUID | MS_NODEV, opt) != 0) | if (mount ("tmpfs", arg1, "tmpfs", MS_NOSUID | MS_NODEV, opt) != 0) | |||
die_with_error ("Can't mount tmpfs on %s", arg1); | die_with_error ("Can't mount tmpfs on %s", arg1); | |||
break; | break; | |||
} | } | |||
case PRIV_SEP_OP_DEVPTS_MOUNT: | case PRIV_SEP_OP_DEVPTS_MOUNT: | |||
if (mount ("devpts", arg1, "devpts", MS_NOSUID | MS_NOEXEC, | if (mount ("devpts", arg1, "devpts", MS_NOSUID | MS_NOEXEC, | |||
"newinstance,ptmxmode=0666,mode=620") != 0) | "newinstance,ptmxmode=0666,mode=620") != 0) | |||
die_with_error ("Can't mount devpts on %s", arg1); | die_with_error ("Can't mount devpts on %s", arg1); | |||
skipping to change at line 1200 | skipping to change at line 1227 | |||
if (ensure_dir (dest, 0755) != 0) | if (ensure_dir (dest, 0755) != 0) | |||
die_with_error ("Can't mkdir %s", op->dest); | die_with_error ("Can't mkdir %s", op->dest); | |||
} | } | |||
else if (ensure_file (dest, 0444) != 0) | else if (ensure_file (dest, 0444) != 0) | |||
die_with_error ("Can't create file at %s", op->dest); | die_with_error ("Can't create file at %s", op->dest); | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_BIND_MOUNT, | PRIV_SEP_OP_BIND_MOUNT, | |||
(op->type == SETUP_RO_BIND_MOUNT ? BIND_READONLY : 0) | | (op->type == SETUP_RO_BIND_MOUNT ? BIND_READONLY : 0) | | |||
(op->type == SETUP_DEV_BIND_MOUNT ? BIND_DEVICES : 0), | (op->type == SETUP_DEV_BIND_MOUNT ? BIND_DEVICES : 0), | |||
0, source, dest); | 0, 0, source, dest); | |||
break; | break; | |||
case SETUP_REMOUNT_RO_NO_RECURSIVE: | case SETUP_REMOUNT_RO_NO_RECURSIVE: | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE, 0, 0, NULL, dest); | PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE, 0, 0, 0, NULL, des t); | |||
break; | break; | |||
case SETUP_MOUNT_PROC: | case SETUP_MOUNT_PROC: | |||
if (ensure_dir (dest, 0755) != 0) | if (ensure_dir (dest, 0755) != 0) | |||
die_with_error ("Can't mkdir %s", op->dest); | die_with_error ("Can't mkdir %s", op->dest); | |||
if (unshare_pid || opt_pidns_fd != -1) | if (unshare_pid || opt_pidns_fd != -1) | |||
{ | { | |||
/* Our own procfs */ | /* Our own procfs */ | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_PROC_MOUNT, 0, 0, | PRIV_SEP_OP_PROC_MOUNT, 0, 0, 0, | |||
dest, NULL); | dest, NULL); | |||
} | } | |||
else | else | |||
{ | { | |||
/* Use system procfs, as we share pid namespace anyway */ | /* Use system procfs, as we share pid namespace anyway */ | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_BIND_MOUNT, 0, 0, | PRIV_SEP_OP_BIND_MOUNT, 0, 0, 0, | |||
"oldroot/proc", dest); | "oldroot/proc", dest); | |||
} | } | |||
/* There are a bunch of weird old subdirs of /proc that could potentia lly be | /* There are a bunch of weird old subdirs of /proc that could potentia lly be | |||
problematic (for instance /proc/sysrq-trigger lets you shut down th e machine | problematic (for instance /proc/sysrq-trigger lets you shut down th e machine | |||
if you have write access). We should not have access to these as a non-privileged | if you have write access). We should not have access to these as a non-privileged | |||
user, but lets cover them anyway just to make sure */ | user, but lets cover them anyway just to make sure */ | |||
static const char * const cover_proc_dirs[] = { "sys", "sysrq-trigger" , "irq", "bus" }; | static const char * const cover_proc_dirs[] = { "sys", "sysrq-trigger" , "irq", "bus" }; | |||
for (i = 0; i < N_ELEMENTS (cover_proc_dirs); i++) | for (i = 0; i < N_ELEMENTS (cover_proc_dirs); i++) | |||
{ | { | |||
skipping to change at line 1245 | skipping to change at line 1272 | |||
if (access (subdir, W_OK) < 0) | if (access (subdir, W_OK) < 0) | |||
{ | { | |||
/* The file is already read-only or doesn't exist. */ | /* The file is already read-only or doesn't exist. */ | |||
if (errno == EACCES || errno == ENOENT || errno == EROFS) | if (errno == EACCES || errno == ENOENT || errno == EROFS) | |||
continue; | continue; | |||
die_with_error ("Can't access %s", subdir); | die_with_error ("Can't access %s", subdir); | |||
} | } | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_BIND_MOUNT, BIND_READONLY, 0, | PRIV_SEP_OP_BIND_MOUNT, BIND_READONLY, 0, 0, | |||
subdir, subdir); | subdir, subdir); | |||
} | } | |||
break; | break; | |||
case SETUP_MOUNT_DEV: | case SETUP_MOUNT_DEV: | |||
if (ensure_dir (dest, 0755) != 0) | if (ensure_dir (dest, 0755) != 0) | |||
die_with_error ("Can't mkdir %s", op->dest); | die_with_error ("Can't mkdir %s", op->dest); | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_TMPFS_MOUNT, 0, 0755, | PRIV_SEP_OP_TMPFS_MOUNT, 0, 0755, 0, | |||
dest, NULL); | dest, NULL); | |||
static const char *const devnodes[] = { "null", "zero", "full", "rando m", "urandom", "tty" }; | static const char *const devnodes[] = { "null", "zero", "full", "rando m", "urandom", "tty" }; | |||
for (i = 0; i < N_ELEMENTS (devnodes); i++) | for (i = 0; i < N_ELEMENTS (devnodes); i++) | |||
{ | { | |||
cleanup_free char *node_dest = strconcat3 (dest, "/", devnodes[i]) ; | cleanup_free char *node_dest = strconcat3 (dest, "/", devnodes[i]) ; | |||
cleanup_free char *node_src = strconcat ("/oldroot/dev/", devnodes [i]); | cleanup_free char *node_src = strconcat ("/oldroot/dev/", devnodes [i]); | |||
if (create_file (node_dest, 0444, NULL) != 0) | if (create_file (node_dest, 0444, NULL) != 0) | |||
die_with_error ("Can't create file %s/%s", op->dest, devnodes[i] ); | die_with_error ("Can't create file %s/%s", op->dest, devnodes[i] ); | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_BIND_MOUNT, BIND_DEVICES, 0, | PRIV_SEP_OP_BIND_MOUNT, BIND_DEVICES, 0, 0, | |||
node_src, node_dest); | node_src, node_dest); | |||
} | } | |||
static const char *const stdionodes[] = { "stdin", "stdout", "stderr" }; | static const char *const stdionodes[] = { "stdin", "stdout", "stderr" }; | |||
for (i = 0; i < N_ELEMENTS (stdionodes); i++) | for (i = 0; i < N_ELEMENTS (stdionodes); i++) | |||
{ | { | |||
cleanup_free char *target = xasprintf ("/proc/self/fd/%d", i); | cleanup_free char *target = xasprintf ("/proc/self/fd/%d", i); | |||
cleanup_free char *node_dest = strconcat3 (dest, "/", stdionodes[i ]); | cleanup_free char *node_dest = strconcat3 (dest, "/", stdionodes[i ]); | |||
if (symlink (target, node_dest) < 0) | if (symlink (target, node_dest) < 0) | |||
die_with_error ("Can't create symlink %s/%s", op->dest, stdionod es[i]); | die_with_error ("Can't create symlink %s/%s", op->dest, stdionod es[i]); | |||
skipping to change at line 1301 | skipping to change at line 1328 | |||
cleanup_free char *pts = strconcat (dest, "/pts"); | cleanup_free char *pts = strconcat (dest, "/pts"); | |||
cleanup_free char *ptmx = strconcat (dest, "/ptmx"); | cleanup_free char *ptmx = strconcat (dest, "/ptmx"); | |||
cleanup_free char *shm = strconcat (dest, "/shm"); | cleanup_free char *shm = strconcat (dest, "/shm"); | |||
if (mkdir (shm, 0755) == -1) | if (mkdir (shm, 0755) == -1) | |||
die_with_error ("Can't create %s/shm", op->dest); | die_with_error ("Can't create %s/shm", op->dest); | |||
if (mkdir (pts, 0755) == -1) | if (mkdir (pts, 0755) == -1) | |||
die_with_error ("Can't create %s/devpts", op->dest); | die_with_error ("Can't create %s/devpts", op->dest); | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_DEVPTS_MOUNT, 0, 0, pts, NULL); | PRIV_SEP_OP_DEVPTS_MOUNT, 0, 0, 0, pts, NULL); | |||
if (symlink ("pts/ptmx", ptmx) != 0) | if (symlink ("pts/ptmx", ptmx) != 0) | |||
die_with_error ("Can't make symlink at %s/ptmx", op->dest); | die_with_error ("Can't make symlink at %s/ptmx", op->dest); | |||
} | } | |||
/* If stdout is a tty, that means the sandbox can write to the | /* If stdout is a tty, that means the sandbox can write to the | |||
outside-sandbox tty. In that case we also create a /dev/console | outside-sandbox tty. In that case we also create a /dev/console | |||
that points to this tty device. This should not cause any more | that points to this tty device. This should not cause any more | |||
access than we already have, and it makes ttyname() work in the | access than we already have, and it makes ttyname() work in the | |||
sandbox. */ | sandbox. */ | |||
if (host_tty_dev != NULL && *host_tty_dev != 0) | if (host_tty_dev != NULL && *host_tty_dev != 0) | |||
{ | { | |||
cleanup_free char *src_tty_dev = strconcat ("/oldroot", host_tty_d ev); | cleanup_free char *src_tty_dev = strconcat ("/oldroot", host_tty_d ev); | |||
cleanup_free char *dest_console = strconcat (dest, "/console"); | cleanup_free char *dest_console = strconcat (dest, "/console"); | |||
if (create_file (dest_console, 0444, NULL) != 0) | if (create_file (dest_console, 0444, NULL) != 0) | |||
die_with_error ("creating %s/console", op->dest); | die_with_error ("creating %s/console", op->dest); | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_BIND_MOUNT, BIND_DEVICES, 0, | PRIV_SEP_OP_BIND_MOUNT, BIND_DEVICES, 0, 0, | |||
src_tty_dev, dest_console); | src_tty_dev, dest_console); | |||
} | } | |||
break; | break; | |||
case SETUP_MOUNT_TMPFS: | case SETUP_MOUNT_TMPFS: | |||
assert (dest != NULL); | assert (dest != NULL); | |||
assert (op->perms >= 0); | assert (op->perms >= 0); | |||
assert (op->perms <= 07777); | assert (op->perms <= 07777); | |||
if (ensure_dir (dest, 0755) != 0) | if (ensure_dir (dest, 0755) != 0) | |||
die_with_error ("Can't mkdir %s", op->dest); | die_with_error ("Can't mkdir %s", op->dest); | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_TMPFS_MOUNT, 0, op->perms, | PRIV_SEP_OP_TMPFS_MOUNT, 0, op->perms, op->size, | |||
dest, NULL); | dest, NULL); | |||
break; | break; | |||
case SETUP_MOUNT_MQUEUE: | case SETUP_MOUNT_MQUEUE: | |||
if (ensure_dir (dest, 0755) != 0) | if (ensure_dir (dest, 0755) != 0) | |||
die_with_error ("Can't mkdir %s", op->dest); | die_with_error ("Can't mkdir %s", op->dest); | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_MQUEUE_MOUNT, 0, 0, | PRIV_SEP_OP_MQUEUE_MOUNT, 0, 0, 0, | |||
dest, NULL); | dest, NULL); | |||
break; | break; | |||
case SETUP_MAKE_DIR: | case SETUP_MAKE_DIR: | |||
assert (dest != NULL); | assert (dest != NULL); | |||
assert (op->perms >= 0); | assert (op->perms >= 0); | |||
assert (op->perms <= 07777); | assert (op->perms <= 07777); | |||
if (ensure_dir (dest, op->perms) != 0) | if (ensure_dir (dest, op->perms) != 0) | |||
die_with_error ("Can't mkdir %s", op->dest); | die_with_error ("Can't mkdir %s", op->dest); | |||
skipping to change at line 1426 | skipping to change at line 1453 | |||
op->fd = -1; | op->fd = -1; | |||
assert (dest != NULL); | assert (dest != NULL); | |||
if (ensure_file (dest, 0444) != 0) | if (ensure_file (dest, 0444) != 0) | |||
die_with_error ("Can't create file at %s", op->dest); | die_with_error ("Can't create file at %s", op->dest); | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_BIND_MOUNT, | PRIV_SEP_OP_BIND_MOUNT, | |||
(op->type == SETUP_MAKE_RO_BIND_FILE ? BIND_READONLY : 0), | (op->type == SETUP_MAKE_RO_BIND_FILE ? BIND_READONLY : 0), | |||
0, tempfile, dest); | 0, 0, tempfile, dest); | |||
/* Remove the file so we're sure the app can't get to it in any othe r way. | /* Remove the file so we're sure the app can't get to it in any othe r way. | |||
Its outside the container chroot, so it shouldn't be possible, bu t lets | Its outside the container chroot, so it shouldn't be possible, bu t lets | |||
make it really sure. */ | make it really sure. */ | |||
unlink (tempfile); | unlink (tempfile); | |||
} | } | |||
break; | break; | |||
case SETUP_MAKE_SYMLINK: | case SETUP_MAKE_SYMLINK: | |||
assert (op->source != NULL); /* guaranteed by the constructor */ | assert (op->source != NULL); /* guaranteed by the constructor */ | |||
if (symlink (op->source, dest) != 0) | if (symlink (op->source, dest) != 0) | |||
die_with_error ("Can't make symlink at %s", op->dest); | die_with_error ("Can't make symlink at %s", op->dest); | |||
break; | break; | |||
case SETUP_SET_HOSTNAME: | case SETUP_SET_HOSTNAME: | |||
assert (op->dest != NULL); /* guaranteed by the constructor */ | assert (op->dest != NULL); /* guaranteed by the constructor */ | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_SET_HOSTNAME, 0, 0, | PRIV_SEP_OP_SET_HOSTNAME, 0, 0, 0, | |||
op->dest, NULL); | op->dest, NULL); | |||
break; | break; | |||
default: | default: | |||
die ("Unexpected type %d", op->type); | die ("Unexpected type %d", op->type); | |||
} | } | |||
} | } | |||
privileged_op (privileged_op_socket, | privileged_op (privileged_op_socket, | |||
PRIV_SEP_OP_DONE, 0, 0, NULL, NULL); | PRIV_SEP_OP_DONE, 0, 0, 0, NULL, NULL); | |||
} | } | |||
/* Do not leak file descriptors already used by setup_newroot () */ | /* Do not leak file descriptors already used by setup_newroot () */ | |||
static void | static void | |||
close_ops_fd (void) | close_ops_fd (void) | |||
{ | { | |||
SetupOp *op; | SetupOp *op; | |||
for (op = ops; op != NULL; op = op->next) | for (op = ops; op != NULL; op = op->next) | |||
{ | { | |||
skipping to change at line 1539 | skipping to change at line 1566 | |||
return (const char *) buffer + offset; | return (const char *) buffer + offset; | |||
} | } | |||
static uint32_t | static uint32_t | |||
read_priv_sec_op (int read_socket, | read_priv_sec_op (int read_socket, | |||
void *buffer, | void *buffer, | |||
size_t buffer_size, | size_t buffer_size, | |||
uint32_t *flags, | uint32_t *flags, | |||
uint32_t *perms, | uint32_t *perms, | |||
size_t *size_arg, | ||||
const char **arg1, | const char **arg1, | |||
const char **arg2) | const char **arg2) | |||
{ | { | |||
const PrivSepOp *op = (const PrivSepOp *) buffer; | const PrivSepOp *op = (const PrivSepOp *) buffer; | |||
ssize_t rec_len; | ssize_t rec_len; | |||
do | do | |||
rec_len = read (read_socket, buffer, buffer_size - 1); | rec_len = read (read_socket, buffer, buffer_size - 1); | |||
while (rec_len == -1 && errno == EINTR); | while (rec_len == -1 && errno == EINTR); | |||
skipping to change at line 1563 | skipping to change at line 1591 | |||
exit (1); /* Privileged helper died and printed error, so exit silently */ | exit (1); /* Privileged helper died and printed error, so exit silently */ | |||
if (rec_len < sizeof (PrivSepOp)) | if (rec_len < sizeof (PrivSepOp)) | |||
die ("Invalid size %zd from unprivileged helper", rec_len); | die ("Invalid size %zd from unprivileged helper", rec_len); | |||
/* Guarantee zero termination of any strings */ | /* Guarantee zero termination of any strings */ | |||
((char *) buffer)[rec_len] = 0; | ((char *) buffer)[rec_len] = 0; | |||
*flags = op->flags; | *flags = op->flags; | |||
*perms = op->perms; | *perms = op->perms; | |||
*size_arg = op->size_arg; | ||||
*arg1 = resolve_string_offset (buffer, rec_len, op->arg1_offset); | *arg1 = resolve_string_offset (buffer, rec_len, op->arg1_offset); | |||
*arg2 = resolve_string_offset (buffer, rec_len, op->arg2_offset); | *arg2 = resolve_string_offset (buffer, rec_len, op->arg2_offset); | |||
return op->op; | return op->op; | |||
} | } | |||
static void __attribute__ ((noreturn)) | static void __attribute__ ((noreturn)) | |||
print_version_and_exit (void) | print_version_and_exit (void) | |||
{ | { | |||
printf ("%s\n", PACKAGE_STRING); | printf ("%s\n", PACKAGE_STRING); | |||
exit (0); | exit (0); | |||
} | } | |||
static int | static int | |||
takes_perms (const char *next_option) | is_modifier_option (const char *option) | |||
{ | { | |||
static const char *const options_that_take_perms[] = | return strcmp (option, "--perms") == 0 | |||
{ | || strcmp(option, "--size") == 0; | |||
"--bind-data", | ||||
"--dir", | ||||
"--file", | ||||
"--ro-bind-data", | ||||
"--tmpfs", | ||||
}; | ||||
size_t i; | ||||
for (i = 0; i < N_ELEMENTS (options_that_take_perms); i++) | ||||
{ | ||||
if (strcmp (options_that_take_perms[i], next_option) == 0) | ||||
return 1; | ||||
} | ||||
return 0; | ||||
} | } | |||
static void | static void | |||
warn_only_last_option (const char *name) | warn_only_last_option (const char *name) | |||
{ | { | |||
warn ("Only the last %s option will take effect", name); | warn ("Only the last %s option will take effect", name); | |||
} | } | |||
static void | static void | |||
parse_args_recurse (int *argcp, | parse_args_recurse (int *argcp, | |||
skipping to change at line 1632 | skipping to change at line 1646 | |||
*/ | */ | |||
static const uint32_t MAX_ARGS = 9000; | static const uint32_t MAX_ARGS = 9000; | |||
if (*total_parsed_argc_p > MAX_ARGS) | if (*total_parsed_argc_p > MAX_ARGS) | |||
die ("Exceeded maximum number of arguments %u", MAX_ARGS); | die ("Exceeded maximum number of arguments %u", MAX_ARGS); | |||
while (argc > 0) | while (argc > 0) | |||
{ | { | |||
const char *arg = argv[0]; | const char *arg = argv[0]; | |||
if (next_perms >= 0 && !takes_perms (arg)) | ||||
die ("--perms must be followed by an option that creates a file"); | ||||
if (strcmp (arg, "--help") == 0) | if (strcmp (arg, "--help") == 0) | |||
{ | { | |||
usage (EXIT_SUCCESS, stdout); | usage (EXIT_SUCCESS, stdout); | |||
} | } | |||
else if (strcmp (arg, "--version") == 0) | else if (strcmp (arg, "--version") == 0) | |||
{ | { | |||
print_version_and_exit (); | print_version_and_exit (); | |||
} | } | |||
else if (strcmp (arg, "--args") == 0) | else if (strcmp (arg, "--args") == 0) | |||
{ | { | |||
skipping to change at line 1892 | skipping to change at line 1903 | |||
op = setup_op_new (SETUP_MOUNT_TMPFS); | op = setup_op_new (SETUP_MOUNT_TMPFS); | |||
op->dest = argv[1]; | op->dest = argv[1]; | |||
/* We historically hard-coded the mode of a tmpfs as 0755. */ | /* We historically hard-coded the mode of a tmpfs as 0755. */ | |||
if (next_perms >= 0) | if (next_perms >= 0) | |||
op->perms = next_perms; | op->perms = next_perms; | |||
else | else | |||
op->perms = 0755; | op->perms = 0755; | |||
next_perms = -1; | next_perms = -1; | |||
/* If the option is unset, next_size_arg is zero, which results in | ||||
* the default tmpfs size. This is exactly what we want. */ | ||||
op->size = next_size_arg; | ||||
next_size_arg = 0; | ||||
argv += 1; | argv += 1; | |||
argc -= 1; | argc -= 1; | |||
} | } | |||
else if (strcmp (arg, "--mqueue") == 0) | else if (strcmp (arg, "--mqueue") == 0) | |||
{ | { | |||
if (argc < 2) | if (argc < 2) | |||
die ("--mqueue takes an argument"); | die ("--mqueue takes an argument"); | |||
op = setup_op_new (SETUP_MOUNT_MQUEUE); | op = setup_op_new (SETUP_MOUNT_MQUEUE); | |||
op->dest = argv[1]; | op->dest = argv[1]; | |||
skipping to change at line 2385 | skipping to change at line 2403 | |||
argc -= 1; | argc -= 1; | |||
} | } | |||
else if (strcmp (arg, "--perms") == 0) | else if (strcmp (arg, "--perms") == 0) | |||
{ | { | |||
unsigned long perms; | unsigned long perms; | |||
char *endptr = NULL; | char *endptr = NULL; | |||
if (argc < 2) | if (argc < 2) | |||
die ("--perms takes an argument"); | die ("--perms takes an argument"); | |||
if (next_perms != -1) | ||||
die ("--perms given twice for the same action"); | ||||
perms = strtoul (argv[1], &endptr, 8); | perms = strtoul (argv[1], &endptr, 8); | |||
if (argv[1][0] == '\0' | if (argv[1][0] == '\0' | |||
|| endptr == NULL | || endptr == NULL | |||
|| *endptr != '\0' | || *endptr != '\0' | |||
|| perms > 07777) | || perms > 07777) | |||
die ("--perms takes an octal argument <= 07777"); | die ("--perms takes an octal argument <= 07777"); | |||
next_perms = (int) perms; | next_perms = (int) perms; | |||
argv += 1; | argv += 1; | |||
argc -= 1; | argc -= 1; | |||
} | } | |||
else if (strcmp (arg, "--size") == 0) | ||||
{ | ||||
unsigned long long size; | ||||
char *endptr = NULL; | ||||
if (is_privileged) | ||||
die ("The --size option is not permitted in setuid mode"); | ||||
if (argc < 2) | ||||
die ("--size takes an argument"); | ||||
if (next_size_arg != 0) | ||||
die ("--size given twice for the same action"); | ||||
errno = 0; /* reset errno so we can detect ERANGE from strtoull */ | ||||
size = strtoull (argv[1], &endptr, 0); | ||||
/* isdigit: Not only check that the first digit is not '\0', but | ||||
* simultaneously guard against negative numbers or preceding | ||||
* spaces. */ | ||||
if (errno != 0 /* from strtoull */ | ||||
|| !isdigit(argv[1][0]) | ||||
|| endptr == NULL | ||||
|| *endptr != '\0' | ||||
|| size == 0) | ||||
die ("--size takes a non-zero number of bytes"); | ||||
if (size > MAX_TMPFS_BYTES) | ||||
die ("--size (for tmpfs) is limited to %zu", MAX_TMPFS_BYTES); | ||||
next_size_arg = (size_t) size; | ||||
argv += 1; | ||||
argc -= 1; | ||||
} | ||||
else if (strcmp (arg, "--chmod") == 0) | else if (strcmp (arg, "--chmod") == 0) | |||
{ | { | |||
unsigned long perms; | unsigned long perms; | |||
char *endptr = NULL; | char *endptr = NULL; | |||
if (argc < 3) | if (argc < 3) | |||
die ("--chmod takes two arguments"); | die ("--chmod takes two arguments"); | |||
perms = strtoul (argv[1], &endptr, 8); | perms = strtoul (argv[1], &endptr, 8); | |||
skipping to change at line 2437 | skipping to change at line 2494 | |||
} | } | |||
else if (*arg == '-') | else if (*arg == '-') | |||
{ | { | |||
die ("Unknown option %s", arg); | die ("Unknown option %s", arg); | |||
} | } | |||
else | else | |||
{ | { | |||
break; | break; | |||
} | } | |||
/* If --perms was set for the current action but the current action | ||||
* didn't consume the setting, apparently --perms wasn't suitable for | ||||
* this action. */ | ||||
if (!is_modifier_option(arg) && next_perms >= 0) | ||||
die ("--perms must be followed by an option that creates a file"); | ||||
/* Similarly for --size. */ | ||||
if (!is_modifier_option(arg) && next_size_arg != 0) | ||||
die ("--size must be followed by --tmpfs"); | ||||
argv++; | argv++; | |||
argc--; | argc--; | |||
} | } | |||
*argcp = argc; | *argcp = argc; | |||
*argvp = argv; | *argvp = argv; | |||
} | } | |||
static void | static void | |||
parse_args (int *argcp, | parse_args (int *argcp, | |||
skipping to change at line 2792 | skipping to change at line 2859 | |||
if (pid == -1) | if (pid == -1) | |||
{ | { | |||
if (opt_unshare_user) | if (opt_unshare_user) | |||
{ | { | |||
if (errno == EINVAL) | if (errno == EINVAL) | |||
die ("Creating new namespace failed, likely because the kernel does not support user namespaces. bwrap must be installed setuid on such systems."); | die ("Creating new namespace failed, likely because the kernel does not support user namespaces. bwrap must be installed setuid on such systems."); | |||
else if (errno == EPERM && !is_privileged) | else if (errno == EPERM && !is_privileged) | |||
die ("No permissions to creating new namespace, likely because the k ernel does not allow non-privileged user namespaces. On e.g. debian this can be enabled with 'sysctl kernel.unprivileged_userns_clone=1'."); | die ("No permissions to creating new namespace, likely because the k ernel does not allow non-privileged user namespaces. On e.g. debian this can be enabled with 'sysctl kernel.unprivileged_userns_clone=1'."); | |||
} | } | |||
if (errno == ENOSPC) | ||||
die ("Creating new namespace failed: nesting depth or /proc/sys/user/max | ||||
_*_namespaces exceeded (ENOSPC)"); | ||||
die_with_error ("Creating new namespace failed"); | die_with_error ("Creating new namespace failed"); | |||
} | } | |||
ns_uid = opt_sandbox_uid; | ns_uid = opt_sandbox_uid; | |||
ns_gid = opt_sandbox_gid; | ns_gid = opt_sandbox_gid; | |||
if (pid != 0) | if (pid != 0) | |||
{ | { | |||
/* Parent, outside sandbox, privileged (initially) */ | /* Parent, outside sandbox, privileged (initially) */ | |||
skipping to change at line 3018 | skipping to change at line 3088 | |||
drop_privs (FALSE, TRUE); | drop_privs (FALSE, TRUE); | |||
close (privsep_sockets[0]); | close (privsep_sockets[0]); | |||
setup_newroot (opt_unshare_pid, privsep_sockets[1]); | setup_newroot (opt_unshare_pid, privsep_sockets[1]); | |||
exit (0); | exit (0); | |||
} | } | |||
else | else | |||
{ | { | |||
int status; | int status; | |||
uint32_t buffer[2048]; /* 8k, but is int32 to guarantee nice alignmen t */ | uint32_t buffer[2048]; /* 8k, but is int32 to guarantee nice alignmen t */ | |||
uint32_t op, flags, perms; | uint32_t op, flags, perms; | |||
size_t size_arg; | ||||
const char *arg1, *arg2; | const char *arg1, *arg2; | |||
cleanup_fd int unpriv_socket = -1; | cleanup_fd int unpriv_socket = -1; | |||
unpriv_socket = privsep_sockets[0]; | unpriv_socket = privsep_sockets[0]; | |||
close (privsep_sockets[1]); | close (privsep_sockets[1]); | |||
do | do | |||
{ | { | |||
op = read_priv_sec_op (unpriv_socket, buffer, sizeof (buffer), | op = read_priv_sec_op (unpriv_socket, buffer, sizeof (buffer), | |||
&flags, &perms, &arg1, &arg2); | &flags, &perms, &size_arg, &arg1, &arg2); | |||
privileged_op (-1, op, flags, perms, arg1, arg2); | privileged_op (-1, op, flags, perms, size_arg, arg1, arg2); | |||
if (write (unpriv_socket, buffer, 1) != 1) | if (write (unpriv_socket, buffer, 1) != 1) | |||
die ("Can't write to op_socket"); | die ("Can't write to op_socket"); | |||
} | } | |||
while (op != PRIV_SEP_OP_DONE); | while (op != PRIV_SEP_OP_DONE); | |||
waitpid (child, &status, 0); | waitpid (child, &status, 0); | |||
/* Continue post setup */ | /* Continue post setup */ | |||
} | } | |||
} | } | |||
else | else | |||
End of changes. 43 change blocks. | ||||
43 lines changed or deleted | 116 lines changed or added |