-
Notifications
You must be signed in to change notification settings - Fork 40
/
tutorial6.xml
244 lines (238 loc) · 9.35 KB
/
tutorial6.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
<article data-sblg-article="1" data-sblg-tags="tutorial" itemscope="itemscope" itemtype="http://schema.org/BlogPosting">
<header>
<h2 itemprop="name">
Best practises for <a href="https://man.openbsd.org/pledge.2">pledge(2)</a> security
</h2>
<address itemprop="author"><a href="https://github.com/kristapsdz">Kristaps Dzonsons</a></address>
<time itemprop="datePublished" datetime="2018-04-06">6 April, 2018</time>
</header>
<p>
<aside itemprop="about">
Let's set the record straight for securing <span class="nm">kcgi</span> CGI and FastCGI applications with <a
href="https://man.openbsd.org/pledge.2">pledge(2)</a>.
This is focussed on secure <a href="https://www.openbsd.org">OpenBSD</a> deployments.
</aside>
</p>
<h3>
Theory
</h3>
<p>
Internally, <span class="nm">kcgi</span> makes considerable use of available security tools.
But it's also designed to be invoked in a secure environment.
We'll start with <a href="https://man.openbsd.org/pledge.2">pledge(2)</a>, which has been around on <a
href="https://www.openbsd.org">OpenBSD</a> since version 5.9.
If you're reading this tutorial, you're probably on <a href="https://www.openbsd.org">OpenBSD</a>, and you probably have
knowledge of <a href="https://man.openbsd.org/pledge.2">pledge(2)</a>.
</p>
<p>
How to begin?
Read <a href="kcgi.3.html">kcgi(3)</a>.
It includes canonical information on which <a href="https://man.openbsd.org/pledge.2">pledge(2)</a> promises you'll need for
each function in the library.
This is just a tutorial—the manpage is canonical and overrides what you may read here.
</p>
<p>
Next, assess the promises that your application needs.
From <a href="kcgi.3.html">kcgi(3)</a>, it's easy to see which promises we'll need to start.
You'll need to augment this list with whichever tools you're also using.
The general push is to start with the broadest set of required promises, then restrict as quickly as possible.
Sometimes this can be done in a single <a href="https://man.openbsd.org/pledge.2">pledge(2)</a>, but other times it takes a
few.
</p>
<figure>
<img class="tall" src="tutorial6.svg" alt="" />
</figure>
<h3>
Source Code: CGI
</h3>
<p>
Let's start with a trivial CGI application.
This parses the HTTP context with <a href="khttp_parse.3.html">khttp_parse(3)</a>, then emits output using the writing
functions.
Most applications will perform some sort of work based upon the context, such as with form input, but it's irrelevant for the
purposes of explanation.
</p>
<p>
I avoid error checking for brevity, but <strong>each function needs to be checked for return values</strong>.
This goes for all examples, all the time!
</p>
<figure class="sample">
<pre class="prettyprint linenums">#include <sys/types.h> /* size_t, ssize_t */
#include <stdarg.h> /* va_list */
#include <stddef.h> /* NULL */
#include <stdint.h> /* int64_t */
#include <kcgi.h>
int
main(void) {
struct kreq r;
const char *const pages[1] = { "index" };
if (khttp_parse(&r, NULL, 0, pages, 1, 0) != KCGI_OK)
return 0;
khttp_head(&r, kresps[KRESP_STATUS],
"%s", khttps[KHTTP_200]);
khttp_head(&r, kresps[KRESP_CONTENT_TYPE],
"%s", kmimetypes[KMIME_TEXT_PLAIN]);
khttp_body(&r);
khttp_puts(&r, "Hello, world!\n");
khttp_free(&r);
return 0;
}</pre>
</figure>
<p>
Obviously, this does very little.
And that's fine, because <a href="https://man.openbsd.org/pledge.2">pledge(2)</a> doesn't really care about how complex your
application is—just which system resources are used.
Our main focus is going to be <a href="khttp_parse.3.html">khttp_parse(3)</a>, which requires the system resources to create
its parsing contexts and safely get our data extracted and processed.
After that, all we're doing is constructing a response.
</p>
<p>
To use this security tool, all we need is to include <code><unistd.h></code> and to understand the <a
href="https://man.openbsd.org/pledge.2">pledge(2)</a> system call.
We'll start with the most significant protection: by constraining our application after the context has been parsed.
</p>
<figure class="sample">
<pre class="prettyprint linenums">if (khttp_parse(&r, NULL, 0, pages, 1, 0) != KCGI_OK)
return 0;
if (pledge("stdio", NULL) == -1)
return 0;</pre>
</figure>
<p>
This <a href="https://man.openbsd.org/pledge.2">pledge(2)</a> promise restricts the process to simply using available I/O
channels.
The only <span class="nm">kcgi</span> functions requiring more than <code>"stdio"</code> are the file-based template functions
described in <a href="khttp_template.3.html">khttp_template(3)</a> and <a href="khttp_template.3.html">khttp_templatex(3)</a>,
which also require <code>"rpath"</code>.
</p>
<p>
We can further secure our systems by pushing <a href="https://man.openbsd.org/pledge.2">pledge(2)</a> <em>before</em> the call
to <a href="khttp_parse.3.html">khttp_parse(3)</a>.
The only promises <a href="khttp_parse.3.html">khttp_parse(3)</a> requires are to <a
href="https://man.openbsd.org/fork.2">fork(2)</a> its internal handlers.
</p>
<figure class="sample">
<pre class="prettyprint linenums">if (pledge("stdio proc", NULL) == -1)
return 0;
if (khttp_parse(&r, NULL, 0, pages, 1, 0) != KCGI_OK)
return 0;
if (pledge("stdio", NULL) == -1)
return 0;</pre>
</figure>
<p>
Or if one is using <a href="khttp_template.3.html">khttp_template(3)</a> to marshall a response:
</p>
<figure class="sample">
<pre class="prettyprint linenums">if (pledge("stdio rpath proc", NULL) == -1)
return 0;
if (khttp_parse(&r, NULL, 0, pages, 1, 0) != KCGI_OK)
return 0;
if (pledge("stdio rpath", NULL) == -1)
return 0;</pre>
</figure>
<p>
That's it!
Obviously, you'll need to expand the set of promises proportionate to your application's needs.
Let's put it all together:
</p>
<figure class="sample">
<pre class="prettyprint linenums">#include <sys/types.h> /* size_t, ssize_t */
#include <stdarg.h> /* va_list */
#include <stddef.h> /* NULL */
#include <stdint.h> /* int64_t */
#include <unistd.h> /* pledge */
#include <kcgi.h>
int
main(void) {
struct kreq r;
const char *const pages[1] = { "index" };
if (pledge("stdio proc", NULL) == -1)
return 0;
if (khttp_parse(&r, NULL, 0, pages, 1, 0) != KCGI_OK)
return 0;
if (pledge("stdio", NULL) == -1)
return 0;
khttp_head(&r, kresps[KRESP_STATUS],
"%s", khttps[KHTTP_200]);
khttp_head(&r, kresps[KRESP_CONTENT_TYPE],
"%s", kmimetypes[KMIME_TEXT_PLAIN]);
khttp_body(&r);
khttp_puts(&r, "Hello, world!\n");
khttp_free(&r);
return 0;
}</pre>
</figure>
<p>
One last note: portability.
If you're going to be writing CGI scripts that must be portable across architectures, consider using
<a href="https://github.com/kristapsdz/oconfigure">oconfigure</a> for a selection of portable OpenBSD functions and feature tests.
</p>
<h3>
Source Code: FastCGI
</h3>
<p>
This follows the logic of the CGI example, but we need to have extra promises to account for the increased complexity of FastCGI processing.
First, start with our simple example without any header inclusions—all of which are the same as in the CGI example.
</p>
<p>
Again, <strong>each function needs to be checked for return values</strong>.
This would otherwise clutter up the examples, but production systems <strong>must</strong> error check.
</p>
<figure class="sample">
<pre class="prettyprint linenums">int
main(void) {
struct kreq r;
const char *const pages[1] = { "index" };
struct kfcgi *fcgi;
enum kcgi_err er;
if (khttp_fcgi_init(&fcgi, NULL, 0, pages, 1, 0) != KCGI_OK)
return 0;
for (;;) {
if (khttp_fcgi_parse(fcgi, &req) != KCGI_OK)
break;
khttp_head(&r, kresps[KRESP_STATUS],
"%s", khttps[KHTTP_200]);
khttp_head(&r, kresps[KRESP_CONTENT_TYPE],
"%s", kmimetypes[KMIME_TEXT_PLAIN]);
khttp_body(&r);
khttp_puts(&r, "Hello, world!\n");
khttp_free(&r);
}
return 0;
}</pre>
</figure>
<p>
The FastCGI-specific functions we need to manage are
<a href="khttp_fcgi_init.3.html">khttp_fcgi_init(3)</a> and
<a href="khttp_fcgi_parse.3.html">khttp_fcgi_parse(3)</a>.
The promises required are all noted in <a href="kcgi.3.html">kcgi(3)</a>.
The rest follow from the CGI example.
</p>
<figure class="sample">
<pre class="prettyprint linenums">int
main(void) {
struct kreq r;
const char *const pages[1] = { "index" };
struct kfcgi *fcgi;
enum kcgi_err er;
if (pledge("unix sendfd recvfd proc stdio", NULL) == -1)
return 0;
if (khttp_fcgi_init(&fcgi, NULL, 0, pages, 1, 0) != KCGI_OK)
return 0;
if (pledge("stdio recvfd", NULL) == -1)
return 0;
for (;;) {
if (khttp_fcgi_parse(fcgi, &req) != KCGI_OK)
break;
khttp_head(&r, kresps[KRESP_STATUS],
"%s", khttps[KHTTP_200]);
khttp_head(&r, kresps[KRESP_CONTENT_TYPE],
"%s", kmimetypes[KMIME_TEXT_PLAIN]);
khttp_body(&r);
khttp_puts(&r, "Hello, world!\n");
khttp_free(&r);
}
khttp_fcgi_free(fcgi);
return 0;
}</pre>
</figure>
</article>