lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame] | 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 |