]>
Commit | Line | Data |
---|---|---|
2a038236 RL |
1 | =pod |
2 | ||
3 | =encoding UTF-8 | |
4 | ||
5 | =head1 NAME | |
6 | ||
7 | proxy-certificates - Proxy certificates in OpenSSL | |
8 | ||
9 | =head1 DESCRIPTION | |
10 | ||
11 | Proxy certificates are defined in RFC 3820. They are used to | |
12 | extend rights to some other entity (a computer process, typically, or | |
13 | sometimes to the user itself). This allows the entity to perform | |
14 | operations on behalf of the owner of the EE (End Entity) certificate. | |
15 | ||
16 | The requirements for a valid proxy certificate are: | |
17 | ||
18 | =over 4 | |
19 | ||
20 | =item * | |
21 | ||
22 | They are issued by an End Entity, either a normal EE certificate, or | |
23 | another proxy certificate. | |
24 | ||
25 | =item * | |
26 | ||
27 | They must not have the B<subjectAltName> or B<issuerAltName> | |
28 | extensions. | |
29 | ||
30 | =item * | |
31 | ||
32 | They must have the B<proxyCertInfo> extension. | |
33 | ||
34 | =item * | |
35 | ||
36 | They must have the subject of their issuer, with one B<commonName> | |
37 | added. | |
38 | ||
39 | =back | |
40 | ||
41 | =head2 Enabling proxy certificate verification | |
42 | ||
43 | OpenSSL expects applications that want to use proxy certificates to be | |
44 | specially aware of them, and make that explicit. This is done by | |
45 | setting an X509 verification flag: | |
46 | ||
47 | X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_ALLOW_PROXY_CERTS); | |
48 | ||
49 | or | |
50 | ||
51 | X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_ALLOW_PROXY_CERTS); | |
52 | ||
53 | See L</NOTES> for a discussion on this requirement. | |
54 | ||
55 | =head2 Creating proxy certificates | |
56 | ||
57 | Creating proxy certificates can be done using the L<openssl-x509(1)> | |
58 | command, with some extra extensions: | |
59 | ||
60 | [ v3_proxy ] | |
61 | # A proxy certificate MUST NEVER be a CA certificate. | |
62 | basicConstraints=CA:FALSE | |
63 | ||
64 | # Usual authority key ID | |
65 | authorityKeyIdentifier=keyid,issuer:always | |
66 | ||
67 | # The extension which marks this certificate as a proxy | |
68 | proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:1,policy:text:AB | |
69 | ||
70 | It's also possible to specify the proxy extension in a separate section: | |
71 | ||
72 | proxyCertInfo=critical,@proxy_ext | |
73 | ||
74 | [ proxy_ext ] | |
75 | language=id-ppl-anyLanguage | |
76 | pathlen=0 | |
77 | policy=text:BC | |
78 | ||
79 | The policy value has a specific syntax, I<syntag>:I<string>, where the | |
80 | I<syntag> determines what will be done with the string. The following | |
81 | I<syntag>s are recognised: | |
82 | ||
83 | =over 4 | |
84 | ||
85 | =item B<text> | |
86 | ||
87 | indicates that the string is a byte sequence, without any encoding: | |
88 | ||
89 | policy=text:räksmörgås | |
90 | ||
91 | =item B<hex> | |
92 | ||
93 | indicates the string is encoded hexadecimal encoded binary data, with | |
94 | colons between each byte (every second hex digit): | |
95 | ||
96 | policy=hex:72:E4:6B:73:6D:F6:72:67:E5:73 | |
97 | ||
98 | =item B<file> | |
99 | ||
100 | indicates that the text of the policy should be taken from a file. | |
101 | The string is then a filename. This is useful for policies that are | |
102 | large (more than a few lines, e.g. XML documents). | |
103 | ||
104 | =back | |
105 | ||
106 | I<NOTE: The proxy policy value is what determines the rights granted | |
107 | to the process during the proxy certificate. It's up to the | |
108 | application to interpret and combine these policies.> | |
109 | ||
110 | With a proxy extension, creating a proxy certificate is a matter of | |
111 | two commands: | |
112 | ||
113 | openssl req -new -config proxy.cnf \ | |
114 | -out proxy.req -keyout proxy.key \ | |
115 | -subj "/DC=org/DC=openssl/DC=users/CN=proxy 1" | |
116 | ||
117 | openssl x509 -req -CAcreateserial -in proxy.req -out proxy.crt \ | |
118 | -CA user.crt -CAkey user.key -days 7 \ | |
119 | -extfile proxy.cnf -extensions v3_proxy1 | |
120 | ||
121 | You can also create a proxy certificate using another proxy | |
122 | certificate as issuer (note: using a different configuration | |
123 | section for the proxy extensions): | |
124 | ||
125 | openssl req -new -config proxy.cnf \ | |
126 | -out proxy2.req -keyout proxy2.key \ | |
127 | -subj "/DC=org/DC=openssl/DC=users/CN=proxy 1/CN=proxy 2" | |
128 | ||
129 | openssl x509 -req -CAcreateserial -in proxy2.req -out proxy2.crt \ | |
130 | -CA proxy.crt -CAkey proxy.key -days 7 \ | |
131 | -extfile proxy.cnf -extensions v3_proxy2 | |
132 | ||
133 | =head2 Using proxy certs in applications | |
134 | ||
135 | To interpret proxy policies, the application would normally start with | |
136 | some default rights (perhaps none at all), then compute the resulting | |
137 | rights by checking the rights against the chain of proxy certificates, | |
138 | user certificate and CA certificates. | |
139 | ||
140 | The complicated part is figuring out how to pass data between your | |
141 | application and the certificate validation procedure. | |
142 | ||
143 | The following ingredients are needed for such processing: | |
144 | ||
145 | =over 4 | |
146 | ||
147 | =item * | |
148 | ||
149 | a callback function that will be called for every certificate being | |
150 | validated. The callback is called several times for each certificate, | |
151 | so you must be careful to do the proxy policy interpretation at the | |
152 | right time. You also need to fill in the defaults when the EE | |
153 | certificate is checked. | |
154 | ||
155 | =item * | |
156 | ||
157 | a data structure that is shared between your application code and the | |
158 | callback. | |
159 | ||
160 | =item * | |
161 | ||
162 | a wrapper function that sets it all up. | |
163 | ||
164 | =item * | |
165 | ||
166 | an ex_data index function that creates an index into the generic | |
167 | ex_data store that is attached to an X509 validation context. | |
168 | ||
169 | =back | |
170 | ||
171 | The following skeleton code can be used as a starting point: | |
172 | ||
173 | #include <string.h> | |
174 | #include <netdb.h> | |
175 | #include <openssl/x509.h> | |
176 | #include <openssl/x509v3.h> | |
177 | ||
178 | #define total_rights 25 | |
179 | ||
180 | /* | |
181 | * In this example, I will use a view of granted rights as a bit | |
182 | * array, one bit for each possible right. | |
183 | */ | |
184 | typedef struct your_rights { | |
185 | unsigned char rights[(total_rights + 7) / 8]; | |
186 | } YOUR_RIGHTS; | |
187 | ||
188 | /* | |
189 | * The following procedure will create an index for the ex_data | |
190 | * store in the X509 validation context the first time it's | |
191 | * called. Subsequent calls will return the same index. | |
192 | */ | |
193 | static int get_proxy_auth_ex_data_idx(X509_STORE_CTX *ctx) | |
194 | { | |
195 | static volatile int idx = -1; | |
196 | ||
197 | if (idx < 0) { | |
198 | X509_STORE_lock(X509_STORE_CTX_get0_store(ctx)); | |
199 | if (idx < 0) { | |
200 | idx = X509_STORE_CTX_get_ex_new_index(0, | |
201 | "for verify callback", | |
202 | NULL,NULL,NULL); | |
203 | } | |
204 | X509_STORE_unlock(X509_STORE_CTX_get0_store(ctx)); | |
205 | } | |
206 | return idx; | |
207 | } | |
208 | ||
209 | /* Callback to be given to the X509 validation procedure. */ | |
210 | static int verify_callback(int ok, X509_STORE_CTX *ctx) | |
211 | { | |
212 | if (ok == 1) { | |
213 | /* | |
214 | * It's REALLY important you keep the proxy policy check | |
215 | * within this section. It's important to know that when | |
216 | * ok is 1, the certificates are checked from top to | |
217 | * bottom. You get the CA root first, followed by the | |
218 | * possible chain of intermediate CAs, followed by the EE | |
219 | * certificate, followed by the possible proxy | |
220 | * certificates. | |
221 | */ | |
222 | X509 *xs = X509_STORE_CTX_get_current_cert(ctx); | |
223 | ||
224 | if (X509_get_extension_flags(xs) & EXFLAG_PROXY) { | |
225 | YOUR_RIGHTS *rights = | |
226 | (YOUR_RIGHTS *)X509_STORE_CTX_get_ex_data(ctx, | |
227 | get_proxy_auth_ex_data_idx(ctx)); | |
228 | PROXY_CERT_INFO_EXTENSION *pci = | |
229 | X509_get_ext_d2i(xs, NID_proxyCertInfo, NULL, NULL); | |
230 | ||
231 | switch (OBJ_obj2nid(pci->proxyPolicy->policyLanguage)) { | |
232 | case NID_Independent: | |
233 | /* | |
234 | * Do whatever you need to grant explicit rights | |
235 | * to this particular proxy certificate, usually | |
236 | * by pulling them from some database. If there | |
237 | * are none to be found, clear all rights (making | |
238 | * this and any subsequent proxy certificate void | |
239 | * of any rights). | |
240 | */ | |
241 | memset(rights->rights, 0, sizeof(rights->rights)); | |
242 | break; | |
243 | case NID_id_ppl_inheritAll: | |
244 | /* | |
245 | * This is basically a NOP, we simply let the | |
246 | * current rights stand as they are. | |
247 | */ | |
248 | break; | |
249 | default: | |
250 | /* | |
251 | * This is usually the most complex section of | |
252 | * code. You really do whatever you want as long | |
253 | * as you follow RFC 3820. In the example we use | |
254 | * here, the simplest thing to do is to build | |
255 | * another, temporary bit array and fill it with | |
256 | * the rights granted by the current proxy | |
257 | * certificate, then use it as a mask on the | |
258 | * accumulated rights bit array, and voilà, you | |
259 | * now have a new accumulated rights bit array. | |
260 | */ | |
261 | { | |
262 | int i; | |
263 | YOUR_RIGHTS tmp_rights; | |
264 | memset(tmp_rights.rights, 0, | |
265 | sizeof(tmp_rights.rights)); | |
266 | ||
267 | /* | |
268 | * process_rights() is supposed to be a | |
269 | * procedure that takes a string and its | |
270 | * length, interprets it and sets the bits | |
271 | * in the YOUR_RIGHTS pointed at by the | |
272 | * third argument. | |
273 | */ | |
274 | process_rights((char *) pci->proxyPolicy->policy->data, | |
275 | pci->proxyPolicy->policy->length, | |
276 | &tmp_rights); | |
277 | ||
278 | for(i = 0; i < total_rights / 8; i++) | |
279 | rights->rights[i] &= tmp_rights.rights[i]; | |
280 | } | |
281 | break; | |
282 | } | |
283 | PROXY_CERT_INFO_EXTENSION_free(pci); | |
284 | } else if (!(X509_get_extension_flags(xs) & EXFLAG_CA)) { | |
285 | /* We have an EE certificate, let's use it to set default! */ | |
286 | YOUR_RIGHTS *rights = | |
287 | (YOUR_RIGHTS *)X509_STORE_CTX_get_ex_data(ctx, | |
288 | get_proxy_auth_ex_data_idx(ctx)); | |
289 | ||
290 | /* | |
291 | * The following procedure finds out what rights the | |
292 | * owner of the current certificate has, and sets them | |
293 | * in the YOUR_RIGHTS structure pointed at by the | |
294 | * second argument. | |
295 | */ | |
296 | set_default_rights(xs, rights); | |
297 | } | |
298 | } | |
299 | return ok; | |
300 | } | |
301 | ||
302 | static int my_X509_verify_cert(X509_STORE_CTX *ctx, | |
303 | YOUR_RIGHTS *needed_rights) | |
304 | { | |
305 | int ok; | |
306 | int (*save_verify_cb)(int ok,X509_STORE_CTX *ctx) = | |
307 | X509_STORE_CTX_get_verify_cb(ctx); | |
308 | YOUR_RIGHTS rights; | |
309 | ||
310 | X509_STORE_CTX_set_verify_cb(ctx, verify_callback); | |
311 | X509_STORE_CTX_set_ex_data(ctx, get_proxy_auth_ex_data_idx(ctx), | |
312 | &rights); | |
313 | X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_ALLOW_PROXY_CERTS); | |
314 | ok = X509_verify_cert(ctx); | |
315 | ||
316 | if (ok == 1) { | |
317 | ok = check_needed_rights(rights, needed_rights); | |
318 | } | |
319 | ||
320 | X509_STORE_CTX_set_verify_cb(ctx, save_verify_cb); | |
321 | ||
322 | return ok; | |
323 | } | |
324 | ||
325 | If you use SSL or TLS, you can easily set up a callback to have the | |
326 | certificates checked properly, using the code above: | |
327 | ||
328 | SSL_CTX_set_cert_verify_callback(s_ctx, my_X509_verify_cert, | |
329 | &needed_rights); | |
330 | ||
331 | =head1 NOTES | |
332 | ||
333 | To this date, it seems that proxy certificates have only been used in | |
334 | environments that are aware of them, and no one seems to have | |
335 | investigated how they can be used or misused outside of such an | |
336 | environment. | |
337 | ||
338 | For that reason, OpenSSL requires that applications aware of proxy | |
339 | certificates must also make that explicit. | |
340 | ||
341 | B<subjectAltName> and B<issuerAltName> are forbidden in proxy | |
342 | certificates, and this is enforced in OpenSSL. The subject must be | |
343 | the same as the issuer, with one commonName added on. | |
344 | ||
345 | =head1 SEE ALSO | |
346 | ||
347 | L<X509_STORE_CTX_set_flags(3)>, | |
348 | L<X509_STORE_CTX_set_verify_cb(3)>, | |
349 | L<X509_VERIFY_PARAM_set_flags(3)>, | |
350 | L<SSL_CTX_set_cert_verify_callback(3)>, | |
351 | L<openssl-req(1)>, L<openssl-x509(1)>, | |
352 | L<RFC 3820|https://tools.ietf.org/html/rfc3820> | |
353 | ||
354 | =head1 COPYRIGHT | |
355 | ||
356 | Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. | |
357 | ||
358 | Licensed under the Apache License 2.0 (the "License"). You may not use | |
359 | this file except in compliance with the License. You can obtain a copy | |
360 | in the file LICENSE in the source distribution or at | |
361 | L<https://www.openssl.org/source/license.html>. | |
362 | ||
363 | =cut |