proxy_certificates.txt revision 237657
1<DRAFT!>
2			HOWTO proxy certificates
3
40. WARNING
5
6NONE OF THE CODE PRESENTED HERE HAVE BEEN CHECKED!  They are just an
7example to show you how things can be done.  There may be typos or
8type conflicts, and you will have to resolve them.
9
101. Introduction
11
12Proxy certificates are defined in RFC 3820.  They are really usual
13certificates with the mandatory extension proxyCertInfo.
14
15Proxy certificates are issued by an End Entity (typically a user),
16either directly with the EE certificate as issuing certificate, or by
17extension through an already issued proxy certificate..  They are used
18to extend rights to some other entity (a computer process, typically,
19or sometimes to the user itself), so it can perform operations in the
20name of the owner of the EE certificate.
21
22See http://www.ietf.org/rfc/rfc3820.txt for more information.
23
24
252. A warning about proxy certificates
26
27Noone seems to have tested proxy certificates with security in mind.
28Basically, to this date, it seems that proxy certificates have only
29been used in a world that's highly aware of them.  What would happen
30if an unsuspecting application is to validate a chain of certificates
31that contains proxy certificates?  It would usually consider the leaf
32to be the certificate to check for authorisation data, and since proxy
33certificates are controlled by the EE certificate owner alone, it's
34would be normal to consider what the EE certificate owner could do
35with them.
36
37subjectAltName and issuerAltName are forbidden in proxy certificates,
38and this is enforced in OpenSSL.  The subject must be the same as the
39issuer, with one commonName added on.
40
41Possible 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
48For this reason, OpenSSL requires that the use of proxy certificates
49be explicitely allowed.  Currently, this can be done using the
50following 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
62There are thoughts to allow proxy certificates with a line in the
63default openssl.cnf, but that's still in the future.
64
65
663. How to create proxy cerificates
67
68It's quite easy to create proxy certificates, by taking advantage of
69the lack of checks of the 'openssl x509' application (*ahem*).  But
70first, you need to create a configuration section that contains a
71definition 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
83It'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
92The policy value has a specific syntax, {syntag}:{string}, where the
93syntag determines what will be done with the string.  The recognised
94syntags 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
125The '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
131NOTE: the proxy policy value is the part that determines the rights
132granted to the process using the proxy certificate.  The value is
133completely dependent on the application reading and interpretting it!
134
135Now that you have created an extension section for your proxy
136certificate, 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
144It's just as easy to create a proxy certificate using another proxy
145certificate as issuer (note that I'm using a different configuration
146section 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
1554. How to have your application interpret the policy?
156
157The basic way to interpret proxy policies is to prepare some default
158rights, then do a check of the proxy certificate against the a chain
159of proxy certificates, user certificate and CA certificates, and see
160what rights came out by the end.  Sounds easy, huh?  It almost is.
161
162The slightly complicated part is how to pass data between your
163application and the certificate validation procedure.
164
165You 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
181This 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
315If you use SSL or TLS, you can easily set up a callback to have the
316certificates 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-- 
322Richard Levitte
323