Fossil SCM

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

Keyboard Shortcuts

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