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