Skip to content

Commit

Permalink
Step 3: wrap a function requiring authority.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben Laurie committed Apr 28, 2012
1 parent 499e545 commit 9960df3
Show file tree
Hide file tree
Showing 4 changed files with 310 additions and 0 deletions.
25 changes: 25 additions & 0 deletions contrib/bzip2/CONVERSION
Expand Up @@ -11,3 +11,28 @@

Obviously we will now get errors because of the missing
functions. Add them to an appropriate new header.

3. Compile.

Now the first error is:

"bzip2.c:746: warning: 'applySavedFileAttrToOutputFile' defined but
not used"

Without even looking, it is apparent that this function must only
be used from the wrapped code.

This function requires authority (it needs to be able to chmod), so
we have a choice: grant that authority to the wrapped functions, or
create a capability for it. We choose the latter.

This is accomplished by allowing the child to call the function
remotely, by using RPC over a pipe. For now, we only implement the
parent's side of that - i.e. unmarshalling the arguments, calling
the function, and returning a return code.

A small amount of inspection reveals that only the output file
should be chmod()ed, so we enforce that.

At this point we also need to include utility code that will be
part of Capsicum in the future.
12 changes: 12 additions & 0 deletions contrib/bzip2/bzip2.c
Expand Up @@ -56,6 +56,8 @@
#include <ctype.h>
#include "bzlib.h"
#include "bzip2_wrapped.h"
#include "capsicum.h"
#include <assert.h>

#define ERROR_IF_EOF(i) { if ((i) == EOF) ioError(); }
#define ERROR_IF_NOT_ZERO(i) { if ((i) != 0) ioError(); }
Expand Down Expand Up @@ -759,6 +761,16 @@ void applySavedFileAttrToOutputFile ( IntNative fd )
# endif
}

static int output_fd;

static void unwrap_applySavedFileAttrToOutputFile(int fd) {
int ofd;

lc_read_int(fd, &ofd);
assert(ofd == output_fd);
applySavedFileAttrToOutputFile(ofd);
lc_write_void(fd);
}

/*---------------------------------------------*/
static
Expand Down
253 changes: 253 additions & 0 deletions contrib/bzip2/capsicum.c
@@ -0,0 +1,253 @@
// Candidates for libcapsicum

#include "capsicum.h"

#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/capability.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <unistd.h>

int lc_available(void) {
static int known;
static int available;

if (!known) {
known = 1;
cap_rights_t rights;
if (cap_getrights(0, &rights) == 0 || errno != ENOSYS)
available = 1;
}
return available;
}

static int lc_wrapped;
int lc_is_wrapped(void) { return lc_wrapped; }

static void lc_panic(const char * const msg) {
static int panicking;

if (lc_is_wrapped() && !panicking) {
panicking = 1;
lc_send_to_parent("void", "lc_panic", "int string", errno, msg);
exit(0);
}
fprintf(stderr, "lc_panic: %s\n", msg);
perror("lc_panic");
exit(1);
}

static
void lc_closeallbut(const int *fds, const int nfds) {
struct rlimit rl;
int fd;
int n;

if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
lc_panic("Can't getrlimit");

for (fd = 0; fd < rl.rlim_max; ++fd) {
for (n = 0; n < nfds; ++n)
if (fds[n] == fd)
goto next;
if (close(fd) < 0 && errno != EBADF) {
lc_panic("Can't close");
}
next:
continue;
}
}

static int lc_limitfd(int fd, cap_rights_t rights)
{
int fd_cap;
int error;

fd_cap = cap_new(fd, rights);
if (fd_cap < 0)
return -1;
if (dup2(fd_cap, fd) < 0) {
error = errno;
close(fd_cap);
errno = error;
return -1;
}
close(fd_cap);
return 0;
}

static int lc_parent_fd;

static void lc_write_string(int fd, const char *string) {
uint32_t size = strlen(string);
if (write(fd, &size, sizeof size) != sizeof size)
lc_panic("write failed");
if (write(fd, string, size) != size)
lc_panic("write failed");
}

static void lc_write_int(int fd, int n) {
if (write(fd, &n, sizeof n) != sizeof n)
lc_panic("write_int failed");
}

void lc_write_void(int fd) {
lc_write_int(fd, 0xdeadbeef);
}

static size_t lc_full_read(int fd, void *buffer, size_t count) {
size_t n;

for (n = 0; n < count; ) {
ssize_t r = read(fd, (char *)buffer + n, count - n);
if (r == 0)
return 0;
if (r < 0)
lc_panic("full_read");
n += r;
}
return n;
}

