char * do_command (char *const *argv) { char *out; CLEANUP_FREE char *err = NULL; int r, flags; CLEANUP_BIND_STATE struct bind_state bind_state = { .mounted = false }; CLEANUP_RESOLVER_STATE struct resolver_state resolver_state = { .mounted = false }; /* We need a root filesystem mounted to do this. */ NEED_ROOT (, return NULL); /* Conveniently, argv is already a NULL-terminated argv-style array * of parameters, so we can pass it straight in to our internal * commandv. We just have to check the list is non-empty. */ if (argv[0] == NULL) { reply_with_error ("passed an empty list"); return NULL; } if (bind_mount (&bind_state) == -1) return NULL; if (enable_network) { if (set_up_etc_resolv_conf (&resolver_state) == -1) return NULL; } flags = COMMAND_FLAG_DO_CHROOT; r = commandvf (&out, &err, flags, (const char * const *) argv); free_bind_state (&bind_state); free_resolver_state (&resolver_state); if (r == -1) { reply_with_error ("%s", err); free (out); return NULL; } return out; /* Caller frees. */ } char ** do_command_lines (char *const *argv) { CLEANUP_FREE char *out = NULL; char **lines; out = do_command (argv); if (out == NULL) return NULL; lines = split_lines (out); if (lines == NULL) return NULL; return lines; /* Caller frees. */ } char * do_sh (const char *cmd) { const char *argv[] = { "/bin/sh", "-c", cmd, NULL }; return do_command ((char **) argv); } char ** do_sh_lines (const char *cmd) { const char *argv[] = { "/bin/sh", "-c", cmd, NULL }; return do_command_lines ((char **) argv); }
void bind_mount_host(const gchar *host, const gchar *guest) { g_autofree gchar *point = g_build_filename(guest, "host", NULL); bind_mount(host, point); }
static void privileged_op (int privileged_op_socket, uint32_t op, uint32_t flags, const char *arg1, const char *arg2) { if (privileged_op_socket != -1) { uint32_t buffer[2048]; /* 8k, but is int32 to guarantee nice alignment */ PrivSepOp *op_buffer = (PrivSepOp *)buffer; size_t buffer_size = sizeof (PrivSepOp); uint32_t arg1_offset = 0, arg2_offset = 0; if (arg1 != NULL) { arg1_offset = buffer_size; buffer_size += strlen (arg1) + 1; } if (arg2 != NULL) { arg2_offset = buffer_size; buffer_size += strlen (arg2) + 1; } if (buffer_size >= sizeof (buffer)) die ("privilege separation operation to large"); op_buffer->op = op; op_buffer->flags = flags; op_buffer->arg1_offset = arg1_offset; op_buffer->arg2_offset = arg2_offset; if (arg1 != NULL) strcpy ((char *)buffer + arg1_offset, arg1); if (arg2 != NULL) strcpy ((char *)buffer + arg2_offset, arg2); if (write (privileged_op_socket, buffer, buffer_size) != buffer_size) die ("Can't write to privileged_op_socket"); if (read (privileged_op_socket, buffer, 1) != 1) die ("Can't read from privileged_op_socket"); return; } switch (op) { case PRIV_SEP_OP_DONE: break; case PRIV_SEP_OP_BIND_MOUNT: /* We always bind directories recursively, otherwise this would let us access files that are otherwise covered on the host */ if (bind_mount (proc_fd, arg1, arg2, BIND_RECURSIVE | flags) != 0) die_with_error ("Can't bind mount %s on %s", arg1, arg2); break; case PRIV_SEP_OP_PROC_MOUNT: if (mount ("proc", arg1, "proc", MS_MGC_VAL|MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0) die_with_error ("Can't mount proc on %s", arg1); break; case PRIV_SEP_OP_TMPFS_MOUNT: { cleanup_free char *opt = label_mount ("mode=0755", opt_file_label); if (mount ("tmpfs", arg1, "tmpfs", MS_MGC_VAL | MS_NOSUID | MS_NODEV, opt) != 0) die_with_error ("Can't mount tmpfs on %s", arg1); break; } case PRIV_SEP_OP_DEVPTS_MOUNT: if (mount ("devpts", arg1, "devpts", MS_MGC_VAL | MS_NOSUID | MS_NOEXEC, "newinstance,ptmxmode=0666,mode=620") != 0) die_with_error ("Can't mount devpts on %s", arg1); break; case PRIV_SEP_OP_MQUEUE_MOUNT: if (mount ("mqueue", arg1, "mqueue", 0, NULL) != 0) die_with_error ("Can't mount mqueue on %s", arg1); break; default: die ("Unexpected privileged op %d", op); } }