Fossil SCM

Check the certificate of an https server given by IP address against the certificate's IP addresses. Don't send a TLS server name for an IP address or a non-ASCII host name.

danshearer 2026-10-11 21:20 UTC trunk
Commit 6753e74e26c9167c2dc573453653980bb365ec93d659de22a8949bd74765c5dd
1 file changed +41 -2
+41 -2
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -35,10 +35,11 @@
3535
3636
#include <openssl/bio.h>
3737
#include <openssl/ssl.h>
3838
#include <openssl/err.h>
3939
#include <openssl/x509.h>
40
+#include <openssl/x509v3.h>
4041
4142
#include <assert.h>
4243
#include <sys/types.h>
4344
4445
/*
@@ -436,10 +437,39 @@
436437
** real server or a man-in-the-middle imposter.
437438
*/
438439
void ssl_disable_cert_verification(void){
439440
sslNoCertVerify = 1;
440441
}
442
+
443
+/*
444
+** Return true if zHost is an IPv4 or IPv6 address. A certificate
445
+** lists an IP address separately from its DNS names, and RFC 6066 does not
446
+** permit an IP address as the TLS server name.
447
+*/
448
+static int ssl_host_is_ip_address(const char *zHost){
449
+ ASN1_OCTET_STRING *pIp = a2i_IPADDRESS(zHost);
450
+ if( pIp==0 ){
451
+ ERR_clear_error();
452
+ return 0;
453
+ }
454
+ ASN1_OCTET_STRING_free(pIp);
455
+ return 1;
456
+}
457
+
458
+/*
459
+** Return true if zHost contains a byte outside ASCII. RFC 6066 permits
460
+** only an ASCII DNS name as the TLS server name, so an internationalized
461
+** domain name would have to be sent in its "xn--" form, but Fossil does not
462
+** use that form.
463
+*/
464
+static int ssl_host_is_non_ascii(const char *zHost){
465
+ while( *zHost ){
466
+ if( (unsigned char)*zHost >= 0x80 ) return 1;
467
+ zHost++;
468
+ }
469
+ return 0;
470
+}
441471
442472
/*
443473
** Open an SSL connection as a client that is to connect to the server
444474
** identified by pUrlData.
445475
**
@@ -452,10 +482,11 @@
452482
** Return the number of errors.
453483
*/
454484
int ssl_open_client(UrlData *pUrlData){
455485
X509 *cert;
456486
const char *zRemoteHost;
487
+ int isIpAddr; /* True if zRemoteHost is an IP address */
457488
BIO *sBio;
458489
459490
ssl_global_init_client();
460491
if( socket_open(pUrlData) ){
461492
ssl_set_errmsg("SSL: cannot open socket (%s)", socket_errmsg());
@@ -477,13 +508,16 @@
477508
BIO_set_ssl(sBio, ssl, BIO_NOCLOSE);
478509
BIO_set_ssl_mode(iBio, 1);
479510
BIO_get_ssl(iBio, &ssl);
480511
481512
zRemoteHost = pUrlData->useProxy ? pUrlData->hostname : pUrlData->name;
513
+ isIpAddr = ssl_host_is_ip_address(zRemoteHost);
482514
483515
#if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
484
- if( !SSL_set_tlsext_host_name(ssl, zRemoteHost)){
516
+ if( !isIpAddr && !ssl_host_is_non_ascii(zRemoteHost)
517
+ && !SSL_set_tlsext_host_name(ssl, zRemoteHost)
518
+ ){
485519
fossil_warning("WARNING: failed to set server name indication (SNI), "
486520
"continuing without it.\n");
487521
}
488522
#endif
489523
@@ -490,11 +524,16 @@
490524
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
491525
#if OPENSSL_VERSION_NUMBER >= 0x010002000
492526
if( !sslNoCertVerify ){
493527
X509_VERIFY_PARAM *param = 0;
494528
param = SSL_get0_param(ssl);
495
- if( !X509_VERIFY_PARAM_set1_host(param, zRemoteHost, strlen(zRemoteHost)) ){
529
+ if( isIpAddr ){
530
+ if( !X509_VERIFY_PARAM_set1_ip_asc(param, zRemoteHost) ){
531
+ fossil_fatal("failed to set IP address.");
532
+ }
533
+ }else if( !X509_VERIFY_PARAM_set1_host(param, zRemoteHost,
534
+ strlen(zRemoteHost)) ){
496535
fossil_fatal("failed to set hostname.");
497536
}
498537
/* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */
499538
}
500539
#endif
501540
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -35,10 +35,11 @@
35
36 #include <openssl/bio.h>
37 #include <openssl/ssl.h>
38 #include <openssl/err.h>
39 #include <openssl/x509.h>
 
40
41 #include <assert.h>
42 #include <sys/types.h>
43
44 /*
@@ -436,10 +437,39 @@
436 ** real server or a man-in-the-middle imposter.
437 */
438 void ssl_disable_cert_verification(void){
439 sslNoCertVerify = 1;
440 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
442 /*
443 ** Open an SSL connection as a client that is to connect to the server
444 ** identified by pUrlData.
445 **
@@ -452,10 +482,11 @@
452 ** Return the number of errors.
453 */
454 int ssl_open_client(UrlData *pUrlData){
455 X509 *cert;
456 const char *zRemoteHost;
 
457 BIO *sBio;
458
459 ssl_global_init_client();
460 if( socket_open(pUrlData) ){
461 ssl_set_errmsg("SSL: cannot open socket (%s)", socket_errmsg());
@@ -477,13 +508,16 @@
477 BIO_set_ssl(sBio, ssl, BIO_NOCLOSE);
478 BIO_set_ssl_mode(iBio, 1);
479 BIO_get_ssl(iBio, &ssl);
480
481 zRemoteHost = pUrlData->useProxy ? pUrlData->hostname : pUrlData->name;
 
482
483 #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
484 if( !SSL_set_tlsext_host_name(ssl, zRemoteHost)){
 
 
485 fossil_warning("WARNING: failed to set server name indication (SNI), "
486 "continuing without it.\n");
487 }
488 #endif
489
@@ -490,11 +524,16 @@
490 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
491 #if OPENSSL_VERSION_NUMBER >= 0x010002000
492 if( !sslNoCertVerify ){
493 X509_VERIFY_PARAM *param = 0;
494 param = SSL_get0_param(ssl);
495 if( !X509_VERIFY_PARAM_set1_host(param, zRemoteHost, strlen(zRemoteHost)) ){
 
 
 
 
 
496 fossil_fatal("failed to set hostname.");
497 }
498 /* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */
499 }
500 #endif
501
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -35,10 +35,11 @@
35
36 #include <openssl/bio.h>
37 #include <openssl/ssl.h>
38 #include <openssl/err.h>
39 #include <openssl/x509.h>
40 #include <openssl/x509v3.h>
41
42 #include <assert.h>
43 #include <sys/types.h>
44
45 /*
@@ -436,10 +437,39 @@
437 ** real server or a man-in-the-middle imposter.
438 */
439 void ssl_disable_cert_verification(void){
440 sslNoCertVerify = 1;
441 }
442
443 /*
444 ** Return true if zHost is an IPv4 or IPv6 address. A certificate
445 ** lists an IP address separately from its DNS names, and RFC 6066 does not
446 ** permit an IP address as the TLS server name.
447 */
448 static int ssl_host_is_ip_address(const char *zHost){
449 ASN1_OCTET_STRING *pIp = a2i_IPADDRESS(zHost);
450 if( pIp==0 ){
451 ERR_clear_error();
452 return 0;
453 }
454 ASN1_OCTET_STRING_free(pIp);
455 return 1;
456 }
457
458 /*
459 ** Return true if zHost contains a byte outside ASCII. RFC 6066 permits
460 ** only an ASCII DNS name as the TLS server name, so an internationalized
461 ** domain name would have to be sent in its "xn--" form, but Fossil does not
462 ** use that form.
463 */
464 static int ssl_host_is_non_ascii(const char *zHost){
465 while( *zHost ){
466 if( (unsigned char)*zHost >= 0x80 ) return 1;
467 zHost++;
468 }
469 return 0;
470 }
471
472 /*
473 ** Open an SSL connection as a client that is to connect to the server
474 ** identified by pUrlData.
475 **
@@ -452,10 +482,11 @@
482 ** Return the number of errors.
483 */
484 int ssl_open_client(UrlData *pUrlData){
485 X509 *cert;
486 const char *zRemoteHost;
487 int isIpAddr; /* True if zRemoteHost is an IP address */
488 BIO *sBio;
489
490 ssl_global_init_client();
491 if( socket_open(pUrlData) ){
492 ssl_set_errmsg("SSL: cannot open socket (%s)", socket_errmsg());
@@ -477,13 +508,16 @@
508 BIO_set_ssl(sBio, ssl, BIO_NOCLOSE);
509 BIO_set_ssl_mode(iBio, 1);
510 BIO_get_ssl(iBio, &ssl);
511
512 zRemoteHost = pUrlData->useProxy ? pUrlData->hostname : pUrlData->name;
513 isIpAddr = ssl_host_is_ip_address(zRemoteHost);
514
515 #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
516 if( !isIpAddr && !ssl_host_is_non_ascii(zRemoteHost)
517 && !SSL_set_tlsext_host_name(ssl, zRemoteHost)
518 ){
519 fossil_warning("WARNING: failed to set server name indication (SNI), "
520 "continuing without it.\n");
521 }
522 #endif
523
@@ -490,11 +524,16 @@
524 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
525 #if OPENSSL_VERSION_NUMBER >= 0x010002000
526 if( !sslNoCertVerify ){
527 X509_VERIFY_PARAM *param = 0;
528 param = SSL_get0_param(ssl);
529 if( isIpAddr ){
530 if( !X509_VERIFY_PARAM_set1_ip_asc(param, zRemoteHost) ){
531 fossil_fatal("failed to set IP address.");
532 }
533 }else if( !X509_VERIFY_PARAM_set1_host(param, zRemoteHost,
534 strlen(zRemoteHost)) ){
535 fossil_fatal("failed to set hostname.");
536 }
537 /* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */
538 }
539 #endif
540

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button