Fossil SCM
Add support for feeding OpenSSL a CA certificate file/path for proper chain verification. This is one of several possible solutions to ticket [727af73f467a64be0d0bbbcf46c513062a9e4704]. Also cache the CA certificate file/path, client certificate/key file/path references in the global config (similar to how the server certificates are cached), and attempt to use them if the corresponding environment variables have not been set. Prefixed a function with ssl_ to conform to existing naming conventions.
Commit
b28995ccbdbb54b047b06eae6d7eb81ae80e329b
Parent
513ea81005b9292…
1 file changed
+73
-15
+73
-15
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -133,11 +133,11 @@ | ||
| 133 | 133 | int hasSavedCertificate = 0; |
| 134 | 134 | char *connStr ; |
| 135 | 135 | ssl_global_init(); |
| 136 | 136 | |
| 137 | 137 | /* If client certificate/key has been set, load them into the SSL context. */ |
| 138 | - load_client_authfiles(); | |
| 138 | + ssl_load_client_authfiles(); | |
| 139 | 139 | |
| 140 | 140 | /* Get certificate for current server from global config and |
| 141 | 141 | * (if we have it in config) add it to certificate store. |
| 142 | 142 | */ |
| 143 | 143 | cert = ssl_get_certificate(); |
| @@ -294,34 +294,92 @@ | ||
| 294 | 294 | /* |
| 295 | 295 | ** Read client certificate and key, if set, and store them in the SSL context |
| 296 | 296 | ** to allow communication with servers which are configured to verify client |
| 297 | 297 | ** certificates and certificate chains. |
| 298 | 298 | ** We only support PEM and don't support password protected keys. |
| 299 | -*/ | |
| 300 | -void load_client_authfiles(void) | |
| 301 | -{ | |
| 302 | - const char *certfile; | |
| 303 | - const char *keyfile; | |
| 304 | - | |
| 305 | - certfile = getenv("FOSSIL_CCERT"); | |
| 306 | - if( certfile == NULL ) | |
| 307 | - return; | |
| 308 | - | |
| 309 | - keyfile = getenv("FOSSIL_CKEY"); | |
| 299 | +** | |
| 300 | +** Always try the environment variables first, and if they aren't set, then | |
| 301 | +** use the global config. | |
| 302 | +*/ | |
| 303 | +void ssl_load_client_authfiles(void) | |
| 304 | +{ | |
| 305 | + char *cafile; | |
| 306 | + char *capath; | |
| 307 | + char *certfile; | |
| 308 | + char *keyfile; | |
| 309 | + | |
| 310 | + cafile = ssl_get_and_set_file_ref("FOSSIL_CAFILE", "cafile"); | |
| 311 | + capath = ssl_get_and_set_file_ref("FOSSIL_CAPATH", "capath"); | |
| 312 | + | |
| 313 | + if( cafile || capath ){ | |
| 314 | + /* The OpenSSL documentation warns that if several CA certificates match | |
| 315 | + * the same name, key identifier and serial number conditions, only the | |
| 316 | + * first will be examined. The caveat situation is when one stores an | |
| 317 | + * expired CA certificate among the valid ones. | |
| 318 | + * Simply put: Do not mix expired and valid certificates. | |
| 319 | + */ | |
| 320 | + if( SSL_CTX_load_verify_locations(sslCtx, cafile, capath) == 0){ | |
| 321 | + fossil_fatal("SSL: Unable to load CA verification file/path"); | |
| 322 | + } | |
| 323 | + }else{ | |
| 324 | + fossil_warning("SSL: CA file/path missing for certificate verification."); | |
| 325 | + } | |
| 326 | + | |
| 327 | + certfile = ssl_get_and_set_file_ref("FOSSIL_CCERT", "ccert"); | |
| 328 | + if( !certfile ){ | |
| 329 | + free(capath); | |
| 330 | + free(cafile); | |
| 331 | + return; | |
| 332 | + } | |
| 333 | + | |
| 334 | + keyfile = ssl_get_and_set_file_ref("FOSSIL_CKEY", "ckey"); | |
| 310 | 335 | |
| 311 | 336 | /* Assume the key is in the certificate file if key file was not specified */ |
| 312 | 337 | if( certfile && !keyfile ) |
| 313 | 338 | keyfile = certfile; |
| 314 | 339 | |
| 315 | 340 | if( SSL_CTX_use_certificate_file(sslCtx, certfile, SSL_FILETYPE_PEM) <= 0 ){ |
| 316 | - fossil_fatal("Unable to open client certificate in %s.", certfile); | |
| 341 | + fossil_fatal("SSL: Unable to open client certificate in %s.", certfile); | |
| 317 | 342 | } |
| 318 | 343 | if( SSL_CTX_use_PrivateKey_file(sslCtx, keyfile, SSL_FILETYPE_PEM) <= 0 ){ |
| 319 | - fossil_fatal("Unable to open client key in %s.", keyfile); | |
| 344 | + fossil_fatal("SSL: Unable to open client key in %s.", keyfile); | |
| 320 | 345 | } |
| 321 | 346 | |
| 322 | 347 | if( !SSL_CTX_check_private_key(sslCtx) ){ |
| 323 | - fossil_fatal("Private key does not match the certificate public key."); | |
| 348 | + fossil_fatal("SSL: Private key does not match the certificate public " | |
| 349 | + "key."); | |
| 350 | + } | |
| 351 | + | |
| 352 | + free(keyfile); | |
| 353 | + free(certfile); | |
| 354 | + free(capath); | |
| 355 | + free(cafile); | |
| 356 | +} | |
| 357 | + | |
| 358 | +/* | |
| 359 | +** Get SSL authentication file reference from environment variable. If set, | |
| 360 | +** then store varaible in global config. If environment variable was not set, | |
| 361 | +** attempt to get variable from global config. | |
| 362 | +**/ | |
| 363 | +char *ssl_get_and_set_file_ref(const char *envvar, const char *dbvar) | |
| 364 | +{ | |
| 365 | + char *zVar; | |
| 366 | + char *zTmp; | |
| 367 | + | |
| 368 | + zTmp = mprintf("%s:%s", dbvar, g.urlName); | |
| 369 | + | |
| 370 | + zVar = getenv(envvar); | |
| 371 | + if( zVar ){ | |
| 372 | + zVar = strdup(zVar); | |
| 373 | + if( zVar == NULL ){ | |
| 374 | + fossil_fatal("Unable to allocate memory for %s value.", envvar); | |
| 375 | + } | |
| 376 | + db_set(zTmp, zVar, 1); | |
| 377 | + }else{ | |
| 378 | + zVar = db_get(zTmp, NULL); | |
| 324 | 379 | } |
| 380 | + free(zTmp); | |
| 381 | + | |
| 382 | + return zVar; | |
| 325 | 383 | } |
| 326 | 384 | |
| 327 | 385 | #endif /* FOSSIL_ENABLE_SSL */ |
| 328 | 386 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -133,11 +133,11 @@ | |
| 133 | int hasSavedCertificate = 0; |
| 134 | char *connStr ; |
| 135 | ssl_global_init(); |
| 136 | |
| 137 | /* If client certificate/key has been set, load them into the SSL context. */ |
| 138 | load_client_authfiles(); |
| 139 | |
| 140 | /* Get certificate for current server from global config and |
| 141 | * (if we have it in config) add it to certificate store. |
| 142 | */ |
| 143 | cert = ssl_get_certificate(); |
| @@ -294,34 +294,92 @@ | |
| 294 | /* |
| 295 | ** Read client certificate and key, if set, and store them in the SSL context |
| 296 | ** to allow communication with servers which are configured to verify client |
| 297 | ** certificates and certificate chains. |
| 298 | ** We only support PEM and don't support password protected keys. |
| 299 | */ |
| 300 | void load_client_authfiles(void) |
| 301 | { |
| 302 | const char *certfile; |
| 303 | const char *keyfile; |
| 304 | |
| 305 | certfile = getenv("FOSSIL_CCERT"); |
| 306 | if( certfile == NULL ) |
| 307 | return; |
| 308 | |
| 309 | keyfile = getenv("FOSSIL_CKEY"); |
| 310 | |
| 311 | /* Assume the key is in the certificate file if key file was not specified */ |
| 312 | if( certfile && !keyfile ) |
| 313 | keyfile = certfile; |
| 314 | |
| 315 | if( SSL_CTX_use_certificate_file(sslCtx, certfile, SSL_FILETYPE_PEM) <= 0 ){ |
| 316 | fossil_fatal("Unable to open client certificate in %s.", certfile); |
| 317 | } |
| 318 | if( SSL_CTX_use_PrivateKey_file(sslCtx, keyfile, SSL_FILETYPE_PEM) <= 0 ){ |
| 319 | fossil_fatal("Unable to open client key in %s.", keyfile); |
| 320 | } |
| 321 | |
| 322 | if( !SSL_CTX_check_private_key(sslCtx) ){ |
| 323 | fossil_fatal("Private key does not match the certificate public key."); |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | #endif /* FOSSIL_ENABLE_SSL */ |
| 328 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -133,11 +133,11 @@ | |
| 133 | int hasSavedCertificate = 0; |
| 134 | char *connStr ; |
| 135 | ssl_global_init(); |
| 136 | |
| 137 | /* If client certificate/key has been set, load them into the SSL context. */ |
| 138 | ssl_load_client_authfiles(); |
| 139 | |
| 140 | /* Get certificate for current server from global config and |
| 141 | * (if we have it in config) add it to certificate store. |
| 142 | */ |
| 143 | cert = ssl_get_certificate(); |
| @@ -294,34 +294,92 @@ | |
| 294 | /* |
| 295 | ** Read client certificate and key, if set, and store them in the SSL context |
| 296 | ** to allow communication with servers which are configured to verify client |
| 297 | ** certificates and certificate chains. |
| 298 | ** We only support PEM and don't support password protected keys. |
| 299 | ** |
| 300 | ** Always try the environment variables first, and if they aren't set, then |
| 301 | ** use the global config. |
| 302 | */ |
| 303 | void ssl_load_client_authfiles(void) |
| 304 | { |
| 305 | char *cafile; |
| 306 | char *capath; |
| 307 | char *certfile; |
| 308 | char *keyfile; |
| 309 | |
| 310 | cafile = ssl_get_and_set_file_ref("FOSSIL_CAFILE", "cafile"); |
| 311 | capath = ssl_get_and_set_file_ref("FOSSIL_CAPATH", "capath"); |
| 312 | |
| 313 | if( cafile || capath ){ |
| 314 | /* The OpenSSL documentation warns that if several CA certificates match |
| 315 | * the same name, key identifier and serial number conditions, only the |
| 316 | * first will be examined. The caveat situation is when one stores an |
| 317 | * expired CA certificate among the valid ones. |
| 318 | * Simply put: Do not mix expired and valid certificates. |
| 319 | */ |
| 320 | if( SSL_CTX_load_verify_locations(sslCtx, cafile, capath) == 0){ |
| 321 | fossil_fatal("SSL: Unable to load CA verification file/path"); |
| 322 | } |
| 323 | }else{ |
| 324 | fossil_warning("SSL: CA file/path missing for certificate verification."); |
| 325 | } |
| 326 | |
| 327 | certfile = ssl_get_and_set_file_ref("FOSSIL_CCERT", "ccert"); |
| 328 | if( !certfile ){ |
| 329 | free(capath); |
| 330 | free(cafile); |
| 331 | return; |
| 332 | } |
| 333 | |
| 334 | keyfile = ssl_get_and_set_file_ref("FOSSIL_CKEY", "ckey"); |
| 335 | |
| 336 | /* Assume the key is in the certificate file if key file was not specified */ |
| 337 | if( certfile && !keyfile ) |
| 338 | keyfile = certfile; |
| 339 | |
| 340 | if( SSL_CTX_use_certificate_file(sslCtx, certfile, SSL_FILETYPE_PEM) <= 0 ){ |
| 341 | fossil_fatal("SSL: Unable to open client certificate in %s.", certfile); |
| 342 | } |
| 343 | if( SSL_CTX_use_PrivateKey_file(sslCtx, keyfile, SSL_FILETYPE_PEM) <= 0 ){ |
| 344 | fossil_fatal("SSL: Unable to open client key in %s.", keyfile); |
| 345 | } |
| 346 | |
| 347 | if( !SSL_CTX_check_private_key(sslCtx) ){ |
| 348 | fossil_fatal("SSL: Private key does not match the certificate public " |
| 349 | "key."); |
| 350 | } |
| 351 | |
| 352 | free(keyfile); |
| 353 | free(certfile); |
| 354 | free(capath); |
| 355 | free(cafile); |
| 356 | } |
| 357 | |
| 358 | /* |
| 359 | ** Get SSL authentication file reference from environment variable. If set, |
| 360 | ** then store varaible in global config. If environment variable was not set, |
| 361 | ** attempt to get variable from global config. |
| 362 | **/ |
| 363 | char *ssl_get_and_set_file_ref(const char *envvar, const char *dbvar) |
| 364 | { |
| 365 | char *zVar; |
| 366 | char *zTmp; |
| 367 | |
| 368 | zTmp = mprintf("%s:%s", dbvar, g.urlName); |
| 369 | |
| 370 | zVar = getenv(envvar); |
| 371 | if( zVar ){ |
| 372 | zVar = strdup(zVar); |
| 373 | if( zVar == NULL ){ |
| 374 | fossil_fatal("Unable to allocate memory for %s value.", envvar); |
| 375 | } |
| 376 | db_set(zTmp, zVar, 1); |
| 377 | }else{ |
| 378 | zVar = db_get(zTmp, NULL); |
| 379 | } |
| 380 | free(zTmp); |
| 381 | |
| 382 | return zVar; |
| 383 | } |
| 384 | |
| 385 | #endif /* FOSSIL_ENABLE_SSL */ |
| 386 |