Fossil SCM

fossil-scm / src / http_ssl.c
Source Blame History 1187 lines
68fa646… drh 1 /*
c19f34c… drh 2 ** Copyright (c) 2009 D. Richard Hipp
68fa646… drh 3 **
68fa646… drh 4 ** This program is free software; you can redistribute it and/or
c7133bd… drh 5 ** modify it under the terms of the Simplified BSD License (also
c7133bd… drh 6 ** known as the "2-Clause License" or "FreeBSD License".)
68fa646… drh 7 **
68fa646… drh 8 ** This program is distributed in the hope that it will be useful,
c7133bd… drh 9 ** but without any warranty; without even the implied warranty of
c7133bd… drh 10 ** merchantability or fitness for a particular purpose.
68fa646… drh 11 **
68fa646… drh 12 ** Author contact information:
68fa646… drh 13 ** [email protected]
68fa646… drh 14 ** http://www.hwaci.com/drh/
68fa646… drh 15 **
68fa646… drh 16 *******************************************************************************
68fa646… drh 17 **
68fa646… drh 18 ** This file manages low-level SSL communications.
68fa646… drh 19 **
68fa646… drh 20 ** This file implements a singleton. A single SSL connection may be active
f6263bb… drh 21 ** at a time. State information is stored in static variables.
f6263bb… drh 22 **
f6263bb… drh 23 ** The SSL connections can be either a client or a server. But all
f6263bb… drh 24 ** connections for a single process must be of the same type, either client
f6263bb… drh 25 ** or server.
68fa646… drh 26 **
68fa646… drh 27 ** SSL support is abstracted out into this module because Fossil can
68fa646… drh 28 ** be compiled without SSL support (which requires OpenSSL library)
68fa646… drh 29 */
68fa646… drh 30
68fa646… drh 31 #include "config.h"
b282400… drh 32 #include "http_ssl.h"
68fa646… drh 33
68fa646… drh 34 #ifdef FOSSIL_ENABLE_SSL
68fa646… drh 35
68fa646… drh 36 #include <openssl/bio.h>
68fa646… drh 37 #include <openssl/ssl.h>
68fa646… drh 38 #include <openssl/err.h>
67147dd… drh 39 #include <openssl/x509.h>
68fa646… drh 40
68fa646… drh 41 #include <assert.h>
68fa646… drh 42 #include <sys/types.h>
68fa646… drh 43
68fa646… drh 44 /*
68fa646… drh 45 ** There can only be a single OpenSSL IO connection open at a time.
68fa646… drh 46 ** State information about that IO is stored in the following
68fa646… drh 47 ** local variables:
68fa646… drh 48 */
f6263bb… drh 49 static int sslIsInit = 0; /* 0: uninit 1: init as client 2: init as server */
3a00b61… drh 50 static BIO *iBio = 0; /* OpenSSL I/O abstraction */
68fa646… drh 51 static char *sslErrMsg = 0; /* Text of most recent OpenSSL error */
68fa646… drh 52 static SSL_CTX *sslCtx; /* SSL context */
68fa646… drh 53 static SSL *ssl;
3c194e2… drh 54 static struct { /* Accept this SSL cert for this session only */
3c194e2… drh 55 char *zHost; /* Subject or host name */
3b529d9… drh 56 char *zHash; /* SHA2-256 hash of the cert */
3c194e2… drh 57 } sException;
3c194e2… drh 58 static int sslNoCertVerify = 0; /* Do not verify SSL certs */
f6263bb… drh 59
f6263bb… drh 60
f6263bb… drh 61 /* This is a self-signed cert in the PEM format that can be used when
f6263bb… drh 62 ** no other certs are available.
f6263bb… drh 63 */
275da70… danield 64 static const char sslSelfCert[] =
f6263bb… drh 65 "-----BEGIN CERTIFICATE-----\n"
f6263bb… drh 66 "MIIDMTCCAhkCFGrDmuJkkzWERP/ITBvzwwI2lv0TMA0GCSqGSIb3DQEBCwUAMFQx\n"
f6263bb… drh 67 "CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOQzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMw\n"
f6263bb… drh 68 "EQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYDVQQDDAZGb3NzaWwwIBcNMjExMjI3MTEz\n"
f6263bb… drh 69 "MTU2WhgPMjEyMTEyMjcxMTMxNTZaMFQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJO\n"
f6263bb… drh 70 "QzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMwEQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYD\n"
f6263bb… drh 71 "VQQDDAZGb3NzaWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCCbTU2\n"
f6263bb… drh 72 "6GRQHQqLq7vyZ0OxpAxmgfAKCxt6eIz+jBi2ZM/CB5vVXWVh2+SkSiWEA3UZiUqX\n"
f6263bb… drh 73 "xZlzmS/CglZdiwLLDJML8B4OiV72oivFH/vJ7+cbvh1dTxnYiHuww7GfQngPrLfe\n"
f6263bb… drh 74 "fiIYPDk1GTUJHBQ7Ue477F7F8vKuHdVgwktF/JDM6M60aSqlo2D/oysirrb+dlur\n"
f6263bb… drh 75 "Tlv0rjsYOfq6bLAajoL3qi/vek6DNssoywbge4PfbTgS9g7Gcgncbcet5pvaS12J\n"
f6263bb… drh 76 "avhFcd4JU4Ity49Hl9S/C2MfZ1tE53xVggRwKz4FPj65M5uymTdcxtjKXtCxIE1k\n"
f6263bb… drh 77 "KxJxXQh7rIYjm+RTAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFkdtpqcybAzJN8G\n"
f6263bb… drh 78 "+ONuUm5sXNbWta7JGvm8l0BTSBcCUtJA3hn16iJqXA9KmLnaF2denC4EYk+KlVU1\n"
f6263bb… drh 79 "QXxskPJ4jB8A5B05jMijYv0nzCxKhviI8CR7GLEEGKzeg9pbW0+O3vaVehoZtdFX\n"
f6263bb… drh 80 "z3SsCssr9QjCLiApQxMzW1Iv3od2JXeHBwfVMFrWA1VCEUCRs8OSW/VOqDPJLVEi\n"
f6263bb… drh 81 "G6wxc4kN9dLK+5S29q3nzl24/qzXoF8P9Re5KBCbrwaHgy+OEEceq5jkmfGFxXjw\n"
f6263bb… drh 82 "pvVCNry5uAhH5NqbXZampUWqiWtM4eTaIPo7Y2mDA1uWhuWtO6F9PsnFJlQHCnwy\n"
f6263bb… drh 83 "s/TsrXk=\n"
f6263bb… drh 84 "-----END CERTIFICATE-----\n";
f6263bb… drh 85
f6263bb… drh 86 /* This is the private-key corresponding to the cert above
f6263bb… drh 87 */
275da70… danield 88 static const char sslSelfPKey[] =
f6263bb… drh 89 "-----BEGIN PRIVATE KEY-----\n"
f6263bb… drh 90 "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCCbTU26GRQHQqL\n"
f6263bb… drh 91 "q7vyZ0OxpAxmgfAKCxt6eIz+jBi2ZM/CB5vVXWVh2+SkSiWEA3UZiUqXxZlzmS/C\n"
f6263bb… drh 92 "glZdiwLLDJML8B4OiV72oivFH/vJ7+cbvh1dTxnYiHuww7GfQngPrLfefiIYPDk1\n"
f6263bb… drh 93 "GTUJHBQ7Ue477F7F8vKuHdVgwktF/JDM6M60aSqlo2D/oysirrb+dlurTlv0rjsY\n"
f6263bb… drh 94 "Ofq6bLAajoL3qi/vek6DNssoywbge4PfbTgS9g7Gcgncbcet5pvaS12JavhFcd4J\n"
f6263bb… drh 95 "U4Ity49Hl9S/C2MfZ1tE53xVggRwKz4FPj65M5uymTdcxtjKXtCxIE1kKxJxXQh7\n"
f6263bb… drh 96 "rIYjm+RTAgMBAAECggEANfTH1vc8yIe7HRzmm9lsf8jF+II4s2705y2H5qY+cvYx\n"
f6263bb… drh 97 "nKtZJGOG1X0KkYy7CGoFv5K0cSUl3lS5FVamM/yWIzoIex/Sz2C1EIL2aI5as6ez\n"
f6263bb… drh 98 "jB6SN0/J+XI8+Vt7186/rHxfdIPpxuzjHbxX3HTpScETNWcLrghbrPxakbTPPxwt\n"
f6263bb… drh 99 "+x7QlPmmkFNuMfvkzToFf9NdwL++44TeBPOpvD/Lrw+eyqdth9RJPq9cM96plh9V\n"
f6263bb… drh 100 "HuRqeD8+QNafaXBdSQs3FJK/cDK/vWGKZWIfFVSDbDhwYljkXGijreFjtXQfkkpF\n"
f6263bb… drh 101 "rl1J87/H9Ee7z8fTD2YXQHl+0/rghAVtac3u54dpQQKBgQC2XG3OEeMrOp9dNkUd\n"
f6263bb… drh 102 "F8VffUg0ecwG+9L3LCe7U71K0kPmXjV6xNnuYcNQu84kptc5vI8wD23p29LaxdNc\n"
f6263bb… drh 103 "9m0lcw06/YYBOPkNphcHkINYZTvVJF10mL3isymzMaTtwDkZUkOjL1B+MTiFT/qp\n"
f6263bb… drh 104 "ARKrTYGJ4HxY7+tUkI5pUmg4PQKBgQC3GA4d1Rz3Pb/RRpcsZgWknKsKhoN36mSn\n"
f6263bb… drh 105 "xFJ3wPBvVv2B1ltTMzh/+the0ty6clzMrvoLERzRcheDsNrc/j/TUVG8sVdBYJwX\n"
f6263bb… drh 106 "tMZyFW4NVMOErT/1ukh6jBqIMBo6NJL3EV/AKj0yniksgKOr0/AAduAccnGST8Jd\n"
f6263bb… drh 107 "SHOdjwvHzwKBgGZBq/zqgNTDuYseHGE07CMgcDWkumiMGv8ozlq3mSR0hUiPOTPP\n"
f6263bb… drh 108 "YFjQjyIdPXnF6FfiyPPtIvgIoNK2LVAqiod+XUPf152l4dnqcW13dn9BvOxGyPTR\n"
f6263bb… drh 109 "lWCikFaAFviOWjY9r9m4dU1dslDmySqthFd0TZgPvgps9ivkJ0cdw30NAoGAMC/E\n"
f6263bb… drh 110 "h1VvKiK2OP27C5ROJ+STn1GHiCfIFd81VQ8SODtMvL8NifgRBp2eFFaqgOdYRQZI\n"
f6263bb… drh 111 "CGGYlAbS6XXCJCdF5Peh62dA75PdgN+y2pOJQzjrvB9cle9Q4++7i9wdCvSLOTr5\n"
f6263bb… drh 112 "WDnFoWy+qVexu6crovOmR9ZWzYrwPFy1EOJ010ECgYBl7Q+jmjOSqsVwhFZ0U7LG\n"
f6263bb… drh 113 "diN+vXhWfn1wfOWd8u79oaqU/Oy7xyKW2p3H5z2KFrBM/vib53Lh4EwFZjcX+jVG\n"
f6263bb… drh 114 "krAmbL+M/hP7z3TD2UbESAzR/c6l7FU45xN84Lsz5npkR8H/uAHuqLgb9e430Mjx\n"
f6263bb… drh 115 "YNMwdb8rChHHChNZu6zuxw==\n"
f6263bb… drh 116 "-----END PRIVATE KEY-----\n";
f6263bb… drh 117
f6263bb… drh 118 /*
f6263bb… drh 119 ** Read a PEM certificate from memory and push it into an SSL_CTX.
f6263bb… drh 120 ** Return the number of errors.
f6263bb… drh 121 */
f6263bb… drh 122 static int sslctx_use_cert_from_mem(
f6263bb… drh 123 SSL_CTX *ctx,
f6263bb… drh 124 const char *pData,
f6263bb… drh 125 int nData
f6263bb… drh 126 ){
f6263bb… drh 127 BIO *in;
f6263bb… drh 128 int rc = 1;
f6263bb… drh 129 X509 *x = 0;
f6263bb… drh 130 X509 *cert = 0;
f6263bb… drh 131
f6263bb… drh 132 in = BIO_new_mem_buf(pData, nData);
f6263bb… drh 133 if( in==0 ) goto end_of_ucfm;
99ab5cd… danield 134 /* x = X509_new_ex(ctx->libctx, ctx->propq); */
f6263bb… drh 135 x = X509_new();
f6263bb… drh 136 if( x==0 ) goto end_of_ucfm;
f6263bb… drh 137 cert = PEM_read_bio_X509(in, &x, 0, 0);
f6263bb… drh 138 if( cert==0 ) goto end_of_ucfm;
f6263bb… drh 139 rc = SSL_CTX_use_certificate(ctx, x)<=0;
f6263bb… drh 140 end_of_ucfm:
f6263bb… drh 141 X509_free(x);
f6263bb… drh 142 BIO_free(in);
f6263bb… drh 143 return rc;
f6263bb… drh 144 }
f6263bb… drh 145
f6263bb… drh 146 /*
f6263bb… drh 147 ** Read a PEM private key from memory and add it to an SSL_CTX.
f6263bb… drh 148 ** Return the number of errors.
f6263bb… drh 149 */
f6263bb… drh 150 static int sslctx_use_pkey_from_mem(
f6263bb… drh 151 SSL_CTX *ctx,
f6263bb… drh 152 const char *pData,
f6263bb… drh 153 int nData
f6263bb… drh 154 ){
f6263bb… drh 155 int rc = 1;
f6263bb… drh 156 BIO *in;
f6263bb… drh 157 EVP_PKEY *pkey = 0;
f6263bb… drh 158
f6263bb… drh 159 in = BIO_new_mem_buf(pData, nData);
f6263bb… drh 160 if( in==0 ) goto end_of_upkfm;
f6263bb… drh 161 pkey = PEM_read_bio_PrivateKey(in, 0, 0, 0);
f6263bb… drh 162 if( pkey==0 ) goto end_of_upkfm;
f6263bb… drh 163 rc = SSL_CTX_use_PrivateKey(ctx, pkey)<=0;
f6263bb… drh 164 EVP_PKEY_free(pkey);
f6263bb… drh 165 end_of_upkfm:
f6263bb… drh 166 BIO_free(in);
f6263bb… drh 167 return rc;
f6263bb… drh 168 }
68fa646… drh 169
68fa646… drh 170 /*
68fa646… drh 171 ** Clear the SSL error message
68fa646… drh 172 */
68fa646… drh 173 static void ssl_clear_errmsg(void){
68fa646… drh 174 free(sslErrMsg);
68fa646… drh 175 sslErrMsg = 0;
68fa646… drh 176 }
68fa646… drh 177
68fa646… drh 178 /*
68fa646… drh 179 ** Set the SSL error message.
68fa646… drh 180 */
cfb8d66… jan.nijtmans 181 void ssl_set_errmsg(const char *zFormat, ...){
68fa646… drh 182 va_list ap;
68fa646… drh 183 ssl_clear_errmsg();
68fa646… drh 184 va_start(ap, zFormat);
68fa646… drh 185 sslErrMsg = vmprintf(zFormat, ap);
68fa646… drh 186 va_end(ap);
68fa646… drh 187 }
68fa646… drh 188
68fa646… drh 189 /*
68fa646… drh 190 ** Return the current SSL error message
68fa646… drh 191 */
68fa646… drh 192 const char *ssl_errmsg(void){
68fa646… drh 193 return sslErrMsg;
68fa646… drh 194 }
68fa646… drh 195
68fa646… drh 196 /*
9a0c995… drh 197 ** When a server requests a client certificate that hasn't been provided,
9a0c995… drh 198 ** display a warning message explaining what to do next.
9a0c995… drh 199 */
9a0c995… drh 200 static int ssl_client_cert_callback(SSL *ssl, X509 **x509, EVP_PKEY **pkey){
9a0c995… drh 201 fossil_warning("The remote server requested a client certificate for "
9a0c995… drh 202 "authentication. Specify the pathname to a file containing the PEM "
9a0c995… drh 203 "encoded certificate and private key with the --ssl-identity option "
9a0c995… drh 204 "or the ssl-identity setting.");
cfb8d66… jan.nijtmans 205 return 0; /* no cert available */
cfb8d66… jan.nijtmans 206 }
cfb8d66… jan.nijtmans 207
cfb8d66… jan.nijtmans 208 /*
d847300… danield 209 ** Convert an OpenSSL ASN1_TIME to an ISO8601 timestamp.
d847300… danield 210 **
275da70… danield 211 ** Per RFC 5280, ASN1 timestamps in X.509 certificates must
d847300… danield 212 ** be in UTC (Zulu timezone) with no fractional seconds.
d847300… danield 213 **
d847300… danield 214 ** If showUtc==1, add " UTC" at the end of the returned string. This is
d847300… danield 215 ** not ISO8601-compliant, but makes the displayed value more user-friendly.
d847300… danield 216 */
af8109c… danield 217 static const char *ssl_asn1time_to_iso8601(ASN1_TIME *asn1_time,
d847300… danield 218 int showUtc){
d847300… danield 219 assert( showUtc==0 || showUtc==1 );
d847300… danield 220 if( !ASN1_TIME_check(asn1_time) ){
d847300… danield 221 return mprintf("Bad time value");
d847300… danield 222 }else{
d847300… danield 223 char res[20];
d847300… danield 224 char *pr = res;
0d239b5… drh 225 #if OPENSSL_VERSION_NUMBER < 0x10100000L
0d239b5… drh 226 #define ASN1_STRING_get0_data ASN1_STRING_data
0d239b5… drh 227 #endif
0d239b5… drh 228 const char *pt = (const char *)ASN1_STRING_get0_data(asn1_time);
d847300… danield 229 /* 0123456789 1234
d847300… danield 230 ** UTCTime: YYMMDDHHMMSSZ (YY >= 50 ? 19YY : 20YY)
d847300… danield 231 ** GeneralizedTime: YYYYMMDDHHMMSSZ */
0d239b5… drh 232 if( ASN1_STRING_length(asn1_time) < 15 ){
d847300… danield 233 /* UTCTime, fill out century digits */
d847300… danield 234 *pr++ = pt[0]>='5' ? '1' : '2';
d847300… danield 235 *pr++ = pt[0]>='5' ? '9' : '0';
d847300… danield 236 }else{
d847300… danield 237 /* GeneralizedTime, copy century digits and advance source */
d847300… danield 238 *pr++ = pt[0]; *pr++ = pt[1];
d847300… danield 239 pt += 2;
d847300… danield 240 }
d847300… danield 241 *pr++ = pt[0]; *pr++ = pt[1]; *pr++ = '-';
d847300… danield 242 *pr++ = pt[2]; *pr++ = pt[3]; *pr++ = '-';
d847300… danield 243 *pr++ = pt[4]; *pr++ = pt[5]; *pr++ = ' ';
d847300… danield 244 *pr++ = pt[6]; *pr++ = pt[7]; *pr++ = ':';
d847300… danield 245 *pr++ = pt[8]; *pr++ = pt[9]; *pr++ = ':';
d847300… danield 246 *pr++ = pt[10]; *pr++ = pt[11]; *pr = '\0';
d847300… danield 247 return mprintf("%s%s", res, (showUtc ? " UTC" : ""));
d847300… danield 248 }
d847300… danield 249 }
d847300… danield 250
d847300… danield 251 /*
68fa646… drh 252 ** Call this routine once before any other use of the SSL interface.
68fa646… drh 253 ** This routine does initial configuration of the SSL module.
68fa646… drh 254 */
f6263bb… drh 255 static void ssl_global_init_client(void){
5eb8f01… drh 256 const char *identityFile;
cfb8d66… jan.nijtmans 257
68fa646… drh 258 if( sslIsInit==0 ){
e225dc9… drh 259 const char *zFile;
e225dc9… drh 260 const char *zCaFile = 0;
e225dc9… drh 261 const char *zCaDirectory = 0;
e225dc9… drh 262 int i;
e225dc9… drh 263
68fa646… drh 264 SSL_library_init();
68fa646… drh 265 SSL_load_error_strings();
cfb8d66… jan.nijtmans 266 OpenSSL_add_all_algorithms();
68fa646… drh 267 sslCtx = SSL_CTX_new(SSLv23_client_method());
7a44fdd… drh 268 /* Disable SSLv2 and SSLv3 */
7a44fdd… drh 269 SSL_CTX_set_options(sslCtx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);
7a44fdd… drh 270
e225dc9… drh 271 /* Find the trust store */
e225dc9… drh 272 zFile = 0;
e225dc9… drh 273 for(i=0; zFile==0 && i<5; i++){
e225dc9… drh 274 switch( i ){
e2bdc10… danield 275 case 0: /* First priority is environment variables */
e225dc9… drh 276 zFile = fossil_getenv(X509_get_default_cert_file_env());
e225dc9… drh 277 break;
e225dc9… drh 278 case 1:
e225dc9… drh 279 zFile = fossil_getenv(X509_get_default_cert_dir_env());
e225dc9… drh 280 break;
e225dc9… drh 281 case 2:
0ca1fc4… drh 282 if( !g.repositoryOpen ) db_open_config(0,0);
e225dc9… drh 283 zFile = db_get("ssl-ca-location",0);
e225dc9… drh 284 break;
e225dc9… drh 285 case 3:
e225dc9… drh 286 zFile = X509_get_default_cert_file();
e225dc9… drh 287 break;
e225dc9… drh 288 case 4:
e225dc9… drh 289 zFile = X509_get_default_cert_dir();
e225dc9… drh 290 break;
e225dc9… drh 291 }
e225dc9… drh 292 if( zFile==0 ) continue;
e225dc9… drh 293 switch( file_isdir(zFile, ExtFILE) ){
9a0c995… drh 294 case 0: { /* doesn't exist */
e225dc9… drh 295 zFile = 0;
9a0c995… drh 296 break;
9a0c995… drh 297 }
9a0c995… drh 298 case 1: { /* directory */
e225dc9… drh 299 zCaFile = 0;
e225dc9… drh 300 zCaDirectory = zFile;
5eb8f01… drh 301 break;
5eb8f01… drh 302 }
5eb8f01… drh 303 case 2: { /* file */
e225dc9… drh 304 zCaFile = zFile;
e225dc9… drh 305 zCaDirectory = 0;
5eb8f01… drh 306 break;
5eb8f01… drh 307 }
5eb8f01… drh 308 }
e225dc9… drh 309 }
e225dc9… drh 310 if( zFile==0 ){
e225dc9… drh 311 /* fossil_fatal("Cannot find a trust store"); */
e225dc9… drh 312 }else if( SSL_CTX_load_verify_locations(sslCtx, zCaFile, zCaDirectory)==0 ){
e225dc9… drh 313 fossil_fatal("Cannot load CA root certificates from %s", zFile);
e225dc9… drh 314 }
0ca1fc4… drh 315
6fc64ab… florian 316 /* Enable OpenSSL to use the Windows system ROOT certificate store to search for
6fc64ab… florian 317 ** certificates missing in the file and directory trust stores already loaded by
6fc64ab… florian 318 ** `SSL_CTX_load_verify_locations()'.
6fc64ab… florian 319 ** This feature was introduced with OpenSSL 3.2.0, and may be enabled by default
6fc64ab… florian 320 ** for future versions of OpenSSL, and explicit initialization may be redundant.
6fc64ab… florian 321 ** NOTE TO HACKERS TWEAKING THEIR OPENSSL CONFIGURATION:
6fc64ab… florian 322 ** The following OpenSSL configuration options must not be used for this feature
6fc64ab… florian 323 ** to be available: `no-autoalginit', `no-winstore'. The Fossil makefiles do not
6fc64ab… florian 324 ** currently set these options when building OpenSSL for Windows. */
de6a059… florian 325 #if defined(_WIN32)
6fc64ab… florian 326 #if OPENSSL_VERSION_NUMBER >= 0x030200000
2b6ad00… drh 327 if( SSLeay()!=0x30500000 /* Don't use for 3.5.0 due to a bug */
2b6ad00… drh 328 && SSL_CTX_load_verify_store(sslCtx, "org.openssl.winstore:")==0
2b6ad00… drh 329 ){
5d993d5… florian 330 fossil_print("NOTICE: Failed to load the Windows root certificates.\n");
de6a059… florian 331 }
6fc64ab… florian 332 #endif /* OPENSSL_VERSION_NUMBER >= 0x030200000 */
6fc64ab… florian 333 #endif /* _WIN32 */
6fc64ab… florian 334
5eb8f01… drh 335 /* Load client SSL identity, preferring the filename specified on the
5eb8f01… drh 336 ** command line */
5eb8f01… drh 337 if( g.zSSLIdentity!=0 ){
5eb8f01… drh 338 identityFile = g.zSSLIdentity;
5eb8f01… drh 339 }else{
5eb8f01… drh 340 identityFile = db_get("ssl-identity", 0);
5eb8f01… drh 341 }
5eb8f01… drh 342 if( identityFile!=0 && identityFile[0]!='\0' ){
82b4294… drh 343 if( SSL_CTX_use_certificate_chain_file(sslCtx,identityFile)!=1
5eb8f01… drh 344 || SSL_CTX_use_PrivateKey_file(sslCtx,identityFile,SSL_FILETYPE_PEM)!=1
5eb8f01… drh 345 ){
31c7bdb… larrybr 346 fossil_fatal("Could not load SSL identity from %s", identityFile);
5eb8f01… drh 347 }
5eb8f01… drh 348 }
5eb8f01… drh 349 /* Register a callback to tell the user what to do when the server asks
5eb8f01… drh 350 ** for a cert */
5eb8f01… drh 351 SSL_CTX_set_client_cert_cb(sslCtx, ssl_client_cert_callback);
5eb8f01… drh 352
5eb8f01… drh 353 sslIsInit = 1;
f6263bb… drh 354 }else{
f6263bb… drh 355 assert( sslIsInit==1 );
68fa646… drh 356 }
68fa646… drh 357 }
68fa646… drh 358
68fa646… drh 359 /*
68fa646… drh 360 ** Call this routine to shutdown the SSL module prior to program exit.
68fa646… drh 361 */
68fa646… drh 362 void ssl_global_shutdown(void){
68fa646… drh 363 if( sslIsInit ){
68fa646… drh 364 SSL_CTX_free(sslCtx);
68fa646… drh 365 ssl_clear_errmsg();
68fa646… drh 366 sslIsInit = 0;
68fa646… drh 367 }
f39c525… andybradford 368 socket_global_shutdown();
68fa646… drh 369 }
68fa646… drh 370
68fa646… drh 371 /*
f6263bb… drh 372 ** Close the currently open client SSL connection. If no connection is open,
68fa646… drh 373 ** this routine is a no-op.
68fa646… drh 374 */
f6263bb… drh 375 void ssl_close_client(void){
68fa646… drh 376 if( iBio!=NULL ){
68fa646… drh 377 (void)BIO_reset(iBio);
68fa646… drh 378 BIO_free_all(iBio);
3a00b61… drh 379 iBio = NULL;
3a33435… jan.nijtmans 380 }
f39c525… andybradford 381 socket_close();
3a33435… jan.nijtmans 382 }
3a33435… jan.nijtmans 383
3a33435… jan.nijtmans 384 /* See RFC2817 for details */
3a33435… jan.nijtmans 385 static int establish_proxy_tunnel(UrlData *pUrlData, BIO *bio){
3a33435… jan.nijtmans 386 int rc, httpVerMin;
3a33435… jan.nijtmans 387 char *bbuf;
3a33435… jan.nijtmans 388 Blob snd, reply;
3a33435… jan.nijtmans 389 int done=0,end=0;
3a33435… jan.nijtmans 390 blob_zero(&snd);
3a33435… jan.nijtmans 391 blob_appendf(&snd, "CONNECT %s:%d HTTP/1.1\r\n", pUrlData->hostname,
3a33435… jan.nijtmans 392 pUrlData->proxyOrigPort);
483ac3d… drh 393 blob_appendf(&snd, "Host: %s:%d\r\n",
483ac3d… drh 394 pUrlData->hostname, pUrlData->proxyOrigPort);
3a33435… jan.nijtmans 395 if( pUrlData->proxyAuth ){
3a33435… jan.nijtmans 396 blob_appendf(&snd, "Proxy-Authorization: %s\r\n", pUrlData->proxyAuth);
68fa646… drh 397 }
3a33435… jan.nijtmans 398 blob_append(&snd, "Proxy-Connection: keep-alive\r\n", -1);
a7a7df7… jan.nijtmans 399 blob_appendf(&snd, "User-Agent: %s\r\n", get_user_agent());
3a33435… jan.nijtmans 400 blob_append(&snd, "\r\n", 2);
3a33435… jan.nijtmans 401 BIO_write(bio, blob_buffer(&snd), blob_size(&snd));
3a33435… jan.nijtmans 402 blob_reset(&snd);
3a33435… jan.nijtmans 403
3a33435… jan.nijtmans 404 /* Wait for end of reply */
3a33435… jan.nijtmans 405 blob_zero(&reply);
3a33435… jan.nijtmans 406 do{
3a33435… jan.nijtmans 407 int len;
3a33435… jan.nijtmans 408 char buf[256];
3a33435… jan.nijtmans 409 len = BIO_read(bio, buf, sizeof(buf));
3a33435… jan.nijtmans 410 blob_append(&reply, buf, len);
3a33435… jan.nijtmans 411
3a33435… jan.nijtmans 412 bbuf = blob_buffer(&reply);
3a33435… jan.nijtmans 413 len = blob_size(&reply);
3a33435… jan.nijtmans 414 while(end < len) {
e8d328c… drh 415 if( bbuf[end]=='\n' ) {
e8d328c… drh 416 if( (end+1<len && bbuf[end+1]=='\n')
5a282cf… george 417 || (end+2<len && bbuf[end+1]=='\r' && bbuf[end+2]=='\n')
e8d328c… drh 418 ){
3a33435… jan.nijtmans 419 done = 1;
3a33435… jan.nijtmans 420 break;
3a33435… jan.nijtmans 421 }
3a33435… jan.nijtmans 422 }
3a33435… jan.nijtmans 423 end++;
3a33435… jan.nijtmans 424 }
3a33435… jan.nijtmans 425 }while(!done);
3a33435… jan.nijtmans 426 sscanf(bbuf, "HTTP/1.%d %d", &httpVerMin, &rc);
3a33435… jan.nijtmans 427 blob_reset(&reply);
3a33435… jan.nijtmans 428 return rc;
3a33435… jan.nijtmans 429 }
3a33435… jan.nijtmans 430
3a33435… jan.nijtmans 431 /*
3c194e2… drh 432 ** Invoke this routine to disable SSL cert verification. After
3c194e2… drh 433 ** this call is made, any SSL cert that the server provides will
3c194e2… drh 434 ** be accepted. Communication will still be encrypted, but the
3c194e2… drh 435 ** client has no way of knowing whether it is talking to the
3c194e2… drh 436 ** real server or a man-in-the-middle imposter.
3c194e2… drh 437 */
9f8dc18… drh 438 void ssl_disable_cert_verification(void){
275da70… danield 439 sslNoCertVerify = 1;
3c194e2… drh 440 }
3c194e2… drh 441
3c194e2… drh 442 /*
f6263bb… drh 443 ** Open an SSL connection as a client that is to connect to the server
f6263bb… drh 444 ** identified by pUrlData.
f6263bb… drh 445 **
f6263bb… drh 446 * The identify of the server is determined as follows:
68fa646… drh 447 **
09908ab… drh 448 ** pUrlData->name Name of the server. Ex: fossil-scm.org
455b2aa… drh 449 ** g.url.name Name of the proxy server, if proxying.
ca0a58f… jan.nijtmans 450 ** pUrlData->port TCP/IP port to use. Ex: 80
68fa646… drh 451 **
68fa646… drh 452 ** Return the number of errors.
68fa646… drh 453 */
f6263bb… drh 454 int ssl_open_client(UrlData *pUrlData){
074767b… drh 455 X509 *cert;
aaab2a1… drh 456 const char *zRemoteHost;
f39c525… andybradford 457 BIO *sBio;
3c194e2… drh 458
f6263bb… drh 459 ssl_global_init_client();
f39c525… andybradford 460 if( socket_open(pUrlData) ){
f39c525… andybradford 461 ssl_set_errmsg("SSL: cannot open socket (%s)", socket_errmsg());
f39c525… andybradford 462 return 1;
f39c525… andybradford 463 }
f39c525… andybradford 464 sBio = BIO_new_socket(socket_get_fd(), 0);
3a33435… jan.nijtmans 465 if( pUrlData->useProxy ){
f39c525… andybradford 466 int rc = establish_proxy_tunnel(pUrlData, sBio);
3a33435… jan.nijtmans 467 if( rc<200||rc>299 ){
3a33435… jan.nijtmans 468 ssl_set_errmsg("SSL: proxy connect failed with HTTP status code %d", rc);
f39c525… andybradford 469 ssl_close_client();
3a33435… jan.nijtmans 470 return 1;
3a33435… jan.nijtmans 471 }
3a33435… jan.nijtmans 472
3a33435… jan.nijtmans 473 pUrlData->path = pUrlData->proxyUrlPath;
f39c525… andybradford 474 }
f39c525… andybradford 475 iBio = BIO_new_ssl(sslCtx, 1);
f39c525… andybradford 476 BIO_push(iBio, sBio);
f39c525… andybradford 477 BIO_set_ssl(sBio, ssl, BIO_NOCLOSE);
f39c525… andybradford 478 BIO_set_ssl_mode(iBio, 1);
074767b… drh 479 BIO_get_ssl(iBio, &ssl);
f39c525… andybradford 480
f39c525… andybradford 481 zRemoteHost = pUrlData->useProxy ? pUrlData->hostname : pUrlData->name;
3a33435… jan.nijtmans 482
cb52442… drh 483 #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
aaab2a1… drh 484 if( !SSL_set_tlsext_host_name(ssl, zRemoteHost)){
074767b… drh 485 fossil_warning("WARNING: failed to set server name indication (SNI), "
074767b… drh 486 "continuing without it.\n");
074767b… drh 487 }
cb52442… drh 488 #endif
074767b… drh 489
074767b… drh 490 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
aaab2a1… drh 491 #if OPENSSL_VERSION_NUMBER >= 0x010002000
aaab2a1… drh 492 if( !sslNoCertVerify ){
aaab2a1… drh 493 X509_VERIFY_PARAM *param = 0;
aaab2a1… drh 494 param = SSL_get0_param(ssl);
aaab2a1… drh 495 if( !X509_VERIFY_PARAM_set1_host(param, zRemoteHost, strlen(zRemoteHost)) ){
aaab2a1… drh 496 fossil_fatal("failed to set hostname.");
aaab2a1… drh 497 }
aaab2a1… drh 498 /* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */
aaab2a1… drh 499 }
aaab2a1… drh 500 #endif
aaab2a1… drh 501
f39c525… andybradford 502 if( BIO_do_handshake(iBio)<=0 ){
cfb8d66… jan.nijtmans 503 ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)",
f39c525… andybradford 504 zRemoteHost,
f39c525… andybradford 505 pUrlData->useProxy ? pUrlData->proxyOrigPort : pUrlData->port,
3a33435… jan.nijtmans 506 ERR_reason_error_string(ERR_get_error()));
f6263bb… drh 507 ssl_close_client();
68fa646… drh 508 return 1;
68fa646… drh 509 }
68fa646… drh 510 /* Check if certificate is valid */
68fa646… drh 511 cert = SSL_get_peer_certificate(ssl);
68fa646… drh 512
68fa646… drh 513 if ( cert==NULL ){
68fa646… drh 514 ssl_set_errmsg("No SSL certificate was presented by the peer");
f6263bb… drh 515 ssl_close_client();
3c194e2… drh 516 return 1;
3c194e2… drh 517 }
3c194e2… drh 518
5623188… danield 519 /* Debugging hint: On unix-like system, run something like:
5623188… danield 520 **
5623188… danield 521 ** SSL_CERT_DIR=/tmp ./fossil sync
5623188… danield 522 **
5623188… danield 523 ** to cause certificate validation to fail, and thus test the fallback
5623188… danield 524 ** logic.
5623188… danield 525 */
3c194e2… drh 526 if( !sslNoCertVerify && SSL_get_verify_result(ssl)!=X509_V_OK ){
82d177f… mistachkin 527 int x, desclen;
3c194e2… drh 528 char *desc, *prompt;
3c194e2… drh 529 Blob ans;
3c194e2… drh 530 char cReply;
3c194e2… drh 531 BIO *mem;
82d177f… mistachkin 532 unsigned char md[EVP_MAX_MD_SIZE];
82d177f… mistachkin 533 char zHash[EVP_MAX_MD_SIZE*2+1];
3c194e2… drh 534 unsigned int mdLength = (int)sizeof(md);
3c194e2… drh 535
3c194e2… drh 536 memset(md, 0, sizeof(md));
3c194e2… drh 537 zHash[0] = 0;
64d79ad… drh 538 /* MMNNFFPPS */
64d79ad… drh 539 #if OPENSSL_VERSION_NUMBER >= 0x010000000
64d79ad… drh 540 x = X509_digest(cert, EVP_sha256(), md, &mdLength);
64d79ad… drh 541 #else
64d79ad… drh 542 x = X509_digest(cert, EVP_sha1(), md, &mdLength);
64d79ad… drh 543 #endif
64d79ad… drh 544 if( x ){
53db40e… drh 545 unsigned j;
3c194e2… drh 546 for(j=0; j<mdLength && j*2+1<sizeof(zHash); ++j){
3c194e2… drh 547 zHash[j*2] = "0123456789abcdef"[md[j]>>4];
3c194e2… drh 548 zHash[j*2+1] = "0123456789abcdef"[md[j]&0xf];
3c194e2… drh 549 }
3c194e2… drh 550 zHash[j*2] = 0;
3c194e2… drh 551 }
3c194e2… drh 552
3c194e2… drh 553 if( ssl_certificate_exception_exists(pUrlData, zHash) ){
3c194e2… drh 554 /* Ignore the failure because an exception exists */
3c194e2… drh 555 ssl_one_time_exception(pUrlData, zHash);
3c194e2… drh 556 }else{
3c194e2… drh 557 /* Tell the user about the failure and ask what to do */
3c194e2… drh 558 mem = BIO_new(BIO_s_mem());
5623188… danield 559 BIO_puts(mem, " subject: ");
3c194e2… drh 560 X509_NAME_print_ex(mem, X509_get_subject_name(cert), 0, XN_FLAG_ONELINE);
5623188… danield 561 BIO_puts(mem, "\n issuer: ");
3c194e2… drh 562 X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE);
d847300… danield 563 BIO_printf(mem, "\n notBefore: %s",
d847300… danield 564 ssl_asn1time_to_iso8601(X509_get_notBefore(cert), 1));
d847300… danield 565 BIO_printf(mem, "\n notAfter: %s",
d847300… danield 566 ssl_asn1time_to_iso8601(X509_get_notAfter(cert), 1));
5623188… danield 567 BIO_printf(mem, "\n sha256: %s", zHash);
82d177f… mistachkin 568 desclen = BIO_get_mem_data(mem, &desc);
275da70… danield 569
82d177f… mistachkin 570 prompt = mprintf("Unable to verify SSL cert from %s\n%.*s\n"
48a860f… stephan 571 "accept this cert and continue (y/N/fingerprint)? ",
82d177f… mistachkin 572 pUrlData->name, desclen, desc);
3c194e2… drh 573 BIO_free(mem);
275da70… danield 574
3c194e2… drh 575 prompt_user(prompt, &ans);
3c194e2… drh 576 free(prompt);
3c194e2… drh 577 cReply = blob_str(&ans)[0];
5623188… danield 578 if( cReply!='y' && cReply!='Y'
5623188… danield 579 && fossil_stricmp(blob_str(&ans),zHash)!=0
5623188… danield 580 ){
3c194e2… drh 581 X509_free(cert);
3c194e2… drh 582 ssl_set_errmsg("SSL cert declined");
f6263bb… drh 583 ssl_close_client();
48a860f… stephan 584 blob_reset(&ans);
3c194e2… drh 585 return 1;
3c194e2… drh 586 }
48a860f… stephan 587 blob_reset(&ans);
3c194e2… drh 588 ssl_one_time_exception(pUrlData, zHash);
3c194e2… drh 589 prompt_user("remember this exception (y/N)? ", &ans);
3c194e2… drh 590 cReply = blob_str(&ans)[0];
3c194e2… drh 591 if( cReply=='y' || cReply=='Y') {
edd280c… stephan 592 db_open_config(0,0);
3c194e2… drh 593 ssl_remember_certificate_exception(pUrlData, zHash);
3c194e2… drh 594 }
3c194e2… drh 595 blob_reset(&ans);
3c194e2… drh 596 }
8b9c933… drh 597 }
8b9c933… drh 598
68fa646… drh 599 X509_free(cert);
68fa646… drh 600 return 0;
68fa646… drh 601 }
68fa646… drh 602
68fa646… drh 603 /*
99a319b… wyoung 604 ** Remember that the cert with the given hash is acceptable for
3c194e2… drh 605 ** use with pUrlData->name.
3c194e2… drh 606 */
3c194e2… drh 607 LOCAL void ssl_remember_certificate_exception(
3c194e2… drh 608 UrlData *pUrlData,
3c194e2… drh 609 const char *zHash
3c194e2… drh 610 ){
0a5d0e1… drh 611 db_set_mprintf(zHash, 1, "cert:%s", pUrlData->name);
3c194e2… drh 612 }
3c194e2… drh 613
3c194e2… drh 614 /*
3c194e2… drh 615 ** Return true if the there exists a certificate exception for
3c194e2… drh 616 ** pUrlData->name that matches the hash.
3c194e2… drh 617 */
3c194e2… drh 618 LOCAL int ssl_certificate_exception_exists(
3c194e2… drh 619 UrlData *pUrlData,
3c194e2… drh 620 const char *zHash
3c194e2… drh 621 ){
3c194e2… drh 622 char *zName, *zValue;
3c194e2… drh 623 if( fossil_strcmp(sException.zHost,pUrlData->name)==0
3c194e2… drh 624 && fossil_strcmp(sException.zHash,zHash)==0
3c194e2… drh 625 ){
3c194e2… drh 626 return 1;
3c194e2… drh 627 }
3c194e2… drh 628 zName = mprintf("cert:%s", pUrlData->name);
3c194e2… drh 629 zValue = db_get(zName,0);
3c194e2… drh 630 fossil_free(zName);
3c194e2… drh 631 return zValue!=0 && strcmp(zHash,zValue)==0;
3c194e2… drh 632 }
3c194e2… drh 633
3c194e2… drh 634 /*
3c194e2… drh 635 ** Remember zHash as an acceptable certificate for this session only.
3c194e2… drh 636 */
9f8dc18… drh 637 LOCAL void ssl_one_time_exception(
3c194e2… drh 638 UrlData *pUrlData,
3c194e2… drh 639 const char *zHash
3c194e2… drh 640 ){
3c194e2… drh 641 fossil_free(sException.zHost);
3c194e2… drh 642 sException.zHost = fossil_strdup(pUrlData->name);
3c194e2… drh 643 fossil_free(sException.zHash);
9f8dc18… drh 644 sException.zHash = fossil_strdup(zHash);
3c194e2… drh 645 }
3c194e2… drh 646
3c194e2… drh 647 /*
f6263bb… drh 648 ** Send content out over the SSL connection from the client to
f6263bb… drh 649 ** the server.
68fa646… drh 650 */
68fa646… drh 651 size_t ssl_send(void *NotUsed, void *pContent, size_t N){
68fa646… drh 652 size_t total = 0;
68fa646… drh 653 while( N>0 ){
4abf607… mistachkin 654 int sent = BIO_write(iBio, pContent, N);
4abf607… mistachkin 655 if( sent<=0 ){
4abf607… mistachkin 656 if( BIO_should_retry(iBio) ){
4abf607… mistachkin 657 continue;
4abf607… mistachkin 658 }
4abf607… mistachkin 659 break;
4abf607… mistachkin 660 }
68fa646… drh 661 total += sent;
68fa646… drh 662 N -= sent;
68fa646… drh 663 pContent = (void*)&((char*)pContent)[sent];
68fa646… drh 664 }
68fa646… drh 665 return total;
68fa646… drh 666 }
68fa646… drh 667
68fa646… drh 668 /*
f6263bb… drh 669 ** Receive content back from the client SSL connection. In other
f6263bb… drh 670 ** words read the reply back from the server.
68fa646… drh 671 */
68fa646… drh 672 size_t ssl_receive(void *NotUsed, void *pContent, size_t N){
68fa646… drh 673 size_t total = 0;
68fa646… drh 674 while( N>0 ){
4abf607… mistachkin 675 int got = BIO_read(iBio, pContent, N);
4abf607… mistachkin 676 if( got<=0 ){
4abf607… mistachkin 677 if( BIO_should_retry(iBio) ){
4abf607… mistachkin 678 continue;
4abf607… mistachkin 679 }
4abf607… mistachkin 680 break;
4abf607… mistachkin 681 }
68fa646… drh 682 total += got;
68fa646… drh 683 N -= got;
68fa646… drh 684 pContent = (void*)&((char*)pContent)[got];
68fa646… drh 685 }
68fa646… drh 686 return total;
68fa646… drh 687 }
68fa646… drh 688
f6263bb… drh 689 /*
f6263bb… drh 690 ** Initialize the SSL library so that it is able to handle
f6263bb… drh 691 ** server-side connections. Invoke fossil_fatal() if there are
f6263bb… drh 692 ** any problems.
f6263bb… drh 693 **
f6263bb… drh 694 ** If zKeyFile and zCertFile are not NULL, then they are the names
f6263bb… drh 695 ** of disk files that hold the certificate and private-key for the
f6263bb… drh 696 ** server. If zCertFile is not NULL but zKeyFile is NULL, then
f6263bb… drh 697 ** zCertFile is assumed to be a concatenation of the certificate and
f6263bb… drh 698 ** the private-key in the PEM format.
f6263bb… drh 699 **
d7008b3… drh 700 ** If zCertFile is "unsafe-builtin", then a built-in self-signed cert
d7008b3… drh 701 ** is used. This built-in cert is insecure and should only be used for
d7008b3… drh 702 ** testing and debugging.
f6263bb… drh 703 */
f6263bb… drh 704 void ssl_init_server(const char *zCertFile, const char *zKeyFile){
d7008b3… drh 705 if( sslIsInit==0 && zCertFile ){
f6263bb… drh 706 SSL_library_init();
f6263bb… drh 707 SSL_load_error_strings();
f6263bb… drh 708 OpenSSL_add_all_algorithms();
f6263bb… drh 709 sslCtx = SSL_CTX_new(SSLv23_server_method());
f6263bb… drh 710 if( sslCtx==0 ){
f6263bb… drh 711 ERR_print_errors_fp(stderr);
f6263bb… drh 712 fossil_fatal("Error initializing the SSL server");
f6263bb… drh 713 }
d7008b3… drh 714 if( fossil_strcmp(zCertFile,"unsafe-builtin")==0 ){
d7008b3… drh 715 if( sslctx_use_cert_from_mem(sslCtx, sslSelfCert, -1)
d7008b3… drh 716 || sslctx_use_pkey_from_mem(sslCtx, sslSelfPKey, -1)
d7008b3… drh 717 ){
d7008b3… drh 718 fossil_fatal("Error loading self-signed CERT and KEY");
d7008b3… drh 719 }
d7008b3… drh 720 }else{
82c62e5… stephan 721 if( SSL_CTX_use_certificate_chain_file(sslCtx,zCertFile)!=1 ){
f6263bb… drh 722 ERR_print_errors_fp(stderr);
f6263bb… drh 723 fossil_fatal("Error loading CERT file \"%s\"", zCertFile);
f6263bb… drh 724 }
f6263bb… drh 725 if( zKeyFile==0 ) zKeyFile = zCertFile;
f6263bb… drh 726 if( SSL_CTX_use_PrivateKey_file(sslCtx, zKeyFile, SSL_FILETYPE_PEM)<=0 ){
f6263bb… drh 727 ERR_print_errors_fp(stderr);
d7008b3… drh 728 if( strcmp(zKeyFile,zCertFile)==0 ){
d7008b3… drh 729 fossil_fatal("The private key is not found in \"%s\". "
d7008b3… drh 730 "Either append the private key to the certification in that "
d7008b3… drh 731 "file or use a separate --pkey option to specify the private key.",
d7008b3… drh 732 zKeyFile);
d7008b3… drh 733 }else{
d7008b3… drh 734 fossil_fatal("Error loading the private key from file \"%s\"",
d7008b3… drh 735 zKeyFile);
d7008b3… drh 736 }
d7008b3… drh 737 }
f6263bb… drh 738 }
f6263bb… drh 739 if( !SSL_CTX_check_private_key(sslCtx) ){
f6263bb… drh 740 fossil_fatal("PRIVATE KEY \"%s\" does not match CERT \"%s\"",
f6263bb… drh 741 zKeyFile, zCertFile);
f6263bb… drh 742 }
7a3bf55… drh 743 SSL_CTX_set_mode(sslCtx, SSL_MODE_AUTO_RETRY);
f6263bb… drh 744 sslIsInit = 2;
f6263bb… drh 745 }else{
f6263bb… drh 746 assert( sslIsInit==2 );
f6263bb… drh 747 }
f6263bb… drh 748 }
f6263bb… drh 749
f6263bb… drh 750 typedef struct SslServerConn {
f6263bb… drh 751 SSL *ssl; /* The SSL codec */
7a3bf55… drh 752 int iSocket; /* The socket */
8d456a5… stephan 753 BIO *bio; /* BIO object. Needed for EOF detection. */
f6263bb… drh 754 } SslServerConn;
f6263bb… drh 755
f6263bb… drh 756 /*
2584796… stephan 757 ** Create a new server-side codec. The argument is the socket's file
2584796… stephan 758 ** descriptor from which the codec reads and writes. The returned
2584796… stephan 759 ** memory must eventually be passed to ssl_close_server().
f6263bb… drh 760 */
7a3bf55… drh 761 void *ssl_new_server(int iSocket){
f6263bb… drh 762 SslServerConn *pServer = fossil_malloc_zero(sizeof(*pServer));
7a3bf55… drh 763 BIO *b = BIO_new_socket(iSocket, 0);
f6263bb… drh 764 pServer->ssl = SSL_new(sslCtx);
7a3bf55… drh 765 pServer->iSocket = iSocket;
8d456a5… stephan 766 pServer->bio = b;
7a3bf55… drh 767 SSL_set_bio(pServer->ssl, b, b);
f6263bb… drh 768 SSL_accept(pServer->ssl);
f6263bb… drh 769 return (void*)pServer;
f6263bb… drh 770 }
f6263bb… drh 771
f6263bb… drh 772 /*
f6263bb… drh 773 ** Close a server-side code previously returned from ssl_new_server().
f6263bb… drh 774 */
f6263bb… drh 775 void ssl_close_server(void *pServerArg){
f6263bb… drh 776 SslServerConn *pServer = (SslServerConn*)pServerArg;
f6263bb… drh 777 SSL_free(pServer->ssl);
f6263bb… drh 778 fossil_free(pServer);
f6263bb… drh 779 }
f6263bb… drh 780
f6263bb… drh 781 /*
f6263bb… drh 782 ** Return TRUE if there are no more bytes available to be read from
f6263bb… drh 783 ** the client.
f6263bb… drh 784 */
f6263bb… drh 785 int ssl_eof(void *pServerArg){
f6263bb… drh 786 SslServerConn *pServer = (SslServerConn*)pServerArg;
06e300e… stephan 787 return BIO_eof(pServer->bio);
f6263bb… drh 788 }
f6263bb… drh 789
f6263bb… drh 790 /*
f6263bb… drh 791 ** Read cleartext bytes that have been received from the client and
f6263bb… drh 792 ** decrypted by the SSL server codec.
b0834be… stephan 793 **
b0834be… stephan 794 ** If the expected payload size unknown, i.e. if the HTTP
b0834be… stephan 795 ** Content-Length: header field has not been parsed, the doLoop
b0834be… stephan 796 ** argument should be 0, or SSL_read() may block and wait for more
b0834be… stephan 797 ** data than is eventually going to arrive (on Windows). On
b0834be… stephan 798 ** non-Windows builds, it has been our experience that the final
b0834be… stephan 799 ** argument must always be true, as discussed at length at:
b0834be… stephan 800 **
b0834be… stephan 801 ** https://fossil-scm.org/forum/forumpost/2f818850abb72719
f6263bb… drh 802 */
b0834be… stephan 803 size_t ssl_read_server(void *pServerArg, char *zBuf, size_t nBuf, int doLoop){
b890451… stephan 804 int n;
8d456a5… stephan 805 size_t rc = 0;
f6263bb… drh 806 SslServerConn *pServer = (SslServerConn*)pServerArg;
f6263bb… drh 807 if( nBuf>0x7fffffff ){ fossil_fatal("SSL read too big"); }
b0834be… stephan 808 while( nBuf!=rc && BIO_eof(pServer->bio)==0 ){
8d456a5… stephan 809 n = SSL_read(pServer->ssl, zBuf + rc, (int)(nBuf - rc));
b0834be… stephan 810 if( n>0 ){
b70557f… stephan 811 rc += n;
acffc8f… stephan 812 }
b0834be… stephan 813 if( doLoop==0 || n<=0 ){
b0834be… stephan 814 break;
b0834be… stephan 815 }
8d456a5… stephan 816 }
8d456a5… stephan 817 return rc;
f6263bb… drh 818 }
f6263bb… drh 819
f6263bb… drh 820 /*
b0834be… stephan 821 ** Read a single line of text from the client, up to nBuf-1 bytes. On
b0834be… stephan 822 ** success, writes nBuf-1 bytes to zBuf and NUL-terminates zBuf.
b0834be… stephan 823 ** Returns NULL on an I/O error or at EOF.
f6263bb… drh 824 */
f6263bb… drh 825 char *ssl_gets(void *pServerArg, char *zBuf, int nBuf){
f6263bb… drh 826 int n = 0;
f6263bb… drh 827 int i;
f6263bb… drh 828 SslServerConn *pServer = (SslServerConn*)pServerArg;
06e300e… stephan 829
06e300e… stephan 830 if( BIO_eof(pServer->bio) ) return 0;
f6263bb… drh 831 for(i=0; i<nBuf-1; i++){
f6263bb… drh 832 n = SSL_read(pServer->ssl, &zBuf[i], 1);
f6263bb… drh 833 if( n<=0 ){
f6263bb… drh 834 return 0;
f6263bb… drh 835 }
f6263bb… drh 836 if( zBuf[i]=='\n' ) break;
f6263bb… drh 837 }
f6263bb… drh 838 zBuf[i+1] = 0;
f6263bb… drh 839 return zBuf;
f6263bb… drh 840 }
f6263bb… drh 841
f6263bb… drh 842
f6263bb… drh 843 /*
f6263bb… drh 844 ** Write cleartext bytes into the SSL server codec so that they can
f6263bb… drh 845 ** be encrypted and sent back to the client.
f6263bb… drh 846 */
f6263bb… drh 847 size_t ssl_write_server(void *pServerArg, char *zBuf, size_t nBuf){
f6263bb… drh 848 int n;
f6263bb… drh 849 SslServerConn *pServer = (SslServerConn*)pServerArg;
7a3bf55… drh 850 if( nBuf<=0 ) return 0;
f6263bb… drh 851 if( nBuf>0x7fffffff ){ fossil_fatal("SSL write too big"); }
f6263bb… drh 852 n = SSL_write(pServer->ssl, zBuf, (int)nBuf);
7a3bf55… drh 853 if( n<=0 ){
7a3bf55… drh 854 return -SSL_get_error(pServer->ssl, n);
7a3bf55… drh 855 }else{
7a3bf55… drh 856 return n;
7a3bf55… drh 857 }
f6263bb… drh 858 }
f6263bb… drh 859
7a3bf55… drh 860 #endif /* FOSSIL_ENABLE_SSL */
7a3bf55… drh 861
2a563d6… drh 862 #ifdef FOSSIL_ENABLE_SSL
0abeed1… drh 863 /*
0abeed1… drh 864 ** zPath is a name that might be a file or directory containing a trust
0abeed1… drh 865 ** store. *pzStore is the name of the trust store to actually use.
0abeed1… drh 866 **
0abeed1… drh 867 ** If *pzStore is not NULL (meaning no trust store has been found yet)
0abeed1… drh 868 ** and if zPath exists, then set *pzStore to point to zPath.
0abeed1… drh 869 */
0abeed1… drh 870 static void trust_location_usable(const char *zPath, const char **pzStore){
0abeed1… drh 871 if( *pzStore!=0 ) return;
0abeed1… drh 872 if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath;
0abeed1… drh 873 }
2a563d6… drh 874 #endif /* FOSSIL_ENABLE_SSL */
0abeed1… drh 875
bc23620… drh 876 /*
3e7c7e2… drh 877 ** COMMAND: tls-config* abbrv-subcom
3e7c7e2… drh 878 ** COMMAND: ssl-config abbrv-subcom
bc23620… drh 879 **
f6263bb… drh 880 ** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
bc23620… drh 881 **
bc23620… drh 882 ** This command is used to view or modify the TLS (Transport Layer
bc23620… drh 883 ** Security) configuration for Fossil. TLS (formerly SSL) is the
bc23620… drh 884 ** encryption technology used for secure HTTPS transport.
bc23620… drh 885 **
bc23620… drh 886 ** Sub-commands:
bc23620… drh 887 **
62cb8ea… drh 888 ** remove-exception DOMAINS Remove TLS cert exceptions for the domains
62cb8ea… drh 889 ** listed. Or remove them all if the --all
62cb8ea… drh 890 ** option is specified.
62cb8ea… drh 891 **
62cb8ea… drh 892 ** scrub ?--force? Remove all SSL configuration data from the
62cb8ea… drh 893 ** repository. Use --force to omit the
62cb8ea… drh 894 ** confirmation.
62cb8ea… drh 895 **
62cb8ea… drh 896 ** show ?-v? Show the TLS configuration. Add -v to see
62cb8ea… drh 897 ** additional explanation
bc23620… drh 898 */
bc23620… drh 899 void test_tlsconfig_info(void){
bc23620… drh 900 const char *zCmd;
bc23620… drh 901 size_t nCmd;
bc23620… drh 902 int nHit = 0;
0abeed1… drh 903
bc23620… drh 904 db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
bc23620… drh 905 db_open_config(1,0);
f6263bb… drh 906 if( g.argc==2 || (g.argc>=3 && g.argv[2][0]=='-') ){
f6263bb… drh 907 zCmd = "show";
f6263bb… drh 908 nCmd = 4;
f6263bb… drh 909 }else{
f6263bb… drh 910 zCmd = g.argv[2];
f6263bb… drh 911 nCmd = strlen(zCmd);
f6263bb… drh 912 }
f6263bb… drh 913 if( strncmp("scrub",zCmd,nCmd)==0 && nCmd>4 ){
f6263bb… drh 914 int bForce = find_option("force","f",0)!=0;
f6263bb… drh 915 verify_all_options();
f6263bb… drh 916 if( !bForce ){
f6263bb… drh 917 Blob ans;
f6263bb… drh 918 char cReply;
f6263bb… drh 919 prompt_user(
f6263bb… drh 920 "Scrubbing the SSL configuration will permanently delete information.\n"
f6263bb… drh 921 "Changes cannot be undone. Continue (y/N)? ", &ans);
f6263bb… drh 922 cReply = blob_str(&ans)[0];
f6263bb… drh 923 if( cReply!='y' && cReply!='Y' ){
f6263bb… drh 924 fossil_exit(1);
f6263bb… drh 925 }
f6263bb… drh 926 }
f6263bb… drh 927 db_unprotect(PROTECT_ALL);
f6263bb… drh 928 db_multi_exec(
f6263bb… drh 929 "PRAGMA secure_delete=ON;"
f6263bb… drh 930 "DELETE FROM config WHERE name GLOB 'ssl-*';"
f6263bb… drh 931 );
f6263bb… drh 932 db_protect_pop();
f6263bb… drh 933 }else
bc23620… drh 934 if( strncmp("show",zCmd,nCmd)==0 ){
c1350c8… drh 935 #if defined(FOSSIL_ENABLE_SSL)
bc23620… drh 936 const char *zName, *zValue;
0abeed1… drh 937 const char *zUsed = 0; /* Trust store location actually used */
bc23620… drh 938 size_t nName;
317c665… drh 939 #endif
bc23620… drh 940 Stmt q;
f6263bb… drh 941 int verbose = find_option("verbose","v",0)!=0;
f6263bb… drh 942 verify_all_options();
f6263bb… drh 943
f6263bb… drh 944 #if !defined(FOSSIL_ENABLE_SSL)
0abeed1… drh 945 fossil_print("OpenSSL-version: (none)\n");
f6263bb… drh 946 if( verbose ){
f6263bb… drh 947 fossil_print("\n"
f6263bb… drh 948 " The OpenSSL library is not used by this build of Fossil\n\n"
f6263bb… drh 949 );
f6263bb… drh 950 }
f6263bb… drh 951 #else
2b6ad00… drh 952 fossil_print("OpenSSL-version: %s (0x%09llx)\n",
2b6ad00… drh 953 SSLeay_version(SSLEAY_VERSION), (unsigned long long)SSLeay());
f6263bb… drh 954 if( verbose ){
f6263bb… drh 955 fossil_print("\n"
f6263bb… drh 956 " The version of the OpenSSL library being used\n"
f6263bb… drh 957 " by this instance of Fossil. Version 3.0.0 or\n"
f6263bb… drh 958 " later is recommended.\n\n"
f6263bb… drh 959 );
f6263bb… drh 960 }
f6263bb… drh 961
0abeed1… drh 962 fossil_print("Trust store location\n");
bc23620… drh 963 zName = X509_get_default_cert_file_env();
bc23620… drh 964 zValue = fossil_getenv(zName);
bc23620… drh 965 if( zValue==0 ) zValue = "";
0abeed1… drh 966 trust_location_usable(zValue, &zUsed);
bc23620… drh 967 nName = strlen(zName);
0abeed1… drh 968 fossil_print(" %s:%*s%s\n", zName, 19-nName, "", zValue);
bc23620… drh 969 zName = X509_get_default_cert_dir_env();
bc23620… drh 970 zValue = fossil_getenv(zName);
bc23620… drh 971 if( zValue==0 ) zValue = "";
0abeed1… drh 972 trust_location_usable(zValue, &zUsed);
0abeed1… drh 973 nName = strlen(zName);
0abeed1… drh 974 fossil_print(" %s:%*s%s\n", zName, 19-nName, "", zValue);
0abeed1… drh 975 if( verbose ){
0abeed1… drh 976 fossil_print("\n"
0abeed1… drh 977 " Environment variables that determine alternative locations for\n"
0abeed1… drh 978 " the root certificates used by Fossil when it is acting as a SSL\n"
e225dc9… drh 979 " client. If specified, these alternative locations take top\n"
e225dc9… drh 980 " priority.\n\n"
e225dc9… drh 981 );
e225dc9… drh 982 }
e225dc9… drh 983
e225dc9… drh 984 zValue = db_get("ssl-ca-location","");
e225dc9… drh 985 trust_location_usable(zValue, &zUsed);
e225dc9… drh 986 fossil_print(" ssl-ca-location: %s\n", zValue);
e225dc9… drh 987 if( verbose ){
e225dc9… drh 988 fossil_print("\n"
e225dc9… drh 989 " This setting is the name of a file or directory that contains\n"
e225dc9… drh 990 " the complete set of root certificates used by Fossil when it\n"
e225dc9… drh 991 " is acting as a SSL client. If defined, this setting takes\n"
e225dc9… drh 992 " priority over built-in paths.\n\n"
0abeed1… drh 993 );
0abeed1… drh 994 }
e225dc9… drh 995
0abeed1… drh 996
0abeed1… drh 997 zValue = X509_get_default_cert_file();
0abeed1… drh 998 trust_location_usable(zValue, &zUsed);
0abeed1… drh 999 fossil_print(" OpenSSL-cert-file: %s\n", zValue);
0abeed1… drh 1000 zValue = X509_get_default_cert_dir();
0abeed1… drh 1001 trust_location_usable(zValue, &zUsed);
0abeed1… drh 1002 fossil_print(" OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir());
0abeed1… drh 1003 if( verbose ){
0abeed1… drh 1004 fossil_print("\n"
0abeed1… drh 1005 " The default locations for the set of root certificates\n"
0abeed1… drh 1006 " used by the \"fossil sync\" and similar commands to verify\n"
0abeed1… drh 1007 " the identity of servers for \"https:\" URLs. These values\n"
0abeed1… drh 1008 " come into play when Fossil is used as a TLS client. These\n"
0abeed1… drh 1009 " values are built into your OpenSSL library.\n\n"
0abeed1… drh 1010 );
0abeed1… drh 1011 }
6fc64ab… florian 1012
de6a059… florian 1013 #if defined(_WIN32)
2b6ad00… drh 1014 fossil_print(" OpenSSL-winstore: %s\n",
2b6ad00… drh 1015 (SSLeay()>=0x30200000 && SSLeay()!=0x30500000) ? "Yes" : "No");
6fc64ab… florian 1016 if( verbose ){
6fc64ab… florian 1017 fossil_print("\n"
2b6ad00… drh 1018 " OpenSSL 3.2.0, or newer, but not version 3.5.0 due to a bug,\n"
a9b075a… florian 1019 " are able to use the root certificates managed by the Windows\n"
a9b075a… florian 1020 " operating system. The installed root certificates are listed\n"
a9b075a… florian 1021 " by the command:\n\n"
de6a059… florian 1022 " certutil -store \"ROOT\"\n\n"
6fc64ab… florian 1023 );
6fc64ab… florian 1024 }
6fc64ab… florian 1025 #endif /* _WIN32 */
0abeed1… drh 1026
0abeed1… drh 1027 if( zUsed==0 ) zUsed = "";
0abeed1… drh 1028 fossil_print(" Trust store used: %s\n", zUsed);
0abeed1… drh 1029 if( verbose ){
0abeed1… drh 1030 fossil_print("\n"
0abeed1… drh 1031 " The location that is actually used for the root certificates\n"
0abeed1… drh 1032 " used to verify the identity of servers for \"https:\" URLs.\n"
0abeed1… drh 1033 " This will be one of the first of the five locations listed\n"
0abeed1… drh 1034 " above that actually exists.\n\n"
0abeed1… drh 1035 );
0abeed1… drh 1036 }
0abeed1… drh 1037
0abeed1… drh 1038
0abeed1… drh 1039 #endif /* FOSSIL_ENABLE_SSL */
0abeed1… drh 1040
0abeed1… drh 1041
0abeed1… drh 1042 fossil_print("ssl-identity: %s\n", db_get("ssl-identity",""));
d7008b3… drh 1043 if( verbose ){
d7008b3… drh 1044 fossil_print("\n"
d7008b3… drh 1045 " This setting is the name of a file that contains the PEM-format\n"
d7008b3… drh 1046 " certificate and private-key used by Fossil clients to authenticate\n"
d7008b3… drh 1047 " with servers. Few servers actually require this, so this setting\n"
d7008b3… drh 1048 " is usually blank.\n\n"
f6263bb… drh 1049 );
f6263bb… drh 1050 }
f6263bb… drh 1051
bc23620… drh 1052 db_prepare(&q,
0abeed1… drh 1053 "SELECT name, '', value FROM global_config"
bc23620… drh 1054 " WHERE name GLOB 'cert:*'"
bc23620… drh 1055 "UNION ALL "
0abeed1… drh 1056 "SELECT name, date(mtime,'unixepoch'), value FROM config"
bc23620… drh 1057 " WHERE name GLOB 'cert:*'"
bc23620… drh 1058 " ORDER BY name"
bc23620… drh 1059 );
f6263bb… drh 1060 nHit = 0;
bc23620… drh 1061 while( db_step(&q)==SQLITE_ROW ){
0abeed1… drh 1062 /* 123456789 123456789 123456789 */
0abeed1… drh 1063 if( verbose ){
0abeed1… drh 1064 fossil_print("exception: %-40s %s\n"
0abeed1… drh 1065 " hash: %.57s\n",
0abeed1… drh 1066 db_column_text(&q,0)+5, db_column_text(&q,1),
0abeed1… drh 1067 db_column_text(&q,2));
0abeed1… drh 1068 }else{
0abeed1… drh 1069 fossil_print("exception: %-40s %s\n",
0abeed1… drh 1070 db_column_text(&q,0)+5, db_column_text(&q,1));
0abeed1… drh 1071 }
f6263bb… drh 1072 nHit++;
bc23620… drh 1073 }
bc23620… drh 1074 db_finalize(&q);
f6263bb… drh 1075 if( nHit && verbose ){
f6263bb… drh 1076 fossil_print("\n"
f6263bb… drh 1077 " The exceptions are server certificates that the Fossil client\n"
f6263bb… drh 1078 " is unable to verify using root certificates, but which should be\n"
f6263bb… drh 1079 " accepted anyhow.\n\n"
f6263bb… drh 1080 );
f6263bb… drh 1081 }
f6263bb… drh 1082
bc23620… drh 1083 }else
bc23620… drh 1084 if( strncmp("remove-exception",zCmd,nCmd)==0 ){
bc23620… drh 1085 int i;
bc23620… drh 1086 Blob sql;
bc23620… drh 1087 char *zSep = "(";
bc23620… drh 1088 db_begin_transaction();
bc23620… drh 1089 blob_init(&sql, 0, 0);
bc23620… drh 1090 if( g.argc==4 && find_option("all",0,0)!=0 ){
bc23620… drh 1091 blob_append_sql(&sql,
bc23620… drh 1092 "DELETE FROM global_config WHERE name GLOB 'cert:*';\n"
bc23620… drh 1093 "DELETE FROM global_config WHERE name GLOB 'trusted:*';\n"
bc23620… drh 1094 "DELETE FROM config WHERE name GLOB 'cert:*';\n"
bc23620… drh 1095 "DELETE FROM config WHERE name GLOB 'trusted:*';\n"
bc23620… drh 1096 );
bc23620… drh 1097 }else{
bc23620… drh 1098 if( g.argc<4 ){
bc23620… drh 1099 usage("remove-exception DOMAIN-NAME ...");
bc23620… drh 1100 }
bc23620… drh 1101 blob_append_sql(&sql,"DELETE FROM global_config WHERE name IN ");
bc23620… drh 1102 for(i=3; i<g.argc; i++){
bc23620… drh 1103 blob_append_sql(&sql,"%s'cert:%q','trust:%q'",
bc23620… drh 1104 zSep/*safe-for-%s*/, g.argv[i], g.argv[i]);
bc23620… drh 1105 zSep = ",";
bc23620… drh 1106 }
bc23620… drh 1107 blob_append_sql(&sql,");\n");
bc23620… drh 1108 zSep = "(";
bc23620… drh 1109 blob_append_sql(&sql,"DELETE FROM config WHERE name IN ");
bc23620… drh 1110 for(i=3; i<g.argc; i++){
bc23620… drh 1111 blob_append_sql(&sql,"%s'cert:%q','trusted:%q'",
bc23620… drh 1112 zSep/*safe-for-%s*/, g.argv[i], g.argv[i]);
bc23620… drh 1113 zSep = ",";
bc23620… drh 1114 }
bc23620… drh 1115 blob_append_sql(&sql,");");
bc23620… drh 1116 }
156c890… drh 1117 db_unprotect(PROTECT_CONFIG);
bc23620… drh 1118 db_exec_sql(blob_str(&sql));
156c890… drh 1119 db_protect_pop();
bc23620… drh 1120 db_commit_transaction();
bc23620… drh 1121 blob_reset(&sql);
bc23620… drh 1122 }else
bc23620… drh 1123 /*default*/{
bc23620… drh 1124 fossil_fatal("unknown sub-command \"%s\".\nshould be one of:"
d7008b3… drh 1125 " remove-exception scrub show",
bc23620… drh 1126 zCmd);
bc23620… drh 1127 }
f6263bb… drh 1128 }
f6263bb… drh 1129
f6263bb… drh 1130 /*
f6263bb… drh 1131 ** WEBPAGE: .well-known
f6263bb… drh 1132 **
f6263bb… drh 1133 ** If the "--acme" option was supplied to "fossil server" or "fossil http" or
f6263bb… drh 1134 ** similar, then this page returns the content of files found in the
f6263bb… drh 1135 ** ".well-known" subdirectory of the same directory that contains the
f6263bb… drh 1136 ** repository file. This facilitates Automated Certificate
f6263bb… drh 1137 ** Management using tools like "certbot".
f6263bb… drh 1138 **
f6263bb… drh 1139 ** The content is returned directly, without any interpretation, using
f6263bb… drh 1140 ** a generic mimetype.
f6263bb… drh 1141 */
f6263bb… drh 1142 void wellknown_page(void){
f6263bb… drh 1143 char *zPath = 0;
f6263bb… drh 1144 const char *zTail = P("name");
f6263bb… drh 1145 Blob content;
f6263bb… drh 1146 int i;
f6263bb… drh 1147 char c;
f6263bb… drh 1148 if( !g.fAllowACME ) goto wellknown_notfound;
f6263bb… drh 1149 if( g.zRepositoryName==0 ) goto wellknown_notfound;
f6263bb… drh 1150 if( zTail==0 ) goto wellknown_notfound;
f6263bb… drh 1151 zPath = mprintf("%z/.well-known/%s", file_dirname(g.zRepositoryName), zTail);
f6263bb… drh 1152 for(i=0; (c = zTail[i])!=0; i++){
f6263bb… drh 1153 if( fossil_isalnum(c) ) continue;
f6263bb… drh 1154 if( c=='.' ){
f6263bb… drh 1155 if( i==0 || zTail[i-1]=='/' || zTail[i-1]=='.' ) goto wellknown_notfound;
f6263bb… drh 1156 continue;
f6263bb… drh 1157 }
f6263bb… drh 1158 if( c==',' || c!='-' || c=='/' || c==':' || c=='_' || c=='~' ) continue;
f6263bb… drh 1159 goto wellknown_notfound;
f6263bb… drh 1160 }
f6263bb… drh 1161 if( strstr("/..", zPath)!=0 ) goto wellknown_notfound;
f6263bb… drh 1162 if( !file_isfile(zPath, ExtFILE) ) goto wellknown_notfound;
f6263bb… drh 1163 blob_read_from_file(&content, zPath, ExtFILE);
f6263bb… drh 1164 cgi_set_content(&content);
f6263bb… drh 1165 cgi_set_content_type(mimetype_from_name(zPath));
f6263bb… drh 1166 cgi_reply();
f6263bb… drh 1167 return;
f6263bb… drh 1168
f6263bb… drh 1169 wellknown_notfound:
f6263bb… drh 1170 fossil_free(zPath);
2afff83… drh 1171 webpage_notfound_error(0 /*works-like:""*/);
f6263bb… drh 1172 return;
bb09ff8… drh 1173 }
bb09ff8… drh 1174
bb09ff8… drh 1175 /*
bb09ff8… drh 1176 ** Return the OpenSSL version number being used. Space to hold
bb09ff8… drh 1177 ** this name is obtained from fossil_malloc() and should be
bb09ff8… drh 1178 ** freed by the caller.
bb09ff8… drh 1179 */
bb09ff8… drh 1180 char *fossil_openssl_version(void){
275da70… danield 1181 #if defined(FOSSIL_ENABLE_SSL)
bb09ff8… drh 1182 return mprintf("%s (0x%09x)\n",
2b6ad00… drh 1183 SSLeay_version(SSLEAY_VERSION), (sqlite3_uint64)SSLeay());
bb09ff8… drh 1184 #else
bb09ff8… drh 1185 return mprintf("none");
bb09ff8… drh 1186 #endif
67147dd… drh 1187 }

Keyboard Shortcuts

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