Skip to content

Commit

Permalink
WIP: cap_enter
Browse files Browse the repository at this point in the history
  • Loading branch information
talex5 committed Feb 19, 2024
1 parent b897454 commit bf0003b
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 8 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,24 @@ A program that operates on the current directory will probably want to use `cwd`
whereas a program that accepts a path from the user will probably want to use `fs`,
perhaps with `open_dir` to constrain all access to be within that directory.

On systems that provide the `cap_enter` system call, you can ask the OS to enforce this.
See <examples/capsicum> for an example.
This opens the directory given on the command line, calls `cap_enter` to drop privileges,
uses the directory, and then tries reading `/etc/passwd` using the standard library.
Running on FreeBSD, you should see:

```
mkdir /tmp/cap
dune exec -- ./examples/capsicum/main.exe /tmp/cap
+Opening directory <fs:/tmp/cap>...
+Capsicum mode enabled
+Using the file-system via the directory resource works:
+Writing <cap:capsicum-test.txt>...
+Read: "A test file"
+Bypassing Eio and accessing other resources should fail in Capsicum mode:
Fatal error: exception Sys_error("/etc/passwd: Not permitted in capability mode")
```

## Running processes

Spawning a child process can be done using the [Eio.Process][] module:
Expand Down
3 changes: 3 additions & 0 deletions examples/capsicum/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(executable
(name main)
(libraries eio_main))
40 changes: 40 additions & 0 deletions examples/capsicum/main.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
open Eio.Std

let ( / ) = Eio.Path.( / )

let test_eio dir =
traceln "Using the file-system via the directory resource works:";
let test_file = dir / "capsicum-test.txt" in
traceln "Writing %a..." Eio.Path.pp test_file;
Eio.Path.save test_file "A test file" ~create:(`Exclusive 0o644);
traceln "Read: %S" (Eio.Path.load test_file);
Eio.Path.unlink test_file

let test_legacy () =
traceln "Bypassing Eio and accessing other resources should fail in Capsicum mode:";
let ch = open_in "/etc/passwd" in
let len = in_channel_length ch in
let data = really_input_string ch len in
close_in ch;
traceln "Was able to read /etc/passwd:@.%s" (String.trim data)

let () =
Eio_main.run @@ fun env ->
(* Parse command-line arguments *)
let path =
match Sys.argv with
| [| _; dir |] -> Eio.Stdenv.fs env / dir
| _ -> failwith "Usage: main.exe DIR"
in
if not (Eio.Path.is_directory path) then Fmt.failwith "%a is not a directory" Eio.Path.pp path;
(* Get access to resources before calling cap_enter: *)
Eio.Path.with_open_dir path @@ fun dir ->
traceln "Opened directory %a" Eio.Path.pp path;
(* Switch to capability mode, if possible: *)
begin match Eio_unix.Cap.enter () with
| Ok () -> traceln "Capsicum mode enabled"
| Error `Not_supported -> traceln "!! CAPSICUM PROTECTION NOT AVAILABLE !!"
end;
(* Run tests: *)
test_eio dir;
test_legacy ()
15 changes: 9 additions & 6 deletions lib_eio/unix/cap.c
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
#include "primitives.h"

#define HAVE_CAP_ENTER

#include <errno.h>
#include <sys/param.h>

#ifdef __FreeBSD__
# define HAVE_CAPSICUM
#endif

#ifdef HAVE_CAP_ENTER
#ifdef HAVE_CAPSICUM
# include <sys/capsicum.h>
#endif

#include <caml/mlvalues.h>
#include <caml/unixsupport.h>

CAMLprim value eio_unix_cap_enter(value v_unit) {
#ifdef HAVE_CAP_ENTER
#ifdef HAVE_CAPSICUM
int r = cap_enter();
if (r == -1 && errno != ENOSYS)
caml_uerror("cap_enter", Nothing);

return Val_bool(r == 0)
return Val_bool(r == 0);
#else
return Val_bool(0)
return Val_bool(0);
#endif
}
2 changes: 1 addition & 1 deletion lib_eio/unix/cap.ml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
external eio_cap_enter : unit -> bool = "eio_unix_cap_enter"

let cap_enter () =
let enter () =
if eio_cap_enter () then Ok ()
else Error `Not_supported
2 changes: 1 addition & 1 deletion lib_eio/unix/cap.mli
Original file line number Diff line number Diff line change
@@ -1 +1 @@
val cap_enter : unit -> (unit, [`Not_supported]) result
val enter : unit -> (unit, [`Not_supported]) result

0 comments on commit bf0003b

Please sign in to comment.