Skip to content

Commit

Permalink
Fix #1 - add unveil(2) support (#2)
Browse files Browse the repository at this point in the history
* Working on unveil support

* Fix spelling mistake

* Remove check when expecting failure

* Use Nim v1.0.0 for CI

* Always trigger email, revert to Nim 0.20.2 as 1.0.0 is failing to compile
  • Loading branch information
euantorano authored Sep 30, 2019
1 parent 5a516b4 commit 5935629
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .builds/openbsd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ tasks:
env PATH="$HOME/nim-${NIM_VERSION}/bin:$PATH" nim c --cc:clang -r tests/main.nim
triggers:
- action: email
condition: failure
condition: always
to: Euan T <[email protected]>
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# pledge.nim [![builds.sr.ht status](https://builds.sr.ht/~euantorano.svg?search=pledge.nim)](https://builds.sr.ht/~euantorano?search=pledge.nim)

A wrapper around OpenBSD's `pledge(2)` systemcall for Nim.
A wrapper around OpenBSD's `pledge(2)` system call for Nim.

Includes support for OpenBSD's `unveil(2)` system call.

## Installation

Expand All @@ -15,7 +17,7 @@ Or add the following to your `.nimble` file:
```
# Dependencies
requires "pledge >= 1.1.0"
requires "pledge >= 2.0.0"
```

