Commit Graph

22 Commits

Author SHA1 Message Date
Michael Santos
888343e9dd Stream data from the alcove port
Handle framing in erlang code rather than relying on the {packet,2}
option to open_port/2. This change simplifies the port protocol codec
and should eventually allow easily calling exec() in the port.
2014-12-07 08:27:54 -05:00
Michael Santos
ef3faf49ca Specify OS PID return values 2014-07-07 10:05:51 -04:00
Michael Santos
296cdc911e seccomp/allow_syscall: remove the call to port
The macro should be a "pure" function. Remove the call to the port. This
forces the caller to catch the case where a syscall does not exist on a
particular platform.
2014-06-21 10:33:21 -04:00
Michael Santos
7ea1404ebb seccomp: move macros to header
Move the BPF generation macros to the header file. This needs further
cleanup:

* some of the macros require the port pid, others do not

* some of the macros refer to functions

* some of the raw BPF statements should be wrapped in a function or
  macro
2014-06-20 10:53:37 -04:00
Michael Santos
b0f9e5336e alcove_list_to_buf: include static binaries
Return information about the length of unallocated binaries so the
caller can return a list including the contents of the pointers, rather
than a binary including the pointers themselves.
2014-06-14 10:27:42 -04:00
Michael Santos
6bfe49ec6c seccomp: add functions to generate BPF bytecode
ERL_LIBS was pulling in the bpf functions from procket. Copy/paste of
bpf functions from procket into alcove_seccomp. Amazingly, seccomp mode
worked out of the box. Still needs a lot of clean up, as well as a
higher level, simpler, friendlier library to wrap it all up.

The biggest change to follow will be altering the return value of prctl.
Right now, passing in a struct (represented as a list of binaries and tuples)
will return a binary. This isn't very useful because the binary contains
the pointer to the value, not the value itself. With seccomp mode, this
value isn't updated anyway.

prctl/6,7 will change to returning the contents of the pointer. So if a
struct is passed in like:

    [<<"some data">>, {ptr, 6}, {ptr, <<"stuff">>}, <<"foo">>]

The return value will look like:

    [<<"some data">>, {ptr, <<1,2,3,4,5,6>>}, {ptr, <<"stuff">>}, <<"foo">>]
2014-06-10 08:00:08 -04:00
Michael Santos
fa07b11b85 Add macros for Linux seccomp mode
Macros to create bpf instructions, taken from procket.

A minimal example (set up for little endian ARM):

-module(filt).
-export([enforce/2,enforce/3]).

-include_lib("alcove/include/alcove.hrl").
-include_lib("alcove/include/alcove_seccomp.hrl").