static int lc_read_string(int fd, char **result, uint32_t max) {
uint32_t size;

// FIXME: check for errors
if (lc_full_read(fd, &size, sizeof size) != sizeof size)
return 0;
fprintf(stderr, "Read string size %d\n", size);
if (size > max)
lc_panic("oversized string read");
*result = malloc(size + 1);
size_t n = lc_full_read(fd, *result, size);
if (n != size)
lc_panic("string read failed");
(*result)[size] = '\0';
return 1;
}

int lc_read_int(int fd, int *result) {
if (lc_full_read(fd, result, sizeof *result) != sizeof *result)
return 0;
return 1;
}

static int lc_read_void(int fd) {
unsigned v;

if (!lc_read_int(fd, &v))
return 0;
assert(v == 0xdeadbeef);
return 1;
}

void lc_send_to_parent(const char * const return_type,
const char * const function,
const char * const arg_types,
...) {
va_list ap;

assert(lc_is_wrapped());
va_start(ap, arg_types);
fprintf(stderr, "Send: %s\n", function);
lc_write_string(lc_parent_fd, function);
if (!strcmp(arg_types, "int"))
lc_write_int(lc_parent_fd, va_arg(ap, int));
else if (!strcmp(arg_types, "void"))
/* do nothing */;
else
assert(!"unknown arg_types");
assert(!strcmp(return_type, "void"));
lc_read_void(lc_parent_fd);
}

static void lc_process_messages(int fd, const struct lc_capability *caps,
size_t ncaps) {
for ( ; ; ) {
char *name;
size_t n;

if (!lc_read_string(fd, &name, 100))
return;

for (n = 0; n < ncaps; ++n)
if (!strcmp(caps[n].name, name)) {
caps[n].invoke(fd);
goto done;
}

fprintf(stderr, "Can't process capability \"%s\"\n", name);
lc_panic("bad capability");
done:
continue;
}
}

// FIXME: do this some other way, since we can't stop the child from
// using our code.
#define FILTER_EXIT 123
int lc_wrap_filter(int (*func)(FILE *in, FILE *out), FILE *in, FILE *out,
const struct lc_capability *caps, size_t ncaps) {
int ifd, ofd, pid, status;
int pfds[2];

ifd = fileno(in);
ofd = fileno(out);

if (pipe(pfds) < 0)
lc_panic("Cannot pipe");

if ((pid = fork()) < 0)
lc_panic("Cannot fork");

if (pid != 0) {
/* Parent process */
close(pfds[1]);
lc_process_messages(pfds[0], caps, ncaps);

wait(&status);
if(WIFEXITED(status)) {
status = WEXITSTATUS(status);
if (status != 0 && status != FILTER_EXIT)
lc_panic("Unexpected child status");
return status == 0;
} else {
lc_panic("Child exited abnormally");
}
} else {
/* Child process */
int fds[4];

lc_wrapped = 1;
close(pfds[0]);
lc_parent_fd = pfds[1];
if(lc_limitfd(ifd, CAP_READ | CAP_SEEK) < 0
|| lc_limitfd(ofd, CAP_WRITE | CAP_SEEK) < 0
// FIXME: CAP_SEEK should not be needed!
|| lc_limitfd(lc_parent_fd, CAP_READ | CAP_WRITE | CAP_SEEK) < 0) {
lc_panic("Cannot limit descriptors");
}
fds[0] = 2;
fds[1] = ofd;
fds[2] = ifd;
fds[3] = lc_parent_fd;
lc_closeallbut(fds, 4);

if (lc_available() && cap_enter() < 0)
lc_panic("cap_enter() failed");

if((*func)(in, out)) {
exit(0);
} else {
exit(FILTER_EXIT);
}
/* NOTREACHED */
}
/* NOTREACHED */
return 0;
}
20 changes: 20 additions & 0 deletions contrib/bzip2/capsicum.h
@@ -0,0 +1,20 @@
#include <stdio.h>

int lc_available(void);

void lc_send_to_parent(const char *return_type, const char *function,
const char *arg_types, ...);

int lc_is_wrapped(void);

struct lc_capability {
const char *name;
void (*invoke)(int fd);
};

int lc_wrap_filter(int (*func)(FILE *in, FILE *out), FILE *in, FILE *out,
const struct lc_capability *caps, size_t ncaps);

void lc_write_void(int fd);
int lc_read_int(int fd, int *result);

0 comments on commit 9960df3

Please sign in to comment.