## [Documentation](https://htmlpreview.github.io/?https://github.com/euantorano/pledge.nim/blob/master/docs/pledge.html)
Expand All @@ -27,6 +29,6 @@ import pledge
pledge(Promises.Stdio)
# As we haven't used pledge to ask to access files, the below will cause the program to be temrinated with a SIGABRT.
# As we haven't used pledge to ask to access files, the below will cause the program to be terminated with a SIGABRT.
let f = open("/etc/rc.conf")
```
31 changes: 30 additions & 1 deletion docs/pledge.html
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,9 @@ <h1 class="title">pledge</h1>
Recvfd = &quot;recvfd&quot;, Tape = &quot;tape&quot;, Tty = &quot;tty&quot;, Proc = &quot;proc&quot;, Exec = &quot;exec&quot;,
ProtExec = &quot;prot_exec&quot;, Settime = &quot;settime&quot;, Ps = &quot;ps&quot;, Vminfo = &quot;vminfo&quot;, Id = &quot;id&quot;,
Pf = &quot;pf&quot;, Audio = &quot;audio&quot;, Video = &quot;video&quot;, Bpf = &quot;bpf&quot;, Unveil = &quot;unveil&quot;, Error = &quot;error&quot;"><wbr />Promise<span class="attachedType"></span></a></li>
<li><a class="reference" href="#Permission"
title="Permission {.pure.} = enum
CreateRemove = &apos;c&apos;, Read = &apos;r&apos;, Write = &apos;w&apos;, Execute = &apos;x&apos;"><wbr />Permission<span class="attachedType"></span></a></li>

</ul>
</li>
Expand All @@ -844,6 +847,8 @@ <h1 class="title">pledge</h1>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#pledge%2COption%5Bstring%5D%2COption%5Bstring%5D"
title="pledge(promises: Option[string]; execPromises: Option[string] = none(string))"><wbr />pledge<span class="attachedType"></span></a></li>
<li><a class="reference" href="#unveil%2COption%5Bstring%5D%2COption%5Bstring%5D"
title="unveil(path: Option[string]; permissions: Option[string])"><wbr />unveil<span class="attachedType"></span></a></li>

</ul>
</li>
Expand All @@ -852,6 +857,8 @@ <h1 class="title">pledge</h1>
<ul class="simple simple-toc-section">
<li><a class="reference" href="#pledge.t%2Cvarargs%5BPromise%5D"
title="pledge(promises: varargs[Promise])"><wbr />pledge<span class="attachedType"></span></a></li>
<li><a class="reference" href="#unveil.t%2Cstring%2Cset%5BPermission%5D"
title="unveil(path: string; permissions: set[Permission])"><wbr />unveil<span class="attachedType"></span></a></li>

</ul>
</li>
Expand Down Expand Up @@ -888,6 +895,14 @@ <h1><a class="toc-backref" href="#7">Types</a></h1>

The possible operation sets that a program can pledge to be limited to.

</dd>
<a id="Permission"></a>
<dt><pre><a href="pledge.html#Permission"><span class="Identifier">Permission</span></a> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">pure</span></span><span class="Other">.}</span></span> <span class="Other">=</span> <span class="Keyword">enum</span>
<span class="Identifier">CreateRemove</span> <span class="Other">=</span> <span class="CharLit">'c'</span><span class="Other">,</span> <span class="Identifier">Read</span> <span class="Other">=</span> <span class="CharLit">'r'</span><span class="Other">,</span> <span class="Identifier">Write</span> <span class="Other">=</span> <span class="CharLit">'w'</span><span class="Other">,</span> <span class="Identifier">Execute</span> <span class="Other">=</span> <span class="CharLit">'x'</span></pre></dt>
<dd>

The possible permissions that a call to <tt class="docutils literal"><span class="pre">unveil</span></tt> can be used with.

</dd>

</dl></div>
Expand All @@ -903,6 +918,13 @@ <h1><a class="toc-backref" href="#12">Procs</a></h1>
<p>If no promises are provided, the process will be restricted to the <tt class="docutils literal"><span class="pre">_exit(2)</span></tt> system call.</p>


</dd>
<a id="unveil,Option[string],Option[string]"></a>
<dt><pre><span class="Keyword">proc</span> <a href="#unveil%2COption%5Bstring%5D%2COption%5Bstring%5D"><span class="Identifier">unveil</span></a><span class="Other">(</span><span class="Identifier">path</span><span class="Other">:</span> <span class="Identifier">Option</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">;</span> <span class="Identifier">permissions</span><span class="Other">:</span> <span class="Identifier">Option</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
<dd>

Unveil parts of a restricted filesystem view.

</dd>

</dl></div>
Expand All @@ -917,6 +939,13 @@ <h1><a class="toc-backref" href="#18">Templates</a></h1>
<p>This template takes a list of <tt class="docutils literal"><span class="pre">Promise</span></tt>, creates the required promise string and emits a call to the <tt class="docutils literal"><span class="pre">pledge</span></tt> proc.</p>


</dd>
<a id="unveil.t,string,set[Permission]"></a>
<dt><pre><span class="Keyword">template</span> <a href="#unveil.t%2Cstring%2Cset%5BPermission%5D"><span class="Identifier">unveil</span></a><span class="Other">(</span><span class="Identifier">path</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">;</span> <span class="Identifier">permissions</span><span class="Other">:</span> <span class="Identifier">set</span><span class="Other">[</span><a href="pledge.html#Permission"><span class="Identifier">Permission</span></a><span class="Other">]</span><span class="Other">)</span></pre></dt>
<dd>

Unveil parts of a restricted filesystem view.

</dd>

</dl></div>
Expand All @@ -928,7 +957,7 @@ <h1><a class="toc-backref" href="#18">Templates</a></h1>
<div class="twelve-columns footer">
<span class="nim-sprite"></span>
<br/>
<small>Made with Nim. Generated: 2019-08-20 12:07:44 UTC</small>
<small>Made with Nim. Generated: 2019-09-30 19:55:46 UTC</small>
</div>
</div>
</div>
Expand Down
3 changes: 3 additions & 0 deletions docs/pledge.idx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
Example of making a single promise pledge.html#example-of-making-a-single-promise Example of making a single promise
Example of making several promises pledge.html#example-of-making-several-promises Example of making several promises
Promise pledge.html#Promise pledge: Promise
Permission pledge.html#Permission pledge: Permission
pledge pledge.html#pledge,Option[string],Option[string] pledge: pledge(promises: Option[string]; execPromises: Option[string] = none(string))
unveil pledge.html#unveil,Option[string],Option[string] pledge: unveil(path: Option[string]; permissions: Option[string])
pledge pledge.html#pledge.t,varargs[Promise] pledge: pledge(promises: varargs[Promise])
unveil pledge.html#unveil.t,string,set[Permission] pledge: unveil(path: string; permissions: set[Permission])
4 changes: 2 additions & 2 deletions pledge.nimble
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Package

version = "1.1.1"
version = "2.0.0"
author = "Euan T"
description = "A wrapper around OpenBSD's pledge(2) systemcall for Nim."
license = "BSD3"
Expand All @@ -9,7 +9,7 @@ srcDir = "src"

# Dependencies

requires "nim >= 0.13.0"
requires "nim >= 1.0.0"

task docs, "Build documentation":
exec "nim doc2 --index:on -o:docs/pledge.html src/pledge.nim"
48 changes: 43 additions & 5 deletions src/pledge.nim
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
##
## pledge(Promise.Stdio, Promise.Rpath)

import os, options
import options

type Promise* {.pure.} = enum
## The possible operation sets that a program can pledge to be limited to.
Expand Down Expand Up @@ -58,15 +58,26 @@ type Promise* {.pure.} = enum
Unveil = "unveil",
Error = "error"

type Permission* {.pure.} = enum
## The possible permissions that a call to `unveil` can be used with.
CreateRemove = 'c'
Read = 'r'
Write = 'w'
Execute = 'x'

when defined(nimdoc) or not defined(openbsd):
proc pledge*(promises: Option[string], execPromises: Option[string] = none(string)) = discard
## Pledge to use only the defined functions.
##
## If no promises are provided, the process will be restricted to the `_exit(2)` system call.

proc unveil*(path: Option[string], permissions: Option[string]) = discard
## Unveil parts of a restricted filesystem view.
elif defined(openbsd):
import posix_utils, strformat
import os, posix_utils, strformat

proc pledge_c(promises: cstring, execpromises: cstring): cint {.importc: "pledge", header: "<unistd.h>".}
proc unveil_c(path: cstring, permissions: cstring): cint {.importc: "unveil", header: "<unistd.h>".}

type
OpenBsdVersion = object
Expand All @@ -75,6 +86,8 @@ elif defined(openbsd):
PledgeException* = object of Exception
PledgeNotAvailableError* = object of PledgeException
PledgeExecPromisesNotAvailableError* = object of PledgeException
UnveilException* = object of Exception
UnveilNotAvailableException* = object of UnveilException

proc getOpenBsdVersion(): OpenBsdVersion =
let uname = uname()
Expand All @@ -101,7 +114,7 @@ elif defined(openbsd):
if (osVersion.major < 6 or (osVersion.major == 6 and osVersion.minor <= 2)) and execPromises.isSome():
raise newException(PledgeExecPromisesNotAvailableError, &"cannot use execpromises with pledge(2) on OpenBSD {osVersion.major}.{osVersion.minor}")

var promisesValue: cstring = if promises.isSome(): cstring(promises.get()) else: nil
let promisesValue: cstring = if promises.isSome(): cstring(promises.get()) else: nil
var execPromisesValue: cstring = nil

# if running on openBSD <= 6.2, execpromises should be passed as NULL
Expand All @@ -111,9 +124,18 @@ elif defined(openbsd):
if pledge_c(promisesValue, execPromisesValue) != 0:
raiseOSError(osLastError())

proc getPromisesString(promises: openarray[Promise]): string {.compiletime.} =
result = ""
proc unveil*(path: Option[string], permissions: Option[string]) =
## Unveil parts of a restricted filesystem view.
if (osVersion.major == 6 and osVersion.minor < 4) or osVersion.major < 6:
raise newException(UnveilNotAvailableException, &"unveil(2) system call is not available on OpenBSD {osVersion.major}.{osVersion.minor}")

let pathValue: cstring = if path.isSome(): cstring(path.get()) else: nil
let permissionsValue: cstring = if permissions.isSome(): cstring(permissions.get()) else: nil

if unveil_c(pathValue, permissionsValue) != 0:
raiseOSError(osLastError())

proc getPromisesString(promises: openarray[Promise]): string {.compiletime.} =
var
promiseSet: set[Promise] = {}
sep = ""
Expand All @@ -126,6 +148,17 @@ proc getPromisesString(promises: openarray[Promise]): string {.compiletime.} =

sep = " "

proc getPermissionsString(permissions: set[Permission]): Option[string] {.compiletime.} =
var s = ""

for p in permissions:
s.add(char(p))

if len(s) > 0:
result = some(s)
else:
result = none(string)

template pledge*(promises: varargs[Promise]) =
## Pledge to use only the defined functions.
##
Expand All @@ -135,3 +168,8 @@ template pledge*(promises: varargs[Promise]) =
pledge(some(promisesString))
else:
pledge(none(string))

template unveil*(path: string, permissions: set[Permission]) =
## Unveil parts of a restricted filesystem view.
let pathValue = if len(path) > 0: some(path) else: none(string)
unveil(pathValue, getPermissionsString(permissions))
23 changes: 20 additions & 3 deletions tests/main.nim
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
import pledge, unittest, os

suite "unveil tests":
test "can access path that is unveiled":
unveil("/dev/urandom", {Permission.Read})

var f: File
check open(f, "/dev/urandom", fmRead)
var buff: array[0..9, byte]
check f.readBytes(buff, 0, len(buff)) > 0

when defined(openbsd):
test "can't access path that isn't unveiled":
unveil("/dev/urandom", {Permission.Read})
unveil("", {})

var f: File
check not open(f, "/dev/zero", fmRead)

suite "pledge tests":
test "can pledge":
pledge(Promise.Stdio, Promise.Rpath)
pledge(Promise.Stdio, Promise.Rpath, Promise.Unveil)

check true

when defined(openbsd):
test "can not elevate":
pledge(Promise.Stdio)
pledge(Promise.Stdio, Promise.Unveil)

expect OSError:
pledge(Promise.Stdio, Promise.Rpath)
pledge(Promise.Stdio, Promise.Rpath, Promise.Unveil)

0 comments on commit 5935629

Please sign in to comment.