]> git.ipfire.org Git - thirdparty/openssl.git/blob - doc/HOWTO/proxy_certificates.txt
Correct environment variable is OPENSSL_ALLOW_PROXY_CERTS.
[thirdparty/openssl.git] / doc / HOWTO / proxy_certificates.txt
1 <DRAFT!>
2 HOWTO proxy certificates
3
4 0. WARNING
5
6 NONE OF THE CODE PRESENTED HERE HAVE BEEN CHECKED! They are just an
7 example to show you how things can be done. There may be typos or
8 type conflicts, and you will have to resolve them.
9
10 1. Introduction
11
12 Proxy certificates are defined in RFC 3820. They are really usual
13 certificates with the mandatory extension proxyCertInfo.
14
15 Proxy certificates are issued by an End Entity (typically a user),
16 either directly with the EE certificate as issuing certificate, or by
17 extension through an already issued proxy certificate.. They are used
18 to extend rights to some other entity (a computer process, typically,
19 or sometimes to the user itself), so it can perform operations in the
20 name of the owner of the EE certificate.
21
22 See http://www.ietf.org/rfc/rfc3820.txt for more information.
23
24
25 2. A warning about proxy certificates
26
27 Noone seems to have tested proxy certificates with security in mind.
28 Basically, to this date, it seems that proxy certificates have only
29 been used in a world that's highly aware of them. What would happen
30 if an unsuspecting application is to validate a chain of certificates
31 that contains proxy certificates? It would usually consider the leaf
32 to be the certificate to check for authorisation data, and since proxy
33 certificates are controlled by the EE certificate owner alone, it's
34 would be normal to consider what the EE certificate owner could do
35 with them.
36
37 subjectAltName and issuerAltName are forbidden in proxy certificates,
38 and this is enforced in OpenSSL. The subject must be the same as the
39 issuer, with one commonName added on.
40
41 Possible threats are, as far as has been imagined so far:
42
43 - impersonation through commonName (think server certificates).
44 - use of additional extensions, possibly non-standard ones used in
45 certain environments, that would grant extra or different
46 authorisation rights.
47
48 For this reason, OpenSSL requires that the use of proxy certificates
49 be explicitely allowed. Currently, this can be done using the
50 following methods:
51
52 - if the application calls X509_verify_cert() itself, it can do the
53 following prior to that call (ctx is the pointer passed in the call
54 to X509_verify_cert()):
55
56 X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_ALLOW_PROXY_CERTS);
57
58 - in all other cases, proxy certificate validation can be enabled
59 before starting the application by setting the envirnoment variable
60 OPENSSL_ALLOW_PROXY_CERTS with some non-empty value.
61
62 There are thoughts to allow proxy certificates with a line in the
63 default openssl.cnf, but that's still in the future.
64
65
66 3. How to create proxy cerificates
67
68 It's quite easy to create proxy certificates, by taking advantage of
69 the lack of checks of the 'openssl x509' application (*ahem*). But
70 first, you need to create a configuration section that contains a
71 definition of the proxyCertInfo extension, a little like this:
72
73 [ v3_proxy ]
74 # A proxy certificate MUST NEVER be a CA certificate.
75 basicConstraints=CA:FALSE
76
77 # Usual authority key ID
78 authorityKeyIdentifier=keyid,issuer:always
79
80 # Now, for the extension that marks this certificate as a proxy one
81 proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:1,policy:text:AB
82
83 It's also possible to give the proxy extension in a separate section:
84
85 proxyCertInfo=critical,@proxy_ext
86
87 [ proxy_ext ]
88 language=id-ppl-anyLanguage
89 pathlen=0
90 policy=text:BC
91
92 The policy value has a specific syntax, {syntag}:{string}, where the
93 syntag determines what will be done with the string. The recognised
94 syntags are as follows:
95
96 text indicates that the string is simply the bytes, not
97 encoded in any kind of way:
98
99 policy=text:räksmörgås
100
101 Previous versions of this design had a specific tag
102 for UTF-8 text. However, since the bytes are copied
103 as-is anyway, there's no need for it. Instead, use
104 the text: tag, like this:
105
106 policy=text:räksmörgås
107
108 hex indicates the string is encoded in hex, with colons
109 between each byte (every second hex digit):
110
111 policy=hex:72:E4:6B:73:6D:F6:72:67:E5:73
112
113 Previous versions of this design had a tag to insert a
114 complete DER blob. However, the only legal use for
115 this would be to surround the bytes that would go with
116 the hex: tag with what's needed to construct a correct
117 OCTET STRING. Since hex: does that, the DER tag felt
118 superfluous, and was therefore removed.
119
120 file indicates that the text of the policy should really be
121 taken from a file. The string is then really a file
122 name. This is useful for policies that are large
123 (more than a few of lines) XML documents, for example.
124
125 The 'policy' setting can be split up in multiple lines like this:
126
127 0.policy=This is
128 1.polisy= a multi-
129 2.policy=line policy.
130
131 NOTE: the proxy policy value is the part that determines the rights
132 granted to the process using the proxy certificate. The value is
133 completely dependent on the application reading and interpretting it!
134
135 Now that you have created an extension section for your proxy
136 certificate, you can now easily create a proxy certificate like this:
137
138 openssl req -new -config openssl.cnf \
139 -out proxy.req -keyout proxy.key
140 openssl x509 -req -CAcreateserial -in proxy.req -days 7 \
141 -out proxy.crt -CA user.crt -CAkey user.key \
142 -extfile openssl.cnf -extensions v3_proxy
143
144 It's just as easy to create a proxy certificate using another proxy
145 certificate as issuer (note that I'm using a different configuration
146 section for it):
147
148 openssl req -new -config openssl.cnf \
149 -out proxy2.req -keyout proxy2.key
150 openssl x509 -req -CAcreateserial -in proxy2.req -days 7 \
151 -out proxy2.crt -CA proxy.crt -CAkey proxy.key \
152 -extfile openssl.cnf -extensions v3_proxy2
153
154
155 4. How to have your application interpret the policy?
156
157 The basic way to interpret proxy policies is to prepare some default
158 rights, then do a check of the proxy certificate against the a chain
159 of proxy certificates, user certificate and CA certificates, and see
160 what rights came out by the end. Sounds easy, huh? It almost is.
161
162 The slightly complicated part is how to pass data between your
163 application and the certificate validation procedure.
164
165 You need the following ingredients:
166
167 - a callback routing that will be called for every certificate that's
168 validated. It will be called several times for each certificates,
169 so you must be attentive to when it's a good time to do the proxy
170 policy interpretation and check, as well as to fill in the defaults
171 when the EE certificate is checked.
172
173 - a structure of data that's shared between your application code and
174 the callback.
175
176 - a wrapper function that sets it all up.
177
178 - an ex_data index function that creates an index into the generic
179 ex_data store that's attached to an X509 validation context.
180
181 This is some cookbook code for you to fill in:
182
183 /* In this example, I will use a view of granted rights as a bit
184 array, one bit for each possible right. */
185 typedef struct your_rights {
186 unsigned char rights[total_rights / 8];
187 } YOUR_RIGHTS;
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 called.
191 Subsequent calls will return the same index. */
192 static int get_proxy_auth_ex_data_idx(void)
193 {
194 static volatile int idx = -1;
195 if (idx < 0)
196 {
197 CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
198 if (idx < 0)
199 {
200 idx = X509_STORE_CTX_get_ex_new_index(0,
201 "for verify callback",
202 NULL,NULL,NULL);
203 }
204 CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
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) /* It's REALLY important you keep the proxy policy
213 check within this secion. It's important to know
214 that when ok is 1, the certificates are checked
215 from top to bottom. You get the CA root first,
216 followed by the possible chain of intermediate
217 CAs, followed by the EE certificate, followed by
218 the possible proxy certificates. */
219 {
220 X509 *xs = ctx->current_cert;
221
222 if (xs->ex_flags & EXFLAG_PROXY)
223 {
224 YOUR_RIGHTS *rights =
225 (YOUR_RIGHTS *)X509_STORE_CTX_get_ex_data(ctx,
226 get_proxy_auth_ex_data_idx());
227 PROXY_CERT_INFO_EXTENSION *pci =
228 X509_get_ext_d2i(xs, NID_proxyCertInfo, NULL, NULL);
229
230 switch (OBJ_obj2nid(pci->proxyPolicy->policyLanguage))
231 {
232 case NID_Independent:
233 /* Do whatever you need to grant explicit rights to
234 this particular proxy certificate, usually by
235 pulling them from some database. If there are none
236 to be found, clear all rights (making this and any
237 subsequent proxy certificate void of any rights).
238 */
239 memset(rights->rights, 0, sizeof(rights->rights));
240 break;
241 case NID_id_ppl_inheritAll:
242 /* This is basically a NOP, we simply let the current
243 rights stand as they are. */
244 break;
245 default:
246 /* This is usually the most complex section of code.
247 You really do whatever you want as long as you
248 follow RFC 3820. In the example we use here, the
249 simplest thing to do is to build another, temporary
250 bit array and fill it with the rights granted by
251 the current proxy certificate, then use it as a
252 mask on the accumulated rights bit array, and
253 voilà, you now have a new accumulated rights bit
254 array. */
255 {
256 int i;
257 YOUR_RIGHTS tmp_rights;
258 memset(tmp_rights.rights, 0, sizeof(tmp_rights.rights));
259
260 /* process_rights() is supposed to be a procedure
261 that takes a string and it's length, interprets
262 it and sets the bits in the YOUR_RIGHTS pointed
263 at by the third argument. */
264 process_rights((char *) pci->proxyPolicy->policy->data,
265 pci->proxyPolicy->policy->length,
266 &tmp_rights);
267
268 for(i = 0; i < total_rights / 8; i++)
269 rights->rights[i] &= tmp_rights.rights[i];
270 }
271 break;
272 }
273 PROXY_CERT_INFO_EXTENSION_free(pci);
274 }
275 else if (!(xs->ex_flags & EXFLAG_CA))
276 {
277 /* We have a EE certificate, let's use it to set default!
278 */
279 YOUR_RIGHTS *rights =
280 (YOUR_RIGHTS *)X509_STORE_CTX_get_ex_data(ctx,
281 get_proxy_auth_ex_data_idx());
282
283 /* The following procedure finds out what rights the owner
284 of the current certificate has, and sets them in the
285 YOUR_RIGHTS structure pointed at by the second
286 argument. */
287 set_default_rights(xs, rights);
288 }
289 }
290 return ok;
291 }
292
293 static int my_X509_verify_cert(X509_STORE_CTX *ctx,
294 YOUR_RIGHTS *needed_rights)
295 {
296 int i;
297 int (*save_verify_cb)(int ok,X509_STORE_CTX *ctx) = ctx->verify_cb;
298 YOUR_RIGHTS rights;
299
300 X509_STORE_CTX_set_verify_cb(ctx, verify_callback);
301 X509_STORE_CTX_set_ex_data(ctx, get_proxy_auth_ex_data_idx(), &rights);
302 X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_ALLOW_PROXY_CERTS);
303 ok = X509_verify_cert(ctx);
304
305 if (ok == 1)
306 {
307 ok = check_needed_rights(rights, needed_rights);
308 }
309
310 X509_STORE_CTX_set_verify_cb(ctx, save_verify_cb);
311
312 return ok;
313 }
314
315 If you use SSL or TLS, you can easily set up a callback to have the
316 certificates checked properly, using the code above:
317
318 SSL_CTX_set_cert_verify_callback(s_ctx, my_X509_verify_cert, &needed_rights);
319
320
321 --
322 Richard Levitte