Skip to content

Commit 5935629

Browse files
authored
Fix #1 - add unveil(2) support (#2)
* 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
1 parent 5a516b4 commit 5935629

File tree

7 files changed

+104
-15
lines changed

7 files changed

+104
-15
lines changed

.builds/openbsd.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ tasks:
2121
env PATH="$HOME/nim-${NIM_VERSION}/bin:$PATH" nim c --cc:clang -r tests/main.nim
2222
triggers:
2323
- action: email
24-
condition: failure
24+
condition: always
2525
to: Euan T <[email protected]>

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# pledge.nim [![builds.sr.ht status](https://builds.sr.ht/~euantorano.svg?search=pledge.nim)](https://builds.sr.ht/~euantorano?search=pledge.nim)
22

3-
A wrapper around OpenBSD's `pledge(2)` systemcall for Nim.
3+
A wrapper around OpenBSD's `pledge(2)` system call for Nim.
4+
5+
Includes support for OpenBSD's `unveil(2)` system call.
46

57
## Installation
68

@@ -15,7 +17,7 @@ Or add the following to your `.nimble` file:
1517
```
1618
# Dependencies
1719
18-
requires "pledge >= 1.1.0"
20+
requires "pledge >= 2.0.0"
1921
```
2022

2123
## [Documentation](https://htmlpreview.github.io/?https://github.com/euantorano/pledge.nim/blob/master/docs/pledge.html)
@@ -27,6 +29,6 @@ import pledge
2729
2830
pledge(Promises.Stdio)
2931
30-
# As we haven't used pledge to ask to access files, the below will cause the program to be temrinated with a SIGABRT.
32+
# As we haven't used pledge to ask to access files, the below will cause the program to be terminated with a SIGABRT.
3133
let f = open("/etc/rc.conf")
3234
```

docs/pledge.html

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,9 @@ <h1 class="title">pledge</h1>
836836
Recvfd = &quot;recvfd&quot;, Tape = &quot;tape&quot;, Tty = &quot;tty&quot;, Proc = &quot;proc&quot;, Exec = &quot;exec&quot;,
837837
ProtExec = &quot;prot_exec&quot;, Settime = &quot;settime&quot;, Ps = &quot;ps&quot;, Vminfo = &quot;vminfo&quot;, Id = &quot;id&quot;,
838838
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>
839+
<li><a class="reference" href="#Permission"
840+
title="Permission {.pure.} = enum
841+
CreateRemove = &apos;c&apos;, Read = &apos;r&apos;, Write = &apos;w&apos;, Execute = &apos;x&apos;"><wbr />Permission<span class="attachedType"></span></a></li>
839842

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

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

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

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

898+
</dd>
899+
<a id="Permission"></a>
900+
<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>
901+
<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>
902+
<dd>
903+
904+
The possible permissions that a call to <tt class="docutils literal"><span class="pre">unveil</span></tt> can be used with.
905+
891906
</dd>
892907

893908
</dl></div>
@@ -903,6 +918,13 @@ <h1><a class="toc-backref" href="#12">Procs</a></h1>
903918
<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>
904919

905920

921+
</dd>
922+
<a id="unveil,Option[string],Option[string]"></a>
923+
<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>
924+
<dd>
925+
926+
Unveil parts of a restricted filesystem view.
927+
906928
</dd>
907929

908930
</dl></div>
@@ -917,6 +939,13 @@ <h1><a class="toc-backref" href="#18">Templates</a></h1>
917939
<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>
918940

919941

942+
</dd>
943+
<a id="unveil.t,string,set[Permission]"></a>
944+
<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>
945+
<dd>
946+
947+
Unveil parts of a restricted filesystem view.
948+
920949
</dd>
921950

922951
</dl></div>
@@ -928,7 +957,7 @@ <h1><a class="toc-backref" href="#18">Templates</a></h1>
928957
<div class="twelve-columns footer">
929958
<span class="nim-sprite"></span>
930959
<br/>
931-
<small>Made with Nim. Generated: 2019-08-20 12:07:44 UTC</small>
960+
<small>Made with Nim. Generated: 2019-09-30 19:55:46 UTC</small>
932961
</div>
933962
</div>
934963
</div>

docs/pledge.idx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
Example of making a single promise pledge.html#example-of-making-a-single-promise Example of making a single promise
22
Example of making several promises pledge.html#example-of-making-several-promises Example of making several promises
33
Promise pledge.html#Promise pledge: Promise
4+
Permission pledge.html#Permission pledge: Permission
45
pledge pledge.html#pledge,Option[string],Option[string] pledge: pledge(promises: Option[string]; execPromises: Option[string] = none(string))
6+
unveil pledge.html#unveil,Option[string],Option[string] pledge: unveil(path: Option[string]; permissions: Option[string])
57
pledge pledge.html#pledge.t,varargs[Promise] pledge: pledge(promises: varargs[Promise])
8+
unveil pledge.html#unveil.t,string,set[Permission] pledge: unveil(path: string; permissions: set[Permission])

pledge.nimble

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Package
22

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

1010
# Dependencies
1111

12-
requires "nim >= 0.13.0"
12+
requires "nim >= 1.0.0"
1313

1414
task docs, "Build documentation":
1515
exec "nim doc2 --index:on -o:docs/pledge.html src/pledge.nim"

src/pledge.nim

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
##
2323
## pledge(Promise.Stdio, Promise.Rpath)
2424

25-
import os, options
25+
import options
2626

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

61+
type Permission* {.pure.} = enum
62+
## The possible permissions that a call to `unveil` can be used with.
63+
CreateRemove = 'c'
64+
Read = 'r'
65+
Write = 'w'
66+
Execute = 'x'
67+
6168
when defined(nimdoc) or not defined(openbsd):
6269
proc pledge*(promises: Option[string], execPromises: Option[string] = none(string)) = discard
6370
## Pledge to use only the defined functions.
6471
##
6572
## If no promises are provided, the process will be restricted to the `_exit(2)` system call.
73+
74+
proc unveil*(path: Option[string], permissions: Option[string]) = discard
75+
## Unveil parts of a restricted filesystem view.
6676
elif defined(openbsd):
67-
import posix_utils, strformat
77+
import os, posix_utils, strformat
6878

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

7182
type
7283
OpenBsdVersion = object
@@ -75,6 +86,8 @@ elif defined(openbsd):
7586
PledgeException* = object of Exception
7687
PledgeNotAvailableError* = object of PledgeException
7788
PledgeExecPromisesNotAvailableError* = object of PledgeException
89+
UnveilException* = object of Exception
90+
UnveilNotAvailableException* = object of UnveilException
7891

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

104-
var promisesValue: cstring = if promises.isSome(): cstring(promises.get()) else: nil
117+
let promisesValue: cstring = if promises.isSome(): cstring(promises.get()) else: nil
105118
var execPromisesValue: cstring = nil
106119

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

114-
proc getPromisesString(promises: openarray[Promise]): string {.compiletime.} =
115-
result = ""
127+
proc unveil*(path: Option[string], permissions: Option[string]) =
128+
## Unveil parts of a restricted filesystem view.
129+
if (osVersion.major == 6 and osVersion.minor < 4) or osVersion.major < 6:
130+
raise newException(UnveilNotAvailableException, &"unveil(2) system call is not available on OpenBSD {osVersion.major}.{osVersion.minor}")
131+
132+
let pathValue: cstring = if path.isSome(): cstring(path.get()) else: nil
133+
let permissionsValue: cstring = if permissions.isSome(): cstring(permissions.get()) else: nil
134+
135+
if unveil_c(pathValue, permissionsValue) != 0:
136+
raiseOSError(osLastError())
116137

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

127149
sep = " "
128150

151+
proc getPermissionsString(permissions: set[Permission]): Option[string] {.compiletime.} =
152+
var s = ""
153+
154+
for p in permissions:
155+
s.add(char(p))
156+
157+
if len(s) > 0:
158+
result = some(s)
159+
else:
160+
result = none(string)
161+
129162
template pledge*(promises: varargs[Promise]) =
130163
## Pledge to use only the defined functions.
131164
##
@@ -135,3 +168,8 @@ template pledge*(promises: varargs[Promise]) =
135168
pledge(some(promisesString))
136169
else:
137170
pledge(none(string))
171+
172+
template unveil*(path: string, permissions: set[Permission]) =
173+
## Unveil parts of a restricted filesystem view.
174+
let pathValue = if len(path) > 0: some(path) else: none(string)
175+
unveil(pathValue, getPermissionsString(permissions))

tests/main.nim

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,31 @@
11
import pledge, unittest, os
22

3+
suite "unveil tests":
4+
test "can access path that is unveiled":
5+
unveil("/dev/urandom", {Permission.Read})
6+
7+
var f: File
8+
check open(f, "/dev/urandom", fmRead)
9+
var buff: array[0..9, byte]
10+
check f.readBytes(buff, 0, len(buff)) > 0
11+
12+
when defined(openbsd):
13+
test "can't access path that isn't unveiled":
14+
unveil("/dev/urandom", {Permission.Read})
15+
unveil("", {})
16+
17+
var f: File
18+
check not open(f, "/dev/zero", fmRead)
19+
320
suite "pledge tests":
421
test "can pledge":
5-
pledge(Promise.Stdio, Promise.Rpath)
22+
pledge(Promise.Stdio, Promise.Rpath, Promise.Unveil)
623

724
check true
825

926
when defined(openbsd):
1027
test "can not elevate":
11-
pledge(Promise.Stdio)
28+
pledge(Promise.Stdio, Promise.Unveil)
1229

1330
expect OSError:
14-
pledge(Promise.Stdio, Promise.Rpath)
31+
pledge(Promise.Stdio, Promise.Rpath, Promise.Unveil)

0 commit comments

Comments
 (0)