-define(EM_ARM, 40).
-define(__AUDIT_ARCH_LE, 16#40000000).
-define(AUDIT_ARCH_ARM, ?EM_ARM bxor ?__AUDIT_ARCH_LE).

-define(OFFSET_SYSCALL_NR, 0).
-define(OFFSET_ARCH_NR, 4).

-define(VALIDATE_ARCHITECTURE(Offset_arch_nr, Arch_nr), [
        ?BPF_STMT(?BPF_LD+?BPF_W+?BPF_ABS, Offset_arch_nr),
        ?BPF_JUMP(?BPF_JMP+?BPF_JEQ+?BPF_K, Arch_nr, 1, 0),
        ?BPF_STMT(?BPF_RET+?BPF_K, ?SECCOMP_RET_KILL)
    ]).

-define(EXAMINE_SYSCALL(Offset_syscall_nr),
    ?BPF_STMT(?BPF_LD+?BPF_W+?BPF_ABS, Offset_syscall_nr)).

-define(ALLOW_SYSCALL(Syscall_nr), [
        ?BPF_JUMP(?BPF_JMP+?BPF_JEQ+?BPF_K, Syscall_nr, 0, 1),
        ?BPF_STMT(?BPF_RET+?BPF_K, ?SECCOMP_RET_ALLOW)
    ]).

-define(NR_exit, 1).
-define(NR_read, 3).
-define(NR_write, 4).
-define(NR_setrlimit, 75).
-define(NR_getrlimit, 76).
-define(NR_sigreturn, 119).
-define(NR_poll, 168).
-define(NR_rt_sigreturn, 173).
-define(NR_ugetrlimit, 191).
-define(NR_exit_group, 248).

filter() ->
    [
        ?VALIDATE_ARCHITECTURE(?OFFSET_ARCH_NR, ?AUDIT_ARCH_ARM),
        ?EXAMINE_SYSCALL(?OFFSET_SYSCALL_NR),
        ?ALLOW_SYSCALL(?NR_rt_sigreturn),
        ?ALLOW_SYSCALL(?NR_sigreturn),
        ?ALLOW_SYSCALL(?NR_exit_group),
        ?ALLOW_SYSCALL(?NR_exit),
        ?ALLOW_SYSCALL(?NR_read),
        ?ALLOW_SYSCALL(?NR_write),
        ?ALLOW_SYSCALL(?NR_setrlimit),
        ?ALLOW_SYSCALL(?NR_getrlimit),
        ?ALLOW_SYSCALL(?NR_ugetrlimit),
        ?ALLOW_SYSCALL(?NR_poll),
        ?BPF_STMT(?BPF_RET+?BPF_K, ?SECCOMP_RET_KILL)
    ].

enforce(Drv, Pids) ->
    enforce(Drv, Pids, filter()).

enforce(Drv, Pids, Filter) ->
    PR_SET_NO_NEW_PRIVS = alcove:define(Drv, 'PR_SET_NO_NEW_PRIVS'),
    PR_SET_SECCOMP = alcove:define(Drv, 'PR_SET_SECCOMP'),
    SECCOMP_MODE_FILTER = alcove:define(Drv, 'SECCOMP_MODE_FILTER'),

    {ok,_,_,_,_,_} = alcove:prctl(Drv, Pids, PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0),

    Prog = [
        <<(iolist_size(Filter) div 8):2/native-unsigned-integer-unit:8>>,
        <<0:16>>,
        {ptr, list_to_binary(Filter)}
    ],
    {ok,_,_,_,_,_} = alcove:prctl(Drv, Pids,
        PR_SET_SECCOMP, SECCOMP_MODE_FILTER, Prog, 0, 0),
    ok.
```

And running it:

```
1> {ok,Drv} = alcove_drv:start([termsig]).
{ok,<0.44.0>}
2> {ok,Fork} = alcove:fork(Drv).
{ok,19125}
3> filt:enforce(Drv, [Fork]).
ok
4> alcove:version(Drv, [Fork]).
<<"0.5.0">>
5> alcove:getpid(Drv, [Fork]).
19125
6> alcove:fork(Drv, [Fork]).
ok
7> alcove:event(Drv, [Fork]).
{termsig,31}
8> alcove:signal_constant(Drv, 31).
'SIGSYS'
```
2014-06-06 14:35:43 -04:00
Michael Santos
845cf6ec1e Support allocating C structs containing pointers
Provide an interace for creating buffers with embedded pointers for
seccomp mode. Seccomp mode uses bpf programs which look like:

    struct sock_fprog {
        unsigned short len;
        struct sock_filter *filter;
    };

The caller can provide generate the structure using a list:

    [
        % 8 instructions in filter
        <<8:2/native-unsigned-integer-unit:8>>,
        % Pad
        <<0:16>>,
        % Generate a binary containing the BPF instructions
        {ptr, bpf_filter()}
    ]

Rough, buggy code to get started testing seccomp mode. In particular,
the contents of the allocated buffers is write only currently. Some of
the prctl() calls are probably in/out.
2014-06-02 16:07:24 -04:00
Michael Santos
28853b059d Add select(2) 2014-05-10 15:33:29 -04:00
Michael Santos
310b246e2d Use a namespace: #rlimit{} -> #alcove_rlimit{} 2014-05-09 10:41:38 -04:00
Michael Santos
26a207d876 Stream messages from port through a proxy process
Convert the binary format messages from the port into erlang terms using
a gen_server.
2014-04-22 14:16:06 -04:00
Michael Santos
4d77f13131 eof/2,3: close stdin of a process 2014-04-19 16:13:56 -04:00
Michael Santos
86bf475fbf Add convenience macros for stdio 2014-03-27 16:21:50 -04:00
Michael Santos
86ceca06b1 headers: mirror numbering of Unix stdio
Change the order of the message header types so that STDIN is 0, STDOUT
is 1 and STDERR is 3.
2014-03-27 14:50:51 -04:00
Michael Santos
d60a653689 Protocol changes for exec()
Since stdout is used for control and for data, add a new message type
(proxy) to distinguish them.

When a child calls exec():

* if the call is unsuccessful, the child writes a term to stdout which
  the parent proxies

* if the call is successful, no value is returned but the child will close
  the control socket on exec. The parent will "spoof" an "ok" response.

Allocate the message headers on the heap to make it easier to layer them.

If the child process has not exec'ed, use the packet length to avoid
having multiple writes coalesce into a single read.

Before checking if an fd is ready, ensure the fd is a positive value. On
freebsd, invalid fd's cause FD_ISSET() to segfault.
2014-03-23 08:20:26 -04:00
Michael Santos
0167a3a9df Correct headers for stderr 2014-03-21 10:40:44 -04:00
Michael Santos
82c527aa91 Use macros to generate header pattern matches 2014-03-18 17:28:33 -04:00
Michael Santos
fc7a27edda Rename ALCOVE_MSG_CAST -> ALCOVE_MSG_EVENT
Remove the unused function alcove_drv:event/1,2.
2014-03-15 11:23:18 -04:00
Michael Santos
fed8f44a68 Use standard names for standard I/O messages 2014-03-08 14:55:25 -05:00
Michael Santos
4036daca80 Support multiple processes
Modify the message format to include the PID. Tests pass but requires
much more work. The message format is now:

* erlang -> port: make a call

    2:ALCOVE_MSG_CALL
    2:call
    *:arg

* port -> erlang: call results

    2:ALCOVE_MSG_CALL
    *:arg

* erlang -> port: write to child's stdin

    2:ALCOVE_MSG_CHILDIN
    4:pid

* port -> erlang: write from child's stdout

    2:ALCOVE_MSG_CHILDOUT
    4:pid
    *:data

* port -> erlang: write from child's stderr

    2:ALCOVE_MSG_CHILDERR
    4:pid
    *:data
2014-03-07 16:36:08 -05:00
Michael Santos
8f40d16eb1 Add support for setrlimit(2)/getrlimit(2) 2014-03-02 10:31:50 -05:00
Michael Santos
a9245bae00 alcove: sandbox for erlang ports
alcove acts a proxy between the Erlang VM and a forked process. It can
enforce restrictions on the child process, such as dropping privileges,
setting resource limits and chroot'ing.

The goal is to support Linux namespaces, seccomp mode and cgroups so
the port process can run in an application container.

alcove should be portable though and a subset of the features should
work on any unix, possibly even supporting the sandboxing mechanisms on
other platforms.
2014-03-02 08:59:33 -05:00