| 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 |