Fossil SCM

Add initial support for SSL (TLS) servers on unix using "fossil server" or "fossil http". Rename the "tls-config" command to "ssl-config". Extend that command to support specifying certificates. Add support for delivering content from the ".well-known" directory to support obtaining certs from Let's Encrypt.

drh 2021-12-28 19:04 trunk merge
Commit f6263bb64195b07fab7374ed253bbd66a7de5175718195cd9d46afbbf2bd2e10
+24
--- src/blob.c
+++ src/blob.c
@@ -967,10 +967,34 @@
967967
}
968968
}else{
969969
blob_resize(pBlob, nToRead);
970970
n = fread(blob_buffer(pBlob), 1, nToRead, in);
971971
blob_resize(pBlob, n);
972
+ }
973
+ return blob_size(pBlob);
974
+}
975
+
976
+/*
977
+** Initialize a blob to the data read from HTTP input. Return
978
+** the number of bytes read into the blob. Any prior content
979
+** of the blob is discarded, not freed.
980
+*/
981
+int blob_read_from_cgi(Blob *pBlob, int nToRead){
982
+ size_t n;
983
+ blob_zero(pBlob);
984
+ if( nToRead<0 ){
985
+ char zBuf[10000];
986
+ while( !cgi_feof() ){
987
+ n = cgi_fread(zBuf, sizeof(zBuf));
988
+ if( n>0 ){
989
+ blob_append(pBlob, zBuf, n);
990
+ }
991
+ }
992
+ }else{
993
+ blob_resize(pBlob, nToRead);
994
+ n = cgi_fread(blob_buffer(pBlob), nToRead);
995
+ blob_resize(pBlob, n);
972996
}
973997
return blob_size(pBlob);
974998
}
975999
9761000
/*
9771001
--- src/blob.c
+++ src/blob.c
@@ -967,10 +967,34 @@
967 }
968 }else{
969 blob_resize(pBlob, nToRead);
970 n = fread(blob_buffer(pBlob), 1, nToRead, in);
971 blob_resize(pBlob, n);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
972 }
973 return blob_size(pBlob);
974 }
975
976 /*
977
--- src/blob.c
+++ src/blob.c
@@ -967,10 +967,34 @@
967 }
968 }else{
969 blob_resize(pBlob, nToRead);
970 n = fread(blob_buffer(pBlob), 1, nToRead, in);
971 blob_resize(pBlob, n);
972 }
973 return blob_size(pBlob);
974 }
975
976 /*
977 ** Initialize a blob to the data read from HTTP input. Return
978 ** the number of bytes read into the blob. Any prior content
979 ** of the blob is discarded, not freed.
980 */
981 int blob_read_from_cgi(Blob *pBlob, int nToRead){
982 size_t n;
983 blob_zero(pBlob);
984 if( nToRead<0 ){
985 char zBuf[10000];
986 while( !cgi_feof() ){
987 n = cgi_fread(zBuf, sizeof(zBuf));
988 if( n>0 ){
989 blob_append(pBlob, zBuf, n);
990 }
991 }
992 }else{
993 blob_resize(pBlob, nToRead);
994 n = cgi_fread(blob_buffer(pBlob), nToRead);
995 blob_resize(pBlob, n);
996 }
997 return blob_size(pBlob);
998 }
999
1000 /*
1001
+107 -10
--- src/cgi.c
+++ src/cgi.c
@@ -84,10 +84,11 @@
8484
#endif
8585
#include <time.h>
8686
#include <stdio.h>
8787
#include <stdlib.h>
8888
#include <unistd.h>
89
+#include <assert.h>
8990
#include "cgi.h"
9091
#include "cygsup.h"
9192
9293
#if INTERFACE
9394
/*
@@ -333,10 +334,95 @@
333334
if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0;
334335
return strncmp(zContentType, "text/", 5)==0
335336
|| sqlite3_strglob("application/*xml", zContentType)==0
336337
|| sqlite3_strglob("application/*javascript", zContentType)==0;
337338
}
339
+
340
+
341
+/*
342
+** The following routines read or write content from/to the wire for
343
+** an HTTP request. Depending on settings the content might be coming
344
+** from or going to a socket, or a file, or it might come from or go
345
+** to an SSL decoder/encoder.
346
+*/
347
+/*
348
+** Works like fgets():
349
+**
350
+** Read a single line of input into s[]. Ensure that s[] is zero-terminated.
351
+** The s[] buffer is size bytes and so at most size-1 bytes will be read.
352
+**
353
+** Return a pointer to s[] on success, or NULL at end-of-input.
354
+*/
355
+static char *cgi_fgets(char *s, int size){
356
+ if( !g.httpUseSSL ){
357
+ return fgets(s, size, g.httpIn);
358
+ }
359
+#ifdef FOSSIL_ENABLE_SSL
360
+ return ssl_gets(g.httpSSLConn, s, size);
361
+#else
362
+ fossil_fatal("SSL not available");
363
+#endif
364
+}
365
+
366
+/* Works like fread():
367
+**
368
+** Read as many as bytes of content as we can, up to a maximum of nmemb
369
+** bytes. Return the number of bytes read. Return -1 if there is no
370
+** further input or if an I/O error occurs.
371
+*/
372
+size_t cgi_fread(void *ptr, size_t nmemb){
373
+ if( !g.httpUseSSL ){
374
+ return fread(ptr, 1, nmemb, g.httpIn);
375
+ }
376
+#ifdef FOSSIL_ENABLE_SSL
377
+ return ssl_read_server(g.httpSSLConn, ptr, nmemb);
378
+#else
379
+ fossil_fatal("SSL not available");
380
+#endif
381
+}
382
+
383
+/* Works like feof():
384
+**
385
+** Return true if end-of-input has been reached.
386
+*/
387
+int cgi_feof(void){
388
+ if( !g.httpUseSSL ){
389
+ return feof(g.httpIn);
390
+ }
391
+#ifdef FOSSIL_ENABLE_SSL
392
+ return ssl_eof(g.httpSSLConn);
393
+#else
394
+ return 1;
395
+#endif
396
+}
397
+
398
+/* Works like fwrite():
399
+**
400
+** Try to output nmemb bytes of content. Return the number of
401
+** bytes actually written.
402
+*/
403
+static size_t cgi_fwrite(void *ptr, size_t nmemb){
404
+ if( !g.httpUseSSL ){
405
+ return fwrite(ptr, 1, nmemb, g.httpOut);
406
+ }
407
+#ifdef FOSSIL_ENABLE_SSL
408
+ return ssl_write_server(g.httpSSLConn, ptr, nmemb);
409
+#else
410
+ fossil_fatal("SSL not available");
411
+#endif
412
+}
413
+
414
+/* Works like fflush():
415
+**
416
+** Make sure I/O has completed.
417
+*/
418
+static void cgi_fflush(void){
419
+ if( !g.httpUseSSL ){
420
+ fflush(g.httpOut);
421
+ }
422
+}
423
+
338424
339425
/*
340426
** Generate the reply to a web request. The output might be an
341427
** full HTTP response, or a CGI response, depending on how things have
342428
** be set up.
@@ -436,11 +522,11 @@
436522
blob_appendf(&hdr, "Content-Length: %d\r\n", total_size);
437523
}else{
438524
total_size = 0;
439525
}
440526
blob_appendf(&hdr, "\r\n");
441
- fwrite(blob_buffer(&hdr), 1, blob_size(&hdr), g.httpOut);
527
+ cgi_fwrite(blob_buffer(&hdr), blob_size(&hdr));
442528
blob_reset(&hdr);
443529
if( total_size>0
444530
&& iReplyStatus!=304
445531
&& fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0
446532
){
@@ -452,17 +538,17 @@
452538
}else{
453539
int n = size - rangeStart;
454540
if( n>total_size ){
455541
n = total_size;
456542
}
457
- fwrite(blob_buffer(&cgiContent[i])+rangeStart, 1, n, g.httpOut);
543
+ cgi_fwrite(blob_buffer(&cgiContent[i])+rangeStart, n);
458544
rangeStart = 0;
459545
total_size -= n;
460546
}
461547
}
462548
}
463
- fflush(g.httpOut);
549
+ cgi_fflush();
464550
CGIDEBUG(("-------- END cgi ---------\n"));
465551
466552
/* After the webpage has been sent, do any useful background
467553
** processing.
468554
*/
@@ -1262,18 +1348,19 @@
12621348
g.zContentType = zType;
12631349
}
12641350
blob_zero(&g.cgiIn);
12651351
if( len>0 && zType ){
12661352
if( fossil_strcmp(zType, "application/x-fossil")==0 ){
1267
- if( blob_read_from_channel(&g.cgiIn, g.httpIn, len)!=len ){
1353
+ if( blob_read_from_cgi(&g.cgiIn, len)!=len ){
12681354
malformed_request("CGI content-length mismatch");
12691355
}
12701356
blob_uncompress(&g.cgiIn, &g.cgiIn);
12711357
}
12721358
#ifdef FOSSIL_ENABLE_JSON
1273
- else if( noJson==0 && g.json.isJsonMode!=0
1359
+ else if( noJson==0 && g.json.isJsonMode!=0
12741360
&& json_can_consume_content_type(zType)!=0 ){
1361
+ assert( !g.httpUseSSL );
12751362
cgi_parse_POST_JSON(g.httpIn, (unsigned int)len);
12761363
/*
12771364
Potential TODOs:
12781365
12791366
1) If parsing fails, immediately return an error response
@@ -1281,11 +1368,11 @@
12811368
*/
12821369
cgi_set_content_type(json_guess_content_type());
12831370
}
12841371
#endif /* FOSSIL_ENABLE_JSON */
12851372
else{
1286
- blob_read_from_channel(&g.cgiIn, g.httpIn, len);
1373
+ blob_read_from_cgi(&g.cgiIn, len);
12871374
}
12881375
}
12891376
}
12901377
12911378
/*
@@ -1799,11 +1886,11 @@
17991886
char *z, *zToken;
18001887
int i;
18011888
const char *zScheme = "http";
18021889
char zLine[2000]; /* A single line of input. */
18031890
g.fullHttpReply = 1;
1804
- if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1891
+ if( cgi_fgets(zLine, sizeof(zLine))==0 ){
18051892
malformed_request("missing HTTP header");
18061893
}
18071894
blob_append(&g.httpHeader, zLine, -1);
18081895
cgi_trace(zLine);
18091896
zToken = extract_token(zLine, &z);
@@ -1837,11 +1924,11 @@
18371924
}
18381925
18391926
18401927
/* Get all the optional fields that follow the first line.
18411928
*/
1842
- while( fgets(zLine,sizeof(zLine),g.httpIn) ){
1929
+ while( cgi_fgets(zLine,sizeof(zLine)) ){
18431930
char *zFieldName;
18441931
char *zVal;
18451932
18461933
cgi_trace(zLine);
18471934
blob_append(&g.httpHeader, zLine, -1);
@@ -1916,10 +2003,11 @@
19162003
char *z, *zToken;
19172004
const char *zType = 0;
19182005
int i, content_length = 0;
19192006
char zLine[2000]; /* A single line of input. */
19202007
2008
+ assert( !g.httpUseSSL );
19212009
#ifdef FOSSIL_ENABLE_JSON
19222010
if( nCycles==0 ){ json_bootstrap_early(); }
19232011
#endif
19242012
if( zIpAddr ){
19252013
if( nCycles==0 ){
@@ -2057,10 +2145,11 @@
20572145
/*
20582146
** This routine handles the old fossil SSH probes
20592147
*/
20602148
char *cgi_handle_ssh_probes(char *zLine, int zSize, char *z, char *zToken){
20612149
/* Start looking for probes */
2150
+ assert( !g.httpUseSSL );
20622151
while( fossil_strcmp(zToken, "echo")==0 ){
20632152
zToken = extract_token(z, &z);
20642153
if( zToken==0 ){
20652154
malformed_request("malformed probe");
20662155
}
@@ -2094,10 +2183,11 @@
20942183
*/
20952184
void cgi_handle_ssh_transport(const char *zCmd){
20962185
char *z, *zToken;
20972186
char zLine[2000]; /* A single line of input. */
20982187
2188
+ assert( !g.httpUseSSL );
20992189
/* look for second newline of transport_flip */
21002190
if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
21012191
malformed_request("incorrect transport_flip");
21022192
}
21032193
cgi_trace(zLine);
@@ -2143,10 +2233,11 @@
21432233
char *zToFree;
21442234
int nHdr = 0;
21452235
int nRead;
21462236
int c, n, m;
21472237
2238
+ assert( !g.httpUseSSL );
21482239
while( (c = fgetc(g.httpIn))!=EOF && fossil_isdigit((char)c) ){
21492240
nHdr = nHdr*10 + (char)c - '0';
21502241
}
21512242
if( nHdr<16 ) malformed_request("SCGI header too short");
21522243
zToFree = zHdr = fossil_malloc(nHdr);
@@ -2174,10 +2265,11 @@
21742265
#define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */
21752266
#define HTTP_SERVER_SCGI 0x0002 /* SCGI instead of HTTP */
21762267
#define HTTP_SERVER_HAD_REPOSITORY 0x0004 /* Was the repository open? */
21772268
#define HTTP_SERVER_HAD_CHECKOUT 0x0008 /* Was a checkout open? */
21782269
#define HTTP_SERVER_REPOLIST 0x0010 /* Allow repo listing */
2270
+#define HTTP_SERVER_NOFORK 0x0020 /* Do not call fork() */
21792271
21802272
#endif /* INTERFACE */
21812273
21822274
/*
21832275
** Maximum number of child processes that we can have running
@@ -2258,11 +2350,12 @@
22582350
}
22592351
}
22602352
if( iPort>mxPort ) return 1;
22612353
listen(listener,10);
22622354
fossil_print("Listening for %s requests on TCP port %d\n",
2263
- (flags & HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort);
2355
+ (flags & HTTP_SERVER_SCGI)!=0 ? "SCGI" :
2356
+ g.httpUseSSL?"TLS-encrypted HTTPS":"HTTP", iPort);
22642357
fflush(stdout);
22652358
if( zBrowser ){
22662359
assert( strstr(zBrowser,"%d")!=0 );
22672360
zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
22682361
#if defined(__CYGWIN__)
@@ -2293,11 +2386,15 @@
22932386
select( listener+1, &readfds, 0, 0, &delay);
22942387
if( FD_ISSET(listener, &readfds) ){
22952388
lenaddr = sizeof(inaddr);
22962389
connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
22972390
if( connection>=0 ){
2298
- child = fork();
2391
+ if( flags & HTTP_SERVER_NOFORK ){
2392
+ child = 0;
2393
+ }else{
2394
+ child = fork();
2395
+ }
22992396
if( child!=0 ){
23002397
if( child>0 ){
23012398
nchildren++;
23022399
nRequest++;
23032400
}
23042401
--- src/cgi.c
+++ src/cgi.c
@@ -84,10 +84,11 @@
84 #endif
85 #include <time.h>
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <unistd.h>
 
89 #include "cgi.h"
90 #include "cygsup.h"
91
92 #if INTERFACE
93 /*
@@ -333,10 +334,95 @@
333 if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0;
334 return strncmp(zContentType, "text/", 5)==0
335 || sqlite3_strglob("application/*xml", zContentType)==0
336 || sqlite3_strglob("application/*javascript", zContentType)==0;
337 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
339 /*
340 ** Generate the reply to a web request. The output might be an
341 ** full HTTP response, or a CGI response, depending on how things have
342 ** be set up.
@@ -436,11 +522,11 @@
436 blob_appendf(&hdr, "Content-Length: %d\r\n", total_size);
437 }else{
438 total_size = 0;
439 }
440 blob_appendf(&hdr, "\r\n");
441 fwrite(blob_buffer(&hdr), 1, blob_size(&hdr), g.httpOut);
442 blob_reset(&hdr);
443 if( total_size>0
444 && iReplyStatus!=304
445 && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0
446 ){
@@ -452,17 +538,17 @@
452 }else{
453 int n = size - rangeStart;
454 if( n>total_size ){
455 n = total_size;
456 }
457 fwrite(blob_buffer(&cgiContent[i])+rangeStart, 1, n, g.httpOut);
458 rangeStart = 0;
459 total_size -= n;
460 }
461 }
462 }
463 fflush(g.httpOut);
464 CGIDEBUG(("-------- END cgi ---------\n"));
465
466 /* After the webpage has been sent, do any useful background
467 ** processing.
468 */
@@ -1262,18 +1348,19 @@
1262 g.zContentType = zType;
1263 }
1264 blob_zero(&g.cgiIn);
1265 if( len>0 && zType ){
1266 if( fossil_strcmp(zType, "application/x-fossil")==0 ){
1267 if( blob_read_from_channel(&g.cgiIn, g.httpIn, len)!=len ){
1268 malformed_request("CGI content-length mismatch");
1269 }
1270 blob_uncompress(&g.cgiIn, &g.cgiIn);
1271 }
1272 #ifdef FOSSIL_ENABLE_JSON
1273 else if( noJson==0 && g.json.isJsonMode!=0
1274 && json_can_consume_content_type(zType)!=0 ){
 
1275 cgi_parse_POST_JSON(g.httpIn, (unsigned int)len);
1276 /*
1277 Potential TODOs:
1278
1279 1) If parsing fails, immediately return an error response
@@ -1281,11 +1368,11 @@
1281 */
1282 cgi_set_content_type(json_guess_content_type());
1283 }
1284 #endif /* FOSSIL_ENABLE_JSON */
1285 else{
1286 blob_read_from_channel(&g.cgiIn, g.httpIn, len);
1287 }
1288 }
1289 }
1290
1291 /*
@@ -1799,11 +1886,11 @@
1799 char *z, *zToken;
1800 int i;
1801 const char *zScheme = "http";
1802 char zLine[2000]; /* A single line of input. */
1803 g.fullHttpReply = 1;
1804 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1805 malformed_request("missing HTTP header");
1806 }
1807 blob_append(&g.httpHeader, zLine, -1);
1808 cgi_trace(zLine);
1809 zToken = extract_token(zLine, &z);
@@ -1837,11 +1924,11 @@
1837 }
1838
1839
1840 /* Get all the optional fields that follow the first line.
1841 */
1842 while( fgets(zLine,sizeof(zLine),g.httpIn) ){
1843 char *zFieldName;
1844 char *zVal;
1845
1846 cgi_trace(zLine);
1847 blob_append(&g.httpHeader, zLine, -1);
@@ -1916,10 +2003,11 @@
1916 char *z, *zToken;
1917 const char *zType = 0;
1918 int i, content_length = 0;
1919 char zLine[2000]; /* A single line of input. */
1920
 
1921 #ifdef FOSSIL_ENABLE_JSON
1922 if( nCycles==0 ){ json_bootstrap_early(); }
1923 #endif
1924 if( zIpAddr ){
1925 if( nCycles==0 ){
@@ -2057,10 +2145,11 @@
2057 /*
2058 ** This routine handles the old fossil SSH probes
2059 */
2060 char *cgi_handle_ssh_probes(char *zLine, int zSize, char *z, char *zToken){
2061 /* Start looking for probes */
 
2062 while( fossil_strcmp(zToken, "echo")==0 ){
2063 zToken = extract_token(z, &z);
2064 if( zToken==0 ){
2065 malformed_request("malformed probe");
2066 }
@@ -2094,10 +2183,11 @@
2094 */
2095 void cgi_handle_ssh_transport(const char *zCmd){
2096 char *z, *zToken;
2097 char zLine[2000]; /* A single line of input. */
2098
 
2099 /* look for second newline of transport_flip */
2100 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
2101 malformed_request("incorrect transport_flip");
2102 }
2103 cgi_trace(zLine);
@@ -2143,10 +2233,11 @@
2143 char *zToFree;
2144 int nHdr = 0;
2145 int nRead;
2146 int c, n, m;
2147
 
2148 while( (c = fgetc(g.httpIn))!=EOF && fossil_isdigit((char)c) ){
2149 nHdr = nHdr*10 + (char)c - '0';
2150 }
2151 if( nHdr<16 ) malformed_request("SCGI header too short");
2152 zToFree = zHdr = fossil_malloc(nHdr);
@@ -2174,10 +2265,11 @@
2174 #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */
2175 #define HTTP_SERVER_SCGI 0x0002 /* SCGI instead of HTTP */
2176 #define HTTP_SERVER_HAD_REPOSITORY 0x0004 /* Was the repository open? */
2177 #define HTTP_SERVER_HAD_CHECKOUT 0x0008 /* Was a checkout open? */
2178 #define HTTP_SERVER_REPOLIST 0x0010 /* Allow repo listing */
 
2179
2180 #endif /* INTERFACE */
2181
2182 /*
2183 ** Maximum number of child processes that we can have running
@@ -2258,11 +2350,12 @@
2258 }
2259 }
2260 if( iPort>mxPort ) return 1;
2261 listen(listener,10);
2262 fossil_print("Listening for %s requests on TCP port %d\n",
2263 (flags & HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort);
 
2264 fflush(stdout);
2265 if( zBrowser ){
2266 assert( strstr(zBrowser,"%d")!=0 );
2267 zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
2268 #if defined(__CYGWIN__)
@@ -2293,11 +2386,15 @@
2293 select( listener+1, &readfds, 0, 0, &delay);
2294 if( FD_ISSET(listener, &readfds) ){
2295 lenaddr = sizeof(inaddr);
2296 connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
2297 if( connection>=0 ){
2298 child = fork();
 
 
 
 
2299 if( child!=0 ){
2300 if( child>0 ){
2301 nchildren++;
2302 nRequest++;
2303 }
2304
--- src/cgi.c
+++ src/cgi.c
@@ -84,10 +84,11 @@
84 #endif
85 #include <time.h>
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <unistd.h>
89 #include <assert.h>
90 #include "cgi.h"
91 #include "cygsup.h"
92
93 #if INTERFACE
94 /*
@@ -333,10 +334,95 @@
334 if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0;
335 return strncmp(zContentType, "text/", 5)==0
336 || sqlite3_strglob("application/*xml", zContentType)==0
337 || sqlite3_strglob("application/*javascript", zContentType)==0;
338 }
339
340
341 /*
342 ** The following routines read or write content from/to the wire for
343 ** an HTTP request. Depending on settings the content might be coming
344 ** from or going to a socket, or a file, or it might come from or go
345 ** to an SSL decoder/encoder.
346 */
347 /*
348 ** Works like fgets():
349 **
350 ** Read a single line of input into s[]. Ensure that s[] is zero-terminated.
351 ** The s[] buffer is size bytes and so at most size-1 bytes will be read.
352 **
353 ** Return a pointer to s[] on success, or NULL at end-of-input.
354 */
355 static char *cgi_fgets(char *s, int size){
356 if( !g.httpUseSSL ){
357 return fgets(s, size, g.httpIn);
358 }
359 #ifdef FOSSIL_ENABLE_SSL
360 return ssl_gets(g.httpSSLConn, s, size);
361 #else
362 fossil_fatal("SSL not available");
363 #endif
364 }
365
366 /* Works like fread():
367 **
368 ** Read as many as bytes of content as we can, up to a maximum of nmemb
369 ** bytes. Return the number of bytes read. Return -1 if there is no
370 ** further input or if an I/O error occurs.
371 */
372 size_t cgi_fread(void *ptr, size_t nmemb){
373 if( !g.httpUseSSL ){
374 return fread(ptr, 1, nmemb, g.httpIn);
375 }
376 #ifdef FOSSIL_ENABLE_SSL
377 return ssl_read_server(g.httpSSLConn, ptr, nmemb);
378 #else
379 fossil_fatal("SSL not available");
380 #endif
381 }
382
383 /* Works like feof():
384 **
385 ** Return true if end-of-input has been reached.
386 */
387 int cgi_feof(void){
388 if( !g.httpUseSSL ){
389 return feof(g.httpIn);
390 }
391 #ifdef FOSSIL_ENABLE_SSL
392 return ssl_eof(g.httpSSLConn);
393 #else
394 return 1;
395 #endif
396 }
397
398 /* Works like fwrite():
399 **
400 ** Try to output nmemb bytes of content. Return the number of
401 ** bytes actually written.
402 */
403 static size_t cgi_fwrite(void *ptr, size_t nmemb){
404 if( !g.httpUseSSL ){
405 return fwrite(ptr, 1, nmemb, g.httpOut);
406 }
407 #ifdef FOSSIL_ENABLE_SSL
408 return ssl_write_server(g.httpSSLConn, ptr, nmemb);
409 #else
410 fossil_fatal("SSL not available");
411 #endif
412 }
413
414 /* Works like fflush():
415 **
416 ** Make sure I/O has completed.
417 */
418 static void cgi_fflush(void){
419 if( !g.httpUseSSL ){
420 fflush(g.httpOut);
421 }
422 }
423
424
425 /*
426 ** Generate the reply to a web request. The output might be an
427 ** full HTTP response, or a CGI response, depending on how things have
428 ** be set up.
@@ -436,11 +522,11 @@
522 blob_appendf(&hdr, "Content-Length: %d\r\n", total_size);
523 }else{
524 total_size = 0;
525 }
526 blob_appendf(&hdr, "\r\n");
527 cgi_fwrite(blob_buffer(&hdr), blob_size(&hdr));
528 blob_reset(&hdr);
529 if( total_size>0
530 && iReplyStatus!=304
531 && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0
532 ){
@@ -452,17 +538,17 @@
538 }else{
539 int n = size - rangeStart;
540 if( n>total_size ){
541 n = total_size;
542 }
543 cgi_fwrite(blob_buffer(&cgiContent[i])+rangeStart, n);
544 rangeStart = 0;
545 total_size -= n;
546 }
547 }
548 }
549 cgi_fflush();
550 CGIDEBUG(("-------- END cgi ---------\n"));
551
552 /* After the webpage has been sent, do any useful background
553 ** processing.
554 */
@@ -1262,18 +1348,19 @@
1348 g.zContentType = zType;
1349 }
1350 blob_zero(&g.cgiIn);
1351 if( len>0 && zType ){
1352 if( fossil_strcmp(zType, "application/x-fossil")==0 ){
1353 if( blob_read_from_cgi(&g.cgiIn, len)!=len ){
1354 malformed_request("CGI content-length mismatch");
1355 }
1356 blob_uncompress(&g.cgiIn, &g.cgiIn);
1357 }
1358 #ifdef FOSSIL_ENABLE_JSON
1359 else if( noJson==0 && g.json.isJsonMode!=0
1360 && json_can_consume_content_type(zType)!=0 ){
1361 assert( !g.httpUseSSL );
1362 cgi_parse_POST_JSON(g.httpIn, (unsigned int)len);
1363 /*
1364 Potential TODOs:
1365
1366 1) If parsing fails, immediately return an error response
@@ -1281,11 +1368,11 @@
1368 */
1369 cgi_set_content_type(json_guess_content_type());
1370 }
1371 #endif /* FOSSIL_ENABLE_JSON */
1372 else{
1373 blob_read_from_cgi(&g.cgiIn, len);
1374 }
1375 }
1376 }
1377
1378 /*
@@ -1799,11 +1886,11 @@
1886 char *z, *zToken;
1887 int i;
1888 const char *zScheme = "http";
1889 char zLine[2000]; /* A single line of input. */
1890 g.fullHttpReply = 1;
1891 if( cgi_fgets(zLine, sizeof(zLine))==0 ){
1892 malformed_request("missing HTTP header");
1893 }
1894 blob_append(&g.httpHeader, zLine, -1);
1895 cgi_trace(zLine);
1896 zToken = extract_token(zLine, &z);
@@ -1837,11 +1924,11 @@
1924 }
1925
1926
1927 /* Get all the optional fields that follow the first line.
1928 */
1929 while( cgi_fgets(zLine,sizeof(zLine)) ){
1930 char *zFieldName;
1931 char *zVal;
1932
1933 cgi_trace(zLine);
1934 blob_append(&g.httpHeader, zLine, -1);
@@ -1916,10 +2003,11 @@
2003 char *z, *zToken;
2004 const char *zType = 0;
2005 int i, content_length = 0;
2006 char zLine[2000]; /* A single line of input. */
2007
2008 assert( !g.httpUseSSL );
2009 #ifdef FOSSIL_ENABLE_JSON
2010 if( nCycles==0 ){ json_bootstrap_early(); }
2011 #endif
2012 if( zIpAddr ){
2013 if( nCycles==0 ){
@@ -2057,10 +2145,11 @@
2145 /*
2146 ** This routine handles the old fossil SSH probes
2147 */
2148 char *cgi_handle_ssh_probes(char *zLine, int zSize, char *z, char *zToken){
2149 /* Start looking for probes */
2150 assert( !g.httpUseSSL );
2151 while( fossil_strcmp(zToken, "echo")==0 ){
2152 zToken = extract_token(z, &z);
2153 if( zToken==0 ){
2154 malformed_request("malformed probe");
2155 }
@@ -2094,10 +2183,11 @@
2183 */
2184 void cgi_handle_ssh_transport(const char *zCmd){
2185 char *z, *zToken;
2186 char zLine[2000]; /* A single line of input. */
2187
2188 assert( !g.httpUseSSL );
2189 /* look for second newline of transport_flip */
2190 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
2191 malformed_request("incorrect transport_flip");
2192 }
2193 cgi_trace(zLine);
@@ -2143,10 +2233,11 @@
2233 char *zToFree;
2234 int nHdr = 0;
2235 int nRead;
2236 int c, n, m;
2237
2238 assert( !g.httpUseSSL );
2239 while( (c = fgetc(g.httpIn))!=EOF && fossil_isdigit((char)c) ){
2240 nHdr = nHdr*10 + (char)c - '0';
2241 }
2242 if( nHdr<16 ) malformed_request("SCGI header too short");
2243 zToFree = zHdr = fossil_malloc(nHdr);
@@ -2174,10 +2265,11 @@
2265 #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */
2266 #define HTTP_SERVER_SCGI 0x0002 /* SCGI instead of HTTP */
2267 #define HTTP_SERVER_HAD_REPOSITORY 0x0004 /* Was the repository open? */
2268 #define HTTP_SERVER_HAD_CHECKOUT 0x0008 /* Was a checkout open? */
2269 #define HTTP_SERVER_REPOLIST 0x0010 /* Allow repo listing */
2270 #define HTTP_SERVER_NOFORK 0x0020 /* Do not call fork() */
2271
2272 #endif /* INTERFACE */
2273
2274 /*
2275 ** Maximum number of child processes that we can have running
@@ -2258,11 +2350,12 @@
2350 }
2351 }
2352 if( iPort>mxPort ) return 1;
2353 listen(listener,10);
2354 fossil_print("Listening for %s requests on TCP port %d\n",
2355 (flags & HTTP_SERVER_SCGI)!=0 ? "SCGI" :
2356 g.httpUseSSL?"TLS-encrypted HTTPS":"HTTP", iPort);
2357 fflush(stdout);
2358 if( zBrowser ){
2359 assert( strstr(zBrowser,"%d")!=0 );
2360 zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
2361 #if defined(__CYGWIN__)
@@ -2293,11 +2386,15 @@
2386 select( listener+1, &readfds, 0, 0, &delay);
2387 if( FD_ISSET(listener, &readfds) ){
2388 lenaddr = sizeof(inaddr);
2389 connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
2390 if( connection>=0 ){
2391 if( flags & HTTP_SERVER_NOFORK ){
2392 child = 0;
2393 }else{
2394 child = fork();
2395 }
2396 if( child!=0 ){
2397 if( child>0 ){
2398 nchildren++;
2399 nRequest++;
2400 }
2401
+20
--- src/db.c
+++ src/db.c
@@ -4263,10 +4263,11 @@
42634263
*/
42644264
/*
42654265
** SETTING: ssh-command width=40 sensitive
42664266
** The command used to talk to a remote machine with the "ssh://" protocol.
42674267
*/
4268
+
42684269
/*
42694270
** SETTING: ssl-ca-location width=40 sensitive
42704271
** The full pathname to a file containing PEM encoded
42714272
** CA root certificates, or a directory of certificates
42724273
** with filenames formed from the certificate hashes as
@@ -4277,10 +4278,24 @@
42774278
** Some platforms may add additional certificates.
42784279
** Checking your platform behaviour is required if the
42794280
** exact contents of the CA root is critical for your
42804281
** application.
42814282
*/
4283
+/*
4284
+** SETTING: ssl-cert width=40 block-text sensitive
4285
+** The text of SSL server certificate and private key used by commands
4286
+** like "fossil server". The text should be in the PEM format. Use
4287
+** the "fossil ssl-config load-certs" command to change this setting.
4288
+*/
4289
+/*
4290
+** SETTING: ssl-cert-file width=40 sensitive
4291
+** The name of a file that contains the SSL server certificate, or
4292
+** optionally the concatenation of the certificate and private key,
4293
+** for use by Fossil when it is acting as a server. If this file
4294
+** contains only the certificate, then the ssl-key-file setting must
4295
+** contain the name of a file containing the private key.
4296
+*/
42824297
/*
42834298
** SETTING: ssl-identity width=40 sensitive
42844299
** The full pathname to a file containing a certificate
42854300
** and private key in PEM format. Create by concatenating
42864301
** the certificate and private key files.
@@ -4287,10 +4302,15 @@
42874302
**
42884303
** This identity will be presented to SSL servers to
42894304
** authenticate this client, in addition to the normal
42904305
** password authentication.
42914306
*/
4307
+/*
4308
+** SETTING: ssl-key-file width=40 sensitive
4309
+** The name of a file that contains the SSL server certificate private
4310
+** key. Used in combination with "ssl-cert-file".
4311
+*/
42924312
#ifdef FOSSIL_ENABLE_TCL
42934313
/*
42944314
** SETTING: tcl boolean default=off sensitive
42954315
** If enabled Tcl integration commands will be added to the TH1
42964316
** interpreter, allowing arbitrary Tcl expressions and
42974317
--- src/db.c
+++ src/db.c
@@ -4263,10 +4263,11 @@
4263 */
4264 /*
4265 ** SETTING: ssh-command width=40 sensitive
4266 ** The command used to talk to a remote machine with the "ssh://" protocol.
4267 */
 
4268 /*
4269 ** SETTING: ssl-ca-location width=40 sensitive
4270 ** The full pathname to a file containing PEM encoded
4271 ** CA root certificates, or a directory of certificates
4272 ** with filenames formed from the certificate hashes as
@@ -4277,10 +4278,24 @@
4277 ** Some platforms may add additional certificates.
4278 ** Checking your platform behaviour is required if the
4279 ** exact contents of the CA root is critical for your
4280 ** application.
4281 */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4282 /*
4283 ** SETTING: ssl-identity width=40 sensitive
4284 ** The full pathname to a file containing a certificate
4285 ** and private key in PEM format. Create by concatenating
4286 ** the certificate and private key files.
@@ -4287,10 +4302,15 @@
4287 **
4288 ** This identity will be presented to SSL servers to
4289 ** authenticate this client, in addition to the normal
4290 ** password authentication.
4291 */
 
 
 
 
 
4292 #ifdef FOSSIL_ENABLE_TCL
4293 /*
4294 ** SETTING: tcl boolean default=off sensitive
4295 ** If enabled Tcl integration commands will be added to the TH1
4296 ** interpreter, allowing arbitrary Tcl expressions and
4297
--- src/db.c
+++ src/db.c
@@ -4263,10 +4263,11 @@
4263 */
4264 /*
4265 ** SETTING: ssh-command width=40 sensitive
4266 ** The command used to talk to a remote machine with the "ssh://" protocol.
4267 */
4268
4269 /*
4270 ** SETTING: ssl-ca-location width=40 sensitive
4271 ** The full pathname to a file containing PEM encoded
4272 ** CA root certificates, or a directory of certificates
4273 ** with filenames formed from the certificate hashes as
@@ -4277,10 +4278,24 @@
4278 ** Some platforms may add additional certificates.
4279 ** Checking your platform behaviour is required if the
4280 ** exact contents of the CA root is critical for your
4281 ** application.
4282 */
4283 /*
4284 ** SETTING: ssl-cert width=40 block-text sensitive
4285 ** The text of SSL server certificate and private key used by commands
4286 ** like "fossil server". The text should be in the PEM format. Use
4287 ** the "fossil ssl-config load-certs" command to change this setting.
4288 */
4289 /*
4290 ** SETTING: ssl-cert-file width=40 sensitive
4291 ** The name of a file that contains the SSL server certificate, or
4292 ** optionally the concatenation of the certificate and private key,
4293 ** for use by Fossil when it is acting as a server. If this file
4294 ** contains only the certificate, then the ssl-key-file setting must
4295 ** contain the name of a file containing the private key.
4296 */
4297 /*
4298 ** SETTING: ssl-identity width=40 sensitive
4299 ** The full pathname to a file containing a certificate
4300 ** and private key in PEM format. Create by concatenating
4301 ** the certificate and private key files.
@@ -4287,10 +4302,15 @@
4302 **
4303 ** This identity will be presented to SSL servers to
4304 ** authenticate this client, in addition to the normal
4305 ** password authentication.
4306 */
4307 /*
4308 ** SETTING: ssl-key-file width=40 sensitive
4309 ** The name of a file that contains the SSL server certificate private
4310 ** key. Used in combination with "ssl-cert-file".
4311 */
4312 #ifdef FOSSIL_ENABLE_TCL
4313 /*
4314 ** SETTING: tcl boolean default=off sensitive
4315 ** If enabled Tcl integration commands will be added to the TH1
4316 ** interpreter, allowing arbitrary Tcl expressions and
4317
+574 -34
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -16,12 +16,15 @@
1616
*******************************************************************************
1717
**
1818
** This file manages low-level SSL communications.
1919
**
2020
** This file implements a singleton. A single SSL connection may be active
21
-** at a time. State information is stored in static variables. The identity
22
-** of the server is held in global variables that are set by url_parse().
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.
2326
**
2427
** SSL support is abstracted out into this module because Fossil can
2528
** be compiled without SSL support (which requires OpenSSL library)
2629
*/
2730
@@ -41,11 +44,11 @@
4144
/*
4245
** There can only be a single OpenSSL IO connection open at a time.
4346
** State information about that IO is stored in the following
4447
** local variables:
4548
*/
46
-static int sslIsInit = 0; /* True after global initialization */
49
+static int sslIsInit = 0; /* 0: uninit 1: init as client 2: init as server */
4750
static BIO *iBio = 0; /* OpenSSL I/O abstraction */
4851
static char *sslErrMsg = 0; /* Text of most recent OpenSSL error */
4952
static SSL_CTX *sslCtx; /* SSL context */
5053
static SSL *ssl;
5154
static struct { /* Accept this SSL cert for this session only */
@@ -52,10 +55,120 @@
5255
char *zHost; /* Subject or host name */
5356
char *zHash; /* SHA2-256 hash of the cert */
5457
} sException;
5558
static int sslNoCertVerify = 0; /* Do not verify SSL certs */
5659
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
+
57170
/*
58171
** Clear the SSL error message
59172
*/
60173
static void ssl_clear_errmsg(void){
61174
free(sslErrMsg);
@@ -134,11 +247,11 @@
134247
135248
/*
136249
** Call this routine once before any other use of the SSL interface.
137250
** This routine does initial configuration of the SSL module.
138251
*/
139
-void ssl_global_init(void){
252
+static void ssl_global_init_client(void){
140253
const char *zCaSetting = 0, *zCaFile = 0, *zCaDirectory = 0;
141254
const char *identityFile;
142255
143256
if( sslIsInit==0 ){
144257
SSL_library_init();
@@ -193,10 +306,12 @@
193306
/* Register a callback to tell the user what to do when the server asks
194307
** for a cert */
195308
SSL_CTX_set_client_cert_cb(sslCtx, ssl_client_cert_callback);
196309
197310
sslIsInit = 1;
311
+ }else{
312
+ assert( sslIsInit==1 );
198313
}
199314
}
200315
201316
/*
202317
** Call this routine to shutdown the SSL module prior to program exit.
@@ -208,14 +323,14 @@
208323
sslIsInit = 0;
209324
}
210325
}
211326
212327
/*
213
-** Close the currently open SSL connection. If no connection is open,
328
+** Close the currently open client SSL connection. If no connection is open,
214329
** this routine is a no-op.
215330
*/
216
-void ssl_close(void){
331
+void ssl_close_client(void){
217332
if( iBio!=NULL ){
218333
(void)BIO_reset(iBio);
219334
BIO_free_all(iBio);
220335
iBio = NULL;
221336
}
@@ -280,34 +395,36 @@
280395
void ssl_disable_cert_verification(void){
281396
sslNoCertVerify = 1;
282397
}
283398
284399
/*
285
-** Open an SSL connection. The identify of the server is determined
286
-** as follows:
400
+** Open an SSL connection as a client that is to connect to the server
401
+** identified by pUrlData.
402
+**
403
+* The identify of the server is determined as follows:
287404
**
288405
** pUrlData->name Name of the server. Ex: fossil-scm.org
289406
** g.url.name Name of the proxy server, if proxying.
290407
** pUrlData->port TCP/IP port to use. Ex: 80
291408
**
292409
** Return the number of errors.
293410
*/
294
-int ssl_open(UrlData *pUrlData){
411
+int ssl_open_client(UrlData *pUrlData){
295412
X509 *cert;
296413
const char *zRemoteHost;
297414
298
- ssl_global_init();
415
+ ssl_global_init_client();
299416
if( pUrlData->useProxy ){
300417
int rc;
301418
char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
302419
BIO *sBio = BIO_new_connect(connStr);
303420
free(connStr);
304421
if( BIO_do_connect(sBio)<=0 ){
305422
ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
306423
pUrlData->name, pUrlData->port,
307424
ERR_reason_error_string(ERR_get_error()));
308
- ssl_close();
425
+ ssl_close_client();
309426
return 1;
310427
}
311428
rc = establish_proxy_tunnel(pUrlData, sBio);
312429
if( rc<200||rc>299 ){
313430
ssl_set_errmsg("SSL: proxy connect failed with HTTP status code %d", rc);
@@ -355,29 +472,29 @@
355472
free(connStr);
356473
if( BIO_do_connect(iBio)<=0 ){
357474
ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
358475
pUrlData->name, pUrlData->port,
359476
ERR_reason_error_string(ERR_get_error()));
360
- ssl_close();
477
+ ssl_close_client();
361478
return 1;
362479
}
363480
}
364481
365482
if( BIO_do_handshake(iBio)<=0 ) {
366483
ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)",
367484
pUrlData->useProxy?pUrlData->hostname:pUrlData->name,
368485
pUrlData->useProxy?pUrlData->proxyOrigPort:pUrlData->port,
369486
ERR_reason_error_string(ERR_get_error()));
370
- ssl_close();
487
+ ssl_close_client();
371488
return 1;
372489
}
373490
/* Check if certificate is valid */
374491
cert = SSL_get_peer_certificate(ssl);
375492
376493
if ( cert==NULL ){
377494
ssl_set_errmsg("No SSL certificate was presented by the peer");
378
- ssl_close();
495
+ ssl_close_client();
379496
return 1;
380497
}
381498
382499
/* Debugging hint: On unix-like system, run something like:
383500
**
@@ -441,11 +558,11 @@
441558
if( cReply!='y' && cReply!='Y'
442559
&& fossil_stricmp(blob_str(&ans),zHash)!=0
443560
){
444561
X509_free(cert);
445562
ssl_set_errmsg("SSL cert declined");
446
- ssl_close();
563
+ ssl_close_client();
447564
blob_reset(&ans);
448565
return 1;
449566
}
450567
blob_reset(&ans);
451568
ssl_one_time_exception(pUrlData, zHash);
@@ -528,11 +645,12 @@
528645
fossil_free(sException.zHash);
529646
sException.zHash = fossil_strdup(zHash);
530647
}
531648
532649
/*
533
-** Send content out over the SSL connection.
650
+** Send content out over the SSL connection from the client to
651
+** the server.
534652
*/
535653
size_t ssl_send(void *NotUsed, void *pContent, size_t N){
536654
size_t total = 0;
537655
while( N>0 ){
538656
int sent = BIO_write(iBio, pContent, N);
@@ -548,11 +666,12 @@
548666
}
549667
return total;
550668
}
551669
552670
/*
553
-** Receive content back from the SSL connection.
671
+** Receive content back from the client SSL connection. In other
672
+** words read the reply back from the server.
554673
*/
555674
size_t ssl_receive(void *NotUsed, void *pContent, size_t N){
556675
size_t total = 0;
557676
while( N>0 ){
558677
int got = BIO_read(iBio, pContent, N);
@@ -566,50 +685,366 @@
566685
N -= got;
567686
pContent = (void*)&((char*)pContent)[got];
568687
}
569688
return total;
570689
}
690
+
691
+/*
692
+** Initialize the SSL library so that it is able to handle
693
+** server-side connections. Invoke fossil_fatal() if there are
694
+** any problems.
695
+**
696
+** If zKeyFile and zCertFile are not NULL, then they are the names
697
+** of disk files that hold the certificate and private-key for the
698
+** server. If zCertFile is not NULL but zKeyFile is NULL, then
699
+** zCertFile is assumed to be a concatenation of the certificate and
700
+** the private-key in the PEM format.
701
+**
702
+** If zCertFile is NULL, then "ssl-cert" setting is consulted
703
+** to get the certificate and private-key (concatenated together, in
704
+** the PEM format). If there is no ssl-cert setting, then
705
+** a built-in self-signed cert is used.
706
+*/
707
+void ssl_init_server(const char *zCertFile, const char *zKeyFile){
708
+ if( sslIsInit==0 ){
709
+ const char *zTlsCert;
710
+ SSL_library_init();
711
+ SSL_load_error_strings();
712
+ OpenSSL_add_all_algorithms();
713
+ sslCtx = SSL_CTX_new(SSLv23_server_method());
714
+ if( sslCtx==0 ){
715
+ ERR_print_errors_fp(stderr);
716
+ fossil_fatal("Error initializing the SSL server");
717
+ }
718
+ if( zCertFile && zCertFile[0] ){
719
+ if( SSL_CTX_use_certificate_file(sslCtx,zCertFile,SSL_FILETYPE_PEM)<=0 ){
720
+ ERR_print_errors_fp(stderr);
721
+ fossil_fatal("Error loading CERT file \"%s\"", zCertFile);
722
+ }
723
+ if( zKeyFile==0 ) zKeyFile = zCertFile;
724
+ if( SSL_CTX_use_PrivateKey_file(sslCtx, zKeyFile, SSL_FILETYPE_PEM)<=0 ){
725
+ ERR_print_errors_fp(stderr);
726
+ fossil_fatal("Error loading PRIVATE KEY from file \"%s\"", zKeyFile);
727
+ }
728
+ }else
729
+ if( (zTlsCert = db_get("ssl-cert",0))!=0 ){
730
+ if( sslctx_use_cert_from_mem(sslCtx, zTlsCert, -1)
731
+ || sslctx_use_pkey_from_mem(sslCtx, zTlsCert, -1)
732
+ ){
733
+ fossil_fatal("Error loading the CERT from the"
734
+ " 'ssl-cert' setting");
735
+ }
736
+ }else if( sslctx_use_cert_from_mem(sslCtx, sslSelfCert, -1)
737
+ || sslctx_use_pkey_from_mem(sslCtx, sslSelfPKey, -1) ){
738
+ fossil_fatal("Error loading self-signed CERT");
739
+ }
740
+ if( !SSL_CTX_check_private_key(sslCtx) ){
741
+ fossil_fatal("PRIVATE KEY \"%s\" does not match CERT \"%s\"",
742
+ zKeyFile, zCertFile);
743
+ }
744
+ sslIsInit = 2;
745
+ }else{
746
+ assert( sslIsInit==2 );
747
+ }
748
+}
749
+
750
+typedef struct SslServerConn {
751
+ SSL *ssl; /* The SSL codec */
752
+ int atEof; /* True when EOF reached. */
753
+ int fd0; /* Read channel, or socket */
754
+ int fd1; /* Write channel */
755
+} SslServerConn;
756
+
757
+/*
758
+** Create a new server-side codec. The arguments are the file
759
+** descriptors from which teh codec reads and writes, respectively.
760
+**
761
+** If the writeFd is negative, then use then the readFd is a socket
762
+** over which we both read and write.
763
+*/
764
+void *ssl_new_server(int readFd, int writeFd){
765
+ SslServerConn *pServer = fossil_malloc_zero(sizeof(*pServer));
766
+ pServer->ssl = SSL_new(sslCtx);
767
+ pServer->fd0 = readFd;
768
+ pServer->fd1 = writeFd;
769
+ if( writeFd<0 ){
770
+ SSL_set_fd(pServer->ssl, readFd);
771
+ }else{
772
+ SSL_set_rfd(pServer->ssl, readFd);
773
+ SSL_set_wfd(pServer->ssl, writeFd);
774
+ }
775
+ SSL_accept(pServer->ssl);
776
+ return (void*)pServer;
777
+}
778
+
779
+/*
780
+** Close a server-side code previously returned from ssl_new_server().
781
+*/
782
+void ssl_close_server(void *pServerArg){
783
+ SslServerConn *pServer = (SslServerConn*)pServerArg;
784
+ SSL_free(pServer->ssl);
785
+ close(pServer->fd0);
786
+ if( pServer->fd1>=0 ) close(pServer->fd0);
787
+ fossil_free(pServer);
788
+}
789
+
790
+/*
791
+** Return TRUE if there are no more bytes available to be read from
792
+** the client.
793
+*/
794
+int ssl_eof(void *pServerArg){
795
+ SslServerConn *pServer = (SslServerConn*)pServerArg;
796
+ return pServer->atEof;
797
+}
798
+
799
+/*
800
+** Read cleartext bytes that have been received from the client and
801
+** decrypted by the SSL server codec.
802
+*/
803
+size_t ssl_read_server(void *pServerArg, char *zBuf, size_t nBuf){
804
+ int n;
805
+ SslServerConn *pServer = (SslServerConn*)pServerArg;
806
+ if( pServer->atEof ) return 0;
807
+ if( nBuf>0x7fffffff ){ fossil_fatal("SSL read too big"); }
808
+ n = SSL_read(pServer->ssl, zBuf, (int)nBuf);
809
+ if( n<nBuf ) pServer->atEof = 1;
810
+ return n;
811
+}
812
+
813
+/*
814
+** Read a single line of text from the client.
815
+*/
816
+char *ssl_gets(void *pServerArg, char *zBuf, int nBuf){
817
+ int n = 0;
818
+ int i;
819
+ SslServerConn *pServer = (SslServerConn*)pServerArg;
820
+
821
+ if( pServer->atEof ) return 0;
822
+ for(i=0; i<nBuf-1; i++){
823
+ n = SSL_read(pServer->ssl, &zBuf[i], 1);
824
+ if( n<=0 ){
825
+ return 0;
826
+ }
827
+ if( zBuf[i]=='\n' ) break;
828
+ }
829
+ zBuf[i+1] = 0;
830
+ return zBuf;
831
+}
832
+
833
+
834
+/*
835
+** Write cleartext bytes into the SSL server codec so that they can
836
+** be encrypted and sent back to the client.
837
+*/
838
+size_t ssl_write_server(void *pServerArg, char *zBuf, size_t nBuf){
839
+ int n;
840
+ SslServerConn *pServer = (SslServerConn*)pServerArg;
841
+ if( pServer->atEof ) return 0;
842
+ if( nBuf>0x7fffffff ){ fossil_fatal("SSL write too big"); }
843
+ n = SSL_write(pServer->ssl, zBuf, (int)nBuf);
844
+ return n;
845
+}
571846
572847
#endif /* FOSSIL_ENABLE_SSL */
573848
574849
/*
575850
** COMMAND: tls-config*
851
+** COMMAND: ssl-config
576852
**
577
-** Usage: %fossil tls-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
853
+** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
578854
**
579855
** This command is used to view or modify the TLS (Transport Layer
580856
** Security) configuration for Fossil. TLS (formerly SSL) is the
581857
** encryption technology used for secure HTTPS transport.
582858
**
583859
** Sub-commands:
584860
**
585
-** show Show the TLS configuration
861
+** clear-cert Remove information about server certificates.
862
+** This is a subset of the "scrub" command.
863
+**
864
+** load-cert PEM-FILES... Identify server certificate files. These
865
+** should be in the PEM format. There are
866
+** normally two files, the certificate and the
867
+** private-key. By default, the text of both
868
+** files is concatenated and added to the
869
+** "ssl-cert" setting. Use --filename to store
870
+** just the filenames.
871
+**
872
+** remove-exception DOMAINS Remove TLS cert exceptions for the domains
873
+** listed. Or remove them all if the --all
874
+** option is specified.
875
+**
876
+** scrub ?--force? Remove all SSL configuration data from the
877
+** repository. Use --force to omit the
878
+** confirmation.
586879
**
587
-** remove-exception DOMAIN... Remove TLS cert exceptions
588
-** for the domains listed. Or if
589
-** the --all option is specified,
590
-** remove all TLS cert exceptions.
880
+** show ?-v? Show the TLS configuration. Add -v to see
881
+** additional explaination
591882
*/
592883
void test_tlsconfig_info(void){
593
-#if !defined(FOSSIL_ENABLE_SSL)
594
- fossil_print("TLS disabled in this build\n");
595
-#else
596884
const char *zCmd;
597885
size_t nCmd;
598886
int nHit = 0;
599887
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
600888
db_open_config(1,0);
601
- zCmd = g.argc>=3 ? g.argv[2] : "show";
602
- nCmd = strlen(zCmd);
889
+ if( g.argc==2 || (g.argc>=3 && g.argv[2][0]=='-') ){
890
+ zCmd = "show";
891
+ nCmd = 4;
892
+ }else{
893
+ zCmd = g.argv[2];
894
+ nCmd = strlen(zCmd);
895
+ }
896
+ if( strncmp("clear-cert",zCmd,nCmd)==0 && nCmd>=4 ){
897
+ int bForce = find_option("force","f",0)!=0;
898
+ verify_all_options();
899
+ if( !bForce ){
900
+ Blob ans;
901
+ char cReply;
902
+ prompt_user(
903
+ "Confirm removing of the SSL server certificate from this repository.\n"
904
+ "The removal cannot be undone. Continue (y/N)? ", &ans);
905
+ cReply = blob_str(&ans)[0];
906
+ if( cReply!='y' && cReply!='Y' ){
907
+ fossil_exit(1);
908
+ }
909
+ }
910
+ db_unprotect(PROTECT_ALL);
911
+ db_multi_exec(
912
+ "PRAGMA secure_delete=ON;"
913
+ "DELETE FROM config "
914
+ " WHERE name IN ('ssl-cert','ssl-cert-file','ssl-cert-key');"
915
+ );
916
+ db_protect_pop();
917
+ }else
918
+ if( strncmp("load-cert",zCmd,nCmd)==0 && nCmd>=4 ){
919
+ int bFN = find_option("filename",0,0)!=0;
920
+ int i;
921
+ Blob allText = BLOB_INITIALIZER;
922
+ int haveCert = 0;
923
+ int haveKey = 0;
924
+ verify_all_options();
925
+ db_begin_transaction();
926
+ db_unprotect(PROTECT_ALL);
927
+ db_multi_exec(
928
+ "PRAGMA secure_delete=ON;"
929
+ "DELETE FROM config "
930
+ " WHERE name IN ('ssl-cert','ssl-cert-file','ssl-cert-key');"
931
+ );
932
+ nHit = 0;
933
+ for(i=3; i<g.argc; i++){
934
+ Blob x;
935
+ int isCert;
936
+ int isKey;
937
+ if( !file_isfile(g.argv[i], ExtFILE) ){
938
+ fossil_fatal("no such file: \"%s\"", g.argv[i]);
939
+ }
940
+ blob_read_from_file(&x, g.argv[i], ExtFILE);
941
+ isCert = strstr(blob_str(&x),"-----BEGIN CERTIFICATE-----")!=0;
942
+ isKey = strstr(blob_str(&x),"-----BEGIN PRIVATE KEY-----")!=0;
943
+ if( !isCert && !isKey ){
944
+ fossil_fatal("not a certificate or a private key: \"%s\"", g.argv[i]);
945
+ }
946
+ if( isCert ){
947
+ if( haveCert ){
948
+ fossil_fatal("more than one certificate provided");
949
+ }
950
+ haveCert = 1;
951
+ if( bFN ){
952
+ db_set("ssl-cert-file", file_canonical_name_dup(g.argv[i]), 0);
953
+ }else{
954
+ blob_append(&allText, blob_buffer(&x), blob_size(&x));
955
+ }
956
+ if( isKey && !haveKey ){
957
+ haveKey = 1;
958
+ isKey = 0;
959
+ }
960
+ }
961
+ if( isKey ){
962
+ if( haveKey ){
963
+ fossil_fatal("more than one private key provided");
964
+ }
965
+ haveKey = 1;
966
+ if( bFN ){
967
+ db_set("ssl-key-file", file_canonical_name_dup(g.argv[i]), 0);
968
+ }else{
969
+ blob_append(&allText, blob_buffer(&x), blob_size(&x));
970
+ }
971
+ }
972
+ }
973
+ db_protect_pop();
974
+ if( !haveCert ){
975
+ if( !haveKey ){
976
+ fossil_fatal("missing certificate and private-key");
977
+ }else{
978
+ fossil_fatal("missing certificate");
979
+ }
980
+ }else if( !haveKey ){
981
+ fossil_fatal("missing private-key");
982
+ }
983
+ if( !bFN ){
984
+ db_set("ssl-cert", blob_str(&allText), 0);
985
+ }
986
+ db_commit_transaction();
987
+ }else
988
+ if( strncmp("scrub",zCmd,nCmd)==0 && nCmd>4 ){
989
+ int bForce = find_option("force","f",0)!=0;
990
+ verify_all_options();
991
+ if( !bForce ){
992
+ Blob ans;
993
+ char cReply;
994
+ prompt_user(
995
+ "Scrubbing the SSL configuration will permanently delete information.\n"
996
+ "Changes cannot be undone. Continue (y/N)? ", &ans);
997
+ cReply = blob_str(&ans)[0];
998
+ if( cReply!='y' && cReply!='Y' ){
999
+ fossil_exit(1);
1000
+ }
1001
+ }
1002
+ db_unprotect(PROTECT_ALL);
1003
+ db_multi_exec(
1004
+ "PRAGMA secure_delete=ON;"
1005
+ "DELETE FROM config WHERE name GLOB 'ssl-*';"
1006
+ );
1007
+ db_protect_pop();
1008
+ }else
6031009
if( strncmp("show",zCmd,nCmd)==0 ){
6041010
const char *zName, *zValue;
6051011
size_t nName;
6061012
Stmt q;
1013
+ int verbose = find_option("verbose","v",0)!=0;
1014
+ verify_all_options();
1015
+
1016
+#if !defined(FOSSIL_ENABLE_SSL)
1017
+ fossil_print("OpenSSL-version: (none)\n");
1018
+ if( verbose ){
1019
+ fossil_print("\n"
1020
+ " The OpenSSL library is not used by this build of Fossil\n\n"
1021
+ );
1022
+ }
1023
+#else
6071024
fossil_print("OpenSSL-version: %s (0x%09x)\n",
6081025
SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER);
1026
+ if( verbose ){
1027
+ fossil_print("\n"
1028
+ " The version of the OpenSSL library being used\n"
1029
+ " by this instance of Fossil. Version 3.0.0 or\n"
1030
+ " later is recommended.\n\n"
1031
+ );
1032
+ }
1033
+
6091034
fossil_print("OpenSSL-cert-file: %s\n", X509_get_default_cert_file());
6101035
fossil_print("OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir());
1036
+ if( verbose ){
1037
+ fossil_print("\n"
1038
+ " The default locations for the set of root certificates\n"
1039
+ " used by the \"fossil sync\" and similar commands to verify\n"
1040
+ " the identity of servers for \"https:\" URLs. These values\n"
1041
+ " come into play when Fossil is used as a TLS client. These\n"
1042
+ " values are built into your OpenSSL library.\n\n"
1043
+ );
1044
+ }
1045
+
6111046
zName = X509_get_default_cert_file_env();
6121047
zValue = fossil_getenv(zName);
6131048
if( zValue==0 ) zValue = "";
6141049
nName = strlen(zName);
6151050
fossil_print("%s:%*s%s\n", zName, 18-nName, "", zValue);
@@ -616,25 +1051,86 @@
6161051
zName = X509_get_default_cert_dir_env();
6171052
zValue = fossil_getenv(zName);
6181053
if( zValue==0 ) zValue = "";
6191054
nName = strlen(zName);
6201055
fossil_print("%s:%*s%s\n", zName, 18-nName, "", zValue);
621
- nHit++;
1056
+ if( verbose ){
1057
+ fossil_print("\n"
1058
+ " Alternative locations for the root certificates used by Fossil\n"
1059
+ " when it is acting as a SSL client in order to verify the identity\n"
1060
+ " of servers. If specified, these alternative locations override\n"
1061
+ " the built-in locations.\n\n"
1062
+ );
1063
+ }
1064
+#endif /* FOSSIL_ENABLE_SSL */
1065
+
6221066
fossil_print("ssl-ca-location: %s\n", db_get("ssl-ca-location",""));
1067
+ if( verbose ){
1068
+ fossil_print("\n"
1069
+ " This setting is the name of a file or directory that contains\n"
1070
+ " the complete set of root certificates to used by Fossil when it\n"
1071
+ " is acting as a SSL client. If defined, this setting takes\n"
1072
+ " priority over built-in paths and environment variables\n\n"
1073
+ );
1074
+ }
1075
+
6231076
fossil_print("ssl-identity: %s\n", db_get("ssl-identity",""));
1077
+ if( verbose ){
1078
+ fossil_print("\n"
1079
+ " This setting is the name of a file that contains the PEM-format\n"
1080
+ " certificate and private-key used by Fossil clients to authentice\n"
1081
+ " with servers. Few servers actually require this, so this setting\n"
1082
+ " is usually blank.\n\n"
1083
+ );
1084
+ }
1085
+
1086
+ zValue = db_get("ssl-cert",0);
1087
+ if( zValue ){
1088
+ fossil_print("ssl-cert: (%d-byte PEM)\n", (int)strlen(zValue));
1089
+ }else{
1090
+ fossil_print("ssl-cert:\n");
1091
+ }
1092
+ if( verbose ){
1093
+ fossil_print("\n"
1094
+ " This setting is the PEM-formatted value of the SSL server\n"
1095
+ " certificate and private-key, used by Fossil when it is acting\n"
1096
+ " as a server via the \"fossil server\" command or similar.\n\n"
1097
+ );
1098
+ }
1099
+
1100
+ fossil_print("ssl-cert-file: %s\n", db_get("ssl-cert-file",""));
1101
+ fossil_print("ssl-key-file: %s\n", db_get("ssl-key-file",""));
1102
+ if( verbose ){
1103
+ fossil_print("\n"
1104
+ " This settings are the names of files that contin the certificate\n"
1105
+ " private-key used by Fossil when it is acting as a server.\n\n"
1106
+ );
1107
+ }
1108
+
6241109
db_prepare(&q,
625
- "SELECT name FROM global_config"
1110
+ "SELECT name, '' FROM global_config"
6261111
" WHERE name GLOB 'cert:*'"
6271112
"UNION ALL "
628
- "SELECT name FROM config"
1113
+ "SELECT name, date(mtime,'unixepoch') FROM config"
6291114
" WHERE name GLOB 'cert:*'"
6301115
" ORDER BY name"
6311116
);
1117
+ nHit = 0;
6321118
while( db_step(&q)==SQLITE_ROW ){
633
- fossil_print("exception: %s\n", db_column_text(&q,0)+5);
1119
+ fossil_print("exception: %-40s %s\n",
1120
+ db_column_text(&q,0)+5, db_column_text(&q,1));
1121
+ nHit++;
6341122
}
6351123
db_finalize(&q);
1124
+ if( nHit && verbose ){
1125
+ fossil_print("\n"
1126
+ " The exceptions are server certificates that the Fossil client\n"
1127
+ " is unable to verify using root certificates, but which should be\n"
1128
+ " accepted anyhow.\n\n"
1129
+ );
1130
+ }
1131
+
6361132
}else
6371133
if( strncmp("remove-exception",zCmd,nCmd)==0 ){
6381134
int i;
6391135
Blob sql;
6401136
char *zSep = "(";
@@ -673,10 +1169,54 @@
6731169
db_commit_transaction();
6741170
blob_reset(&sql);
6751171
}else
6761172
/*default*/{
6771173
fossil_fatal("unknown sub-command \"%s\".\nshould be one of:"
678
- " remove-exception show",
1174
+ " clear-certs load-certs remove-exception scrub show",
6791175
zCmd);
6801176
}
681
-#endif
1177
+}
1178
+
1179
+/*
1180
+** WEBPAGE: .well-known
1181
+**
1182
+** If the "--acme" option was supplied to "fossil server" or "fossil http" or
1183
+** similar, then this page returns the content of files found in the
1184
+** ".well-known" subdirectory of the same directory that contains the
1185
+** repository file. This facilitates Automated Certificate
1186
+** Management using tools like "certbot".
1187
+**
1188
+** The content is returned directly, without any interpretation, using
1189
+** a generic mimetype.
1190
+*/
1191
+void wellknown_page(void){
1192
+ char *zPath = 0;
1193
+ const char *zTail = P("name");
1194
+ Blob content;
1195
+ int i;
1196
+ char c;
1197
+ if( !g.fAllowACME ) goto wellknown_notfound;
1198
+ if( g.zRepositoryName==0 ) goto wellknown_notfound;
1199
+ if( zTail==0 ) goto wellknown_notfound;
1200
+ zPath = mprintf("%z/.well-known/%s", file_dirname(g.zRepositoryName), zTail);
1201
+ for(i=0; (c = zTail[i])!=0; i++){
1202
+ if( fossil_isalnum(c) ) continue;
1203
+ if( c=='.' ){
1204
+ if( i==0 || zTail[i-1]=='/' || zTail[i-1]=='.' ) goto wellknown_notfound;
1205
+ continue;
1206
+ }
1207
+ if( c==',' || c!='-' || c=='/' || c==':' || c=='_' || c=='~' ) continue;
1208
+ goto wellknown_notfound;
1209
+ }
1210
+ if( strstr("/..", zPath)!=0 ) goto wellknown_notfound;
1211
+ if( !file_isfile(zPath, ExtFILE) ) goto wellknown_notfound;
1212
+ blob_read_from_file(&content, zPath, ExtFILE);
1213
+ cgi_set_content(&content);
1214
+ cgi_set_content_type(mimetype_from_name(zPath));
1215
+ cgi_reply();
1216
+ return;
1217
+
1218
+wellknown_notfound:
1219
+ fossil_free(zPath);
1220
+ webpage_notfound_error(0);
1221
+ return;
6821222
}
6831223
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -16,12 +16,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. The identity
22 ** of the server is held in global variables that are set by url_parse().
 
 
 
23 **
24 ** SSL support is abstracted out into this module because Fossil can
25 ** be compiled without SSL support (which requires OpenSSL library)
26 */
27
@@ -41,11 +44,11 @@
41 /*
42 ** There can only be a single OpenSSL IO connection open at a time.
43 ** State information about that IO is stored in the following
44 ** local variables:
45 */
46 static int sslIsInit = 0; /* True after global initialization */
47 static BIO *iBio = 0; /* OpenSSL I/O abstraction */
48 static char *sslErrMsg = 0; /* Text of most recent OpenSSL error */
49 static SSL_CTX *sslCtx; /* SSL context */
50 static SSL *ssl;
51 static struct { /* Accept this SSL cert for this session only */
@@ -52,10 +55,120 @@
52 char *zHost; /* Subject or host name */
53 char *zHash; /* SHA2-256 hash of the cert */
54 } sException;
55 static int sslNoCertVerify = 0; /* Do not verify SSL certs */
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57 /*
58 ** Clear the SSL error message
59 */
60 static void ssl_clear_errmsg(void){
61 free(sslErrMsg);
@@ -134,11 +247,11 @@
134
135 /*
136 ** Call this routine once before any other use of the SSL interface.
137 ** This routine does initial configuration of the SSL module.
138 */
139 void ssl_global_init(void){
140 const char *zCaSetting = 0, *zCaFile = 0, *zCaDirectory = 0;
141 const char *identityFile;
142
143 if( sslIsInit==0 ){
144 SSL_library_init();
@@ -193,10 +306,12 @@
193 /* Register a callback to tell the user what to do when the server asks
194 ** for a cert */
195 SSL_CTX_set_client_cert_cb(sslCtx, ssl_client_cert_callback);
196
197 sslIsInit = 1;
 
 
198 }
199 }
200
201 /*
202 ** Call this routine to shutdown the SSL module prior to program exit.
@@ -208,14 +323,14 @@
208 sslIsInit = 0;
209 }
210 }
211
212 /*
213 ** Close the currently open SSL connection. If no connection is open,
214 ** this routine is a no-op.
215 */
216 void ssl_close(void){
217 if( iBio!=NULL ){
218 (void)BIO_reset(iBio);
219 BIO_free_all(iBio);
220 iBio = NULL;
221 }
@@ -280,34 +395,36 @@
280 void ssl_disable_cert_verification(void){
281 sslNoCertVerify = 1;
282 }
283
284 /*
285 ** Open an SSL connection. The identify of the server is determined
286 ** as follows:
 
 
287 **
288 ** pUrlData->name Name of the server. Ex: fossil-scm.org
289 ** g.url.name Name of the proxy server, if proxying.
290 ** pUrlData->port TCP/IP port to use. Ex: 80
291 **
292 ** Return the number of errors.
293 */
294 int ssl_open(UrlData *pUrlData){
295 X509 *cert;
296 const char *zRemoteHost;
297
298 ssl_global_init();
299 if( pUrlData->useProxy ){
300 int rc;
301 char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
302 BIO *sBio = BIO_new_connect(connStr);
303 free(connStr);
304 if( BIO_do_connect(sBio)<=0 ){
305 ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
306 pUrlData->name, pUrlData->port,
307 ERR_reason_error_string(ERR_get_error()));
308 ssl_close();
309 return 1;
310 }
311 rc = establish_proxy_tunnel(pUrlData, sBio);
312 if( rc<200||rc>299 ){
313 ssl_set_errmsg("SSL: proxy connect failed with HTTP status code %d", rc);
@@ -355,29 +472,29 @@
355 free(connStr);
356 if( BIO_do_connect(iBio)<=0 ){
357 ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
358 pUrlData->name, pUrlData->port,
359 ERR_reason_error_string(ERR_get_error()));
360 ssl_close();
361 return 1;
362 }
363 }
364
365 if( BIO_do_handshake(iBio)<=0 ) {
366 ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)",
367 pUrlData->useProxy?pUrlData->hostname:pUrlData->name,
368 pUrlData->useProxy?pUrlData->proxyOrigPort:pUrlData->port,
369 ERR_reason_error_string(ERR_get_error()));
370 ssl_close();
371 return 1;
372 }
373 /* Check if certificate is valid */
374 cert = SSL_get_peer_certificate(ssl);
375
376 if ( cert==NULL ){
377 ssl_set_errmsg("No SSL certificate was presented by the peer");
378 ssl_close();
379 return 1;
380 }
381
382 /* Debugging hint: On unix-like system, run something like:
383 **
@@ -441,11 +558,11 @@
441 if( cReply!='y' && cReply!='Y'
442 && fossil_stricmp(blob_str(&ans),zHash)!=0
443 ){
444 X509_free(cert);
445 ssl_set_errmsg("SSL cert declined");
446 ssl_close();
447 blob_reset(&ans);
448 return 1;
449 }
450 blob_reset(&ans);
451 ssl_one_time_exception(pUrlData, zHash);
@@ -528,11 +645,12 @@
528 fossil_free(sException.zHash);
529 sException.zHash = fossil_strdup(zHash);
530 }
531
532 /*
533 ** Send content out over the SSL connection.
 
534 */
535 size_t ssl_send(void *NotUsed, void *pContent, size_t N){
536 size_t total = 0;
537 while( N>0 ){
538 int sent = BIO_write(iBio, pContent, N);
@@ -548,11 +666,12 @@
548 }
549 return total;
550 }
551
552 /*
553 ** Receive content back from the SSL connection.
 
554 */
555 size_t ssl_receive(void *NotUsed, void *pContent, size_t N){
556 size_t total = 0;
557 while( N>0 ){
558 int got = BIO_read(iBio, pContent, N);
@@ -566,50 +685,366 @@
566 N -= got;
567 pContent = (void*)&((char*)pContent)[got];
568 }
569 return total;
570 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
571
572 #endif /* FOSSIL_ENABLE_SSL */
573
574 /*
575 ** COMMAND: tls-config*
 
576 **
577 ** Usage: %fossil tls-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
578 **
579 ** This command is used to view or modify the TLS (Transport Layer
580 ** Security) configuration for Fossil. TLS (formerly SSL) is the
581 ** encryption technology used for secure HTTPS transport.
582 **
583 ** Sub-commands:
584 **
585 ** show Show the TLS configuration
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
586 **
587 ** remove-exception DOMAIN... Remove TLS cert exceptions
588 ** for the domains listed. Or if
589 ** the --all option is specified,
590 ** remove all TLS cert exceptions.
591 */
592 void test_tlsconfig_info(void){
593 #if !defined(FOSSIL_ENABLE_SSL)
594 fossil_print("TLS disabled in this build\n");
595 #else
596 const char *zCmd;
597 size_t nCmd;
598 int nHit = 0;
599 db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
600 db_open_config(1,0);
601 zCmd = g.argc>=3 ? g.argv[2] : "show";
602 nCmd = strlen(zCmd);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
603 if( strncmp("show",zCmd,nCmd)==0 ){
604 const char *zName, *zValue;
605 size_t nName;
606 Stmt q;
 
 
 
 
 
 
 
 
 
 
 
607 fossil_print("OpenSSL-version: %s (0x%09x)\n",
608 SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER);
 
 
 
 
 
 
 
 
609 fossil_print("OpenSSL-cert-file: %s\n", X509_get_default_cert_file());
610 fossil_print("OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir());
 
 
 
 
 
 
 
 
 
 
611 zName = X509_get_default_cert_file_env();
612 zValue = fossil_getenv(zName);
613 if( zValue==0 ) zValue = "";
614 nName = strlen(zName);
615 fossil_print("%s:%*s%s\n", zName, 18-nName, "", zValue);
@@ -616,25 +1051,86 @@
616 zName = X509_get_default_cert_dir_env();
617 zValue = fossil_getenv(zName);
618 if( zValue==0 ) zValue = "";
619 nName = strlen(zName);
620 fossil_print("%s:%*s%s\n", zName, 18-nName, "", zValue);
621 nHit++;
 
 
 
 
 
 
 
 
 
622 fossil_print("ssl-ca-location: %s\n", db_get("ssl-ca-location",""));
 
 
 
 
 
 
 
 
 
623 fossil_print("ssl-identity: %s\n", db_get("ssl-identity",""));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
624 db_prepare(&q,
625 "SELECT name FROM global_config"
626 " WHERE name GLOB 'cert:*'"
627 "UNION ALL "
628 "SELECT name FROM config"
629 " WHERE name GLOB 'cert:*'"
630 " ORDER BY name"
631 );
 
632 while( db_step(&q)==SQLITE_ROW ){
633 fossil_print("exception: %s\n", db_column_text(&q,0)+5);
 
 
634 }
635 db_finalize(&q);
 
 
 
 
 
 
 
 
636 }else
637 if( strncmp("remove-exception",zCmd,nCmd)==0 ){
638 int i;
639 Blob sql;
640 char *zSep = "(";
@@ -673,10 +1169,54 @@
673 db_commit_transaction();
674 blob_reset(&sql);
675 }else
676 /*default*/{
677 fossil_fatal("unknown sub-command \"%s\".\nshould be one of:"
678 " remove-exception show",
679 zCmd);
680 }
681 #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
682 }
683
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -16,12 +16,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
@@ -41,11 +44,11 @@
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 */
@@ -52,10 +55,120 @@
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);
@@ -134,11 +247,11 @@
247
248 /*
249 ** Call this routine once before any other use of the SSL interface.
250 ** This routine does initial configuration of the SSL module.
251 */
252 static void ssl_global_init_client(void){
253 const char *zCaSetting = 0, *zCaFile = 0, *zCaDirectory = 0;
254 const char *identityFile;
255
256 if( sslIsInit==0 ){
257 SSL_library_init();
@@ -193,10 +306,12 @@
306 /* Register a callback to tell the user what to do when the server asks
307 ** for a cert */
308 SSL_CTX_set_client_cert_cb(sslCtx, ssl_client_cert_callback);
309
310 sslIsInit = 1;
311 }else{
312 assert( sslIsInit==1 );
313 }
314 }
315
316 /*
317 ** Call this routine to shutdown the SSL module prior to program exit.
@@ -208,14 +323,14 @@
323 sslIsInit = 0;
324 }
325 }
326
327 /*
328 ** Close the currently open client SSL connection. If no connection is open,
329 ** this routine is a no-op.
330 */
331 void ssl_close_client(void){
332 if( iBio!=NULL ){
333 (void)BIO_reset(iBio);
334 BIO_free_all(iBio);
335 iBio = NULL;
336 }
@@ -280,34 +395,36 @@
395 void ssl_disable_cert_verification(void){
396 sslNoCertVerify = 1;
397 }
398
399 /*
400 ** Open an SSL connection as a client that is to connect to the server
401 ** identified by pUrlData.
402 **
403 * The identify of the server is determined as follows:
404 **
405 ** pUrlData->name Name of the server. Ex: fossil-scm.org
406 ** g.url.name Name of the proxy server, if proxying.
407 ** pUrlData->port TCP/IP port to use. Ex: 80
408 **
409 ** Return the number of errors.
410 */
411 int ssl_open_client(UrlData *pUrlData){
412 X509 *cert;
413 const char *zRemoteHost;
414
415 ssl_global_init_client();
416 if( pUrlData->useProxy ){
417 int rc;
418 char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
419 BIO *sBio = BIO_new_connect(connStr);
420 free(connStr);
421 if( BIO_do_connect(sBio)<=0 ){
422 ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
423 pUrlData->name, pUrlData->port,
424 ERR_reason_error_string(ERR_get_error()));
425 ssl_close_client();
426 return 1;
427 }
428 rc = establish_proxy_tunnel(pUrlData, sBio);
429 if( rc<200||rc>299 ){
430 ssl_set_errmsg("SSL: proxy connect failed with HTTP status code %d", rc);
@@ -355,29 +472,29 @@
472 free(connStr);
473 if( BIO_do_connect(iBio)<=0 ){
474 ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
475 pUrlData->name, pUrlData->port,
476 ERR_reason_error_string(ERR_get_error()));
477 ssl_close_client();
478 return 1;
479 }
480 }
481
482 if( BIO_do_handshake(iBio)<=0 ) {
483 ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)",
484 pUrlData->useProxy?pUrlData->hostname:pUrlData->name,
485 pUrlData->useProxy?pUrlData->proxyOrigPort:pUrlData->port,
486 ERR_reason_error_string(ERR_get_error()));
487 ssl_close_client();
488 return 1;
489 }
490 /* Check if certificate is valid */
491 cert = SSL_get_peer_certificate(ssl);
492
493 if ( cert==NULL ){
494 ssl_set_errmsg("No SSL certificate was presented by the peer");
495 ssl_close_client();
496 return 1;
497 }
498
499 /* Debugging hint: On unix-like system, run something like:
500 **
@@ -441,11 +558,11 @@
558 if( cReply!='y' && cReply!='Y'
559 && fossil_stricmp(blob_str(&ans),zHash)!=0
560 ){
561 X509_free(cert);
562 ssl_set_errmsg("SSL cert declined");
563 ssl_close_client();
564 blob_reset(&ans);
565 return 1;
566 }
567 blob_reset(&ans);
568 ssl_one_time_exception(pUrlData, zHash);
@@ -528,11 +645,12 @@
645 fossil_free(sException.zHash);
646 sException.zHash = fossil_strdup(zHash);
647 }
648
649 /*
650 ** Send content out over the SSL connection from the client to
651 ** the server.
652 */
653 size_t ssl_send(void *NotUsed, void *pContent, size_t N){
654 size_t total = 0;
655 while( N>0 ){
656 int sent = BIO_write(iBio, pContent, N);
@@ -548,11 +666,12 @@
666 }
667 return total;
668 }
669
670 /*
671 ** Receive content back from the client SSL connection. In other
672 ** words read the reply back from the server.
673 */
674 size_t ssl_receive(void *NotUsed, void *pContent, size_t N){
675 size_t total = 0;
676 while( N>0 ){
677 int got = BIO_read(iBio, pContent, N);
@@ -566,50 +685,366 @@
685 N -= got;
686 pContent = (void*)&((char*)pContent)[got];
687 }
688 return total;
689 }
690
691 /*
692 ** Initialize the SSL library so that it is able to handle
693 ** server-side connections. Invoke fossil_fatal() if there are
694 ** any problems.
695 **
696 ** If zKeyFile and zCertFile are not NULL, then they are the names
697 ** of disk files that hold the certificate and private-key for the
698 ** server. If zCertFile is not NULL but zKeyFile is NULL, then
699 ** zCertFile is assumed to be a concatenation of the certificate and
700 ** the private-key in the PEM format.
701 **
702 ** If zCertFile is NULL, then "ssl-cert" setting is consulted
703 ** to get the certificate and private-key (concatenated together, in
704 ** the PEM format). If there is no ssl-cert setting, then
705 ** a built-in self-signed cert is used.
706 */
707 void ssl_init_server(const char *zCertFile, const char *zKeyFile){
708 if( sslIsInit==0 ){
709 const char *zTlsCert;
710 SSL_library_init();
711 SSL_load_error_strings();
712 OpenSSL_add_all_algorithms();
713 sslCtx = SSL_CTX_new(SSLv23_server_method());
714 if( sslCtx==0 ){
715 ERR_print_errors_fp(stderr);
716 fossil_fatal("Error initializing the SSL server");
717 }
718 if( zCertFile && zCertFile[0] ){
719 if( SSL_CTX_use_certificate_file(sslCtx,zCertFile,SSL_FILETYPE_PEM)<=0 ){
720 ERR_print_errors_fp(stderr);
721 fossil_fatal("Error loading CERT file \"%s\"", zCertFile);
722 }
723 if( zKeyFile==0 ) zKeyFile = zCertFile;
724 if( SSL_CTX_use_PrivateKey_file(sslCtx, zKeyFile, SSL_FILETYPE_PEM)<=0 ){
725 ERR_print_errors_fp(stderr);
726 fossil_fatal("Error loading PRIVATE KEY from file \"%s\"", zKeyFile);
727 }
728 }else
729 if( (zTlsCert = db_get("ssl-cert",0))!=0 ){
730 if( sslctx_use_cert_from_mem(sslCtx, zTlsCert, -1)
731 || sslctx_use_pkey_from_mem(sslCtx, zTlsCert, -1)
732 ){
733 fossil_fatal("Error loading the CERT from the"
734 " 'ssl-cert' setting");
735 }
736 }else if( sslctx_use_cert_from_mem(sslCtx, sslSelfCert, -1)
737 || sslctx_use_pkey_from_mem(sslCtx, sslSelfPKey, -1) ){
738 fossil_fatal("Error loading self-signed CERT");
739 }
740 if( !SSL_CTX_check_private_key(sslCtx) ){
741 fossil_fatal("PRIVATE KEY \"%s\" does not match CERT \"%s\"",
742 zKeyFile, zCertFile);
743 }
744 sslIsInit = 2;
745 }else{
746 assert( sslIsInit==2 );
747 }
748 }
749
750 typedef struct SslServerConn {
751 SSL *ssl; /* The SSL codec */
752 int atEof; /* True when EOF reached. */
753 int fd0; /* Read channel, or socket */
754 int fd1; /* Write channel */
755 } SslServerConn;
756
757 /*
758 ** Create a new server-side codec. The arguments are the file
759 ** descriptors from which teh codec reads and writes, respectively.
760 **
761 ** If the writeFd is negative, then use then the readFd is a socket
762 ** over which we both read and write.
763 */
764 void *ssl_new_server(int readFd, int writeFd){
765 SslServerConn *pServer = fossil_malloc_zero(sizeof(*pServer));
766 pServer->ssl = SSL_new(sslCtx);
767 pServer->fd0 = readFd;
768 pServer->fd1 = writeFd;
769 if( writeFd<0 ){
770 SSL_set_fd(pServer->ssl, readFd);
771 }else{
772 SSL_set_rfd(pServer->ssl, readFd);
773 SSL_set_wfd(pServer->ssl, writeFd);
774 }
775 SSL_accept(pServer->ssl);
776 return (void*)pServer;
777 }
778
779 /*
780 ** Close a server-side code previously returned from ssl_new_server().
781 */
782 void ssl_close_server(void *pServerArg){
783 SslServerConn *pServer = (SslServerConn*)pServerArg;
784 SSL_free(pServer->ssl);
785 close(pServer->fd0);
786 if( pServer->fd1>=0 ) close(pServer->fd0);
787 fossil_free(pServer);
788 }
789
790 /*
791 ** Return TRUE if there are no more bytes available to be read from
792 ** the client.
793 */
794 int ssl_eof(void *pServerArg){
795 SslServerConn *pServer = (SslServerConn*)pServerArg;
796 return pServer->atEof;
797 }
798
799 /*
800 ** Read cleartext bytes that have been received from the client and
801 ** decrypted by the SSL server codec.
802 */
803 size_t ssl_read_server(void *pServerArg, char *zBuf, size_t nBuf){
804 int n;
805 SslServerConn *pServer = (SslServerConn*)pServerArg;
806 if( pServer->atEof ) return 0;
807 if( nBuf>0x7fffffff ){ fossil_fatal("SSL read too big"); }
808 n = SSL_read(pServer->ssl, zBuf, (int)nBuf);
809 if( n<nBuf ) pServer->atEof = 1;
810 return n;
811 }
812
813 /*
814 ** Read a single line of text from the client.
815 */
816 char *ssl_gets(void *pServerArg, char *zBuf, int nBuf){
817 int n = 0;
818 int i;
819 SslServerConn *pServer = (SslServerConn*)pServerArg;
820
821 if( pServer->atEof ) return 0;
822 for(i=0; i<nBuf-1; i++){
823 n = SSL_read(pServer->ssl, &zBuf[i], 1);
824 if( n<=0 ){
825 return 0;
826 }
827 if( zBuf[i]=='\n' ) break;
828 }
829 zBuf[i+1] = 0;
830 return zBuf;
831 }
832
833
834 /*
835 ** Write cleartext bytes into the SSL server codec so that they can
836 ** be encrypted and sent back to the client.
837 */
838 size_t ssl_write_server(void *pServerArg, char *zBuf, size_t nBuf){
839 int n;
840 SslServerConn *pServer = (SslServerConn*)pServerArg;
841 if( pServer->atEof ) return 0;
842 if( nBuf>0x7fffffff ){ fossil_fatal("SSL write too big"); }
843 n = SSL_write(pServer->ssl, zBuf, (int)nBuf);
844 return n;
845 }
846
847 #endif /* FOSSIL_ENABLE_SSL */
848
849 /*
850 ** COMMAND: tls-config*
851 ** COMMAND: ssl-config
852 **
853 ** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
854 **
855 ** This command is used to view or modify the TLS (Transport Layer
856 ** Security) configuration for Fossil. TLS (formerly SSL) is the
857 ** encryption technology used for secure HTTPS transport.
858 **
859 ** Sub-commands:
860 **
861 ** clear-cert Remove information about server certificates.
862 ** This is a subset of the "scrub" command.
863 **
864 ** load-cert PEM-FILES... Identify server certificate files. These
865 ** should be in the PEM format. There are
866 ** normally two files, the certificate and the
867 ** private-key. By default, the text of both
868 ** files is concatenated and added to the
869 ** "ssl-cert" setting. Use --filename to store
870 ** just the filenames.
871 **
872 ** remove-exception DOMAINS Remove TLS cert exceptions for the domains
873 ** listed. Or remove them all if the --all
874 ** option is specified.
875 **
876 ** scrub ?--force? Remove all SSL configuration data from the
877 ** repository. Use --force to omit the
878 ** confirmation.
879 **
880 ** show ?-v? Show the TLS configuration. Add -v to see
881 ** additional explaination
 
 
882 */
883 void test_tlsconfig_info(void){
 
 
 
884 const char *zCmd;
885 size_t nCmd;
886 int nHit = 0;
887 db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
888 db_open_config(1,0);
889 if( g.argc==2 || (g.argc>=3 && g.argv[2][0]=='-') ){
890 zCmd = "show";
891 nCmd = 4;
892 }else{
893 zCmd = g.argv[2];
894 nCmd = strlen(zCmd);
895 }
896 if( strncmp("clear-cert",zCmd,nCmd)==0 && nCmd>=4 ){
897 int bForce = find_option("force","f",0)!=0;
898 verify_all_options();
899 if( !bForce ){
900 Blob ans;
901 char cReply;
902 prompt_user(
903 "Confirm removing of the SSL server certificate from this repository.\n"
904 "The removal cannot be undone. Continue (y/N)? ", &ans);
905 cReply = blob_str(&ans)[0];
906 if( cReply!='y' && cReply!='Y' ){
907 fossil_exit(1);
908 }
909 }
910 db_unprotect(PROTECT_ALL);
911 db_multi_exec(
912 "PRAGMA secure_delete=ON;"
913 "DELETE FROM config "
914 " WHERE name IN ('ssl-cert','ssl-cert-file','ssl-cert-key');"
915 );
916 db_protect_pop();
917 }else
918 if( strncmp("load-cert",zCmd,nCmd)==0 && nCmd>=4 ){
919 int bFN = find_option("filename",0,0)!=0;
920 int i;
921 Blob allText = BLOB_INITIALIZER;
922 int haveCert = 0;
923 int haveKey = 0;
924 verify_all_options();
925 db_begin_transaction();
926 db_unprotect(PROTECT_ALL);
927 db_multi_exec(
928 "PRAGMA secure_delete=ON;"
929 "DELETE FROM config "
930 " WHERE name IN ('ssl-cert','ssl-cert-file','ssl-cert-key');"
931 );
932 nHit = 0;
933 for(i=3; i<g.argc; i++){
934 Blob x;
935 int isCert;
936 int isKey;
937 if( !file_isfile(g.argv[i], ExtFILE) ){
938 fossil_fatal("no such file: \"%s\"", g.argv[i]);
939 }
940 blob_read_from_file(&x, g.argv[i], ExtFILE);
941 isCert = strstr(blob_str(&x),"-----BEGIN CERTIFICATE-----")!=0;
942 isKey = strstr(blob_str(&x),"-----BEGIN PRIVATE KEY-----")!=0;
943 if( !isCert && !isKey ){
944 fossil_fatal("not a certificate or a private key: \"%s\"", g.argv[i]);
945 }
946 if( isCert ){
947 if( haveCert ){
948 fossil_fatal("more than one certificate provided");
949 }
950 haveCert = 1;
951 if( bFN ){
952 db_set("ssl-cert-file", file_canonical_name_dup(g.argv[i]), 0);
953 }else{
954 blob_append(&allText, blob_buffer(&x), blob_size(&x));
955 }
956 if( isKey && !haveKey ){
957 haveKey = 1;
958 isKey = 0;
959 }
960 }
961 if( isKey ){
962 if( haveKey ){
963 fossil_fatal("more than one private key provided");
964 }
965 haveKey = 1;
966 if( bFN ){
967 db_set("ssl-key-file", file_canonical_name_dup(g.argv[i]), 0);
968 }else{
969 blob_append(&allText, blob_buffer(&x), blob_size(&x));
970 }
971 }
972 }
973 db_protect_pop();
974 if( !haveCert ){
975 if( !haveKey ){
976 fossil_fatal("missing certificate and private-key");
977 }else{
978 fossil_fatal("missing certificate");
979 }
980 }else if( !haveKey ){
981 fossil_fatal("missing private-key");
982 }
983 if( !bFN ){
984 db_set("ssl-cert", blob_str(&allText), 0);
985 }
986 db_commit_transaction();
987 }else
988 if( strncmp("scrub",zCmd,nCmd)==0 && nCmd>4 ){
989 int bForce = find_option("force","f",0)!=0;
990 verify_all_options();
991 if( !bForce ){
992 Blob ans;
993 char cReply;
994 prompt_user(
995 "Scrubbing the SSL configuration will permanently delete information.\n"
996 "Changes cannot be undone. Continue (y/N)? ", &ans);
997 cReply = blob_str(&ans)[0];
998 if( cReply!='y' && cReply!='Y' ){
999 fossil_exit(1);
1000 }
1001 }
1002 db_unprotect(PROTECT_ALL);
1003 db_multi_exec(
1004 "PRAGMA secure_delete=ON;"
1005 "DELETE FROM config WHERE name GLOB 'ssl-*';"
1006 );
1007 db_protect_pop();
1008 }else
1009 if( strncmp("show",zCmd,nCmd)==0 ){
1010 const char *zName, *zValue;
1011 size_t nName;
1012 Stmt q;
1013 int verbose = find_option("verbose","v",0)!=0;
1014 verify_all_options();
1015
1016 #if !defined(FOSSIL_ENABLE_SSL)
1017 fossil_print("OpenSSL-version: (none)\n");
1018 if( verbose ){
1019 fossil_print("\n"
1020 " The OpenSSL library is not used by this build of Fossil\n\n"
1021 );
1022 }
1023 #else
1024 fossil_print("OpenSSL-version: %s (0x%09x)\n",
1025 SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER);
1026 if( verbose ){
1027 fossil_print("\n"
1028 " The version of the OpenSSL library being used\n"
1029 " by this instance of Fossil. Version 3.0.0 or\n"
1030 " later is recommended.\n\n"
1031 );
1032 }
1033
1034 fossil_print("OpenSSL-cert-file: %s\n", X509_get_default_cert_file());
1035 fossil_print("OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir());
1036 if( verbose ){
1037 fossil_print("\n"
1038 " The default locations for the set of root certificates\n"
1039 " used by the \"fossil sync\" and similar commands to verify\n"
1040 " the identity of servers for \"https:\" URLs. These values\n"
1041 " come into play when Fossil is used as a TLS client. These\n"
1042 " values are built into your OpenSSL library.\n\n"
1043 );
1044 }
1045
1046 zName = X509_get_default_cert_file_env();
1047 zValue = fossil_getenv(zName);
1048 if( zValue==0 ) zValue = "";
1049 nName = strlen(zName);
1050 fossil_print("%s:%*s%s\n", zName, 18-nName, "", zValue);
@@ -616,25 +1051,86 @@
1051 zName = X509_get_default_cert_dir_env();
1052 zValue = fossil_getenv(zName);
1053 if( zValue==0 ) zValue = "";
1054 nName = strlen(zName);
1055 fossil_print("%s:%*s%s\n", zName, 18-nName, "", zValue);
1056 if( verbose ){
1057 fossil_print("\n"
1058 " Alternative locations for the root certificates used by Fossil\n"
1059 " when it is acting as a SSL client in order to verify the identity\n"
1060 " of servers. If specified, these alternative locations override\n"
1061 " the built-in locations.\n\n"
1062 );
1063 }
1064 #endif /* FOSSIL_ENABLE_SSL */
1065
1066 fossil_print("ssl-ca-location: %s\n", db_get("ssl-ca-location",""));
1067 if( verbose ){
1068 fossil_print("\n"
1069 " This setting is the name of a file or directory that contains\n"
1070 " the complete set of root certificates to used by Fossil when it\n"
1071 " is acting as a SSL client. If defined, this setting takes\n"
1072 " priority over built-in paths and environment variables\n\n"
1073 );
1074 }
1075
1076 fossil_print("ssl-identity: %s\n", db_get("ssl-identity",""));
1077 if( verbose ){
1078 fossil_print("\n"
1079 " This setting is the name of a file that contains the PEM-format\n"
1080 " certificate and private-key used by Fossil clients to authentice\n"
1081 " with servers. Few servers actually require this, so this setting\n"
1082 " is usually blank.\n\n"
1083 );
1084 }
1085
1086 zValue = db_get("ssl-cert",0);
1087 if( zValue ){
1088 fossil_print("ssl-cert: (%d-byte PEM)\n", (int)strlen(zValue));
1089 }else{
1090 fossil_print("ssl-cert:\n");
1091 }
1092 if( verbose ){
1093 fossil_print("\n"
1094 " This setting is the PEM-formatted value of the SSL server\n"
1095 " certificate and private-key, used by Fossil when it is acting\n"
1096 " as a server via the \"fossil server\" command or similar.\n\n"
1097 );
1098 }
1099
1100 fossil_print("ssl-cert-file: %s\n", db_get("ssl-cert-file",""));
1101 fossil_print("ssl-key-file: %s\n", db_get("ssl-key-file",""));
1102 if( verbose ){
1103 fossil_print("\n"
1104 " This settings are the names of files that contin the certificate\n"
1105 " private-key used by Fossil when it is acting as a server.\n\n"
1106 );
1107 }
1108
1109 db_prepare(&q,
1110 "SELECT name, '' FROM global_config"
1111 " WHERE name GLOB 'cert:*'"
1112 "UNION ALL "
1113 "SELECT name, date(mtime,'unixepoch') FROM config"
1114 " WHERE name GLOB 'cert:*'"
1115 " ORDER BY name"
1116 );
1117 nHit = 0;
1118 while( db_step(&q)==SQLITE_ROW ){
1119 fossil_print("exception: %-40s %s\n",
1120 db_column_text(&q,0)+5, db_column_text(&q,1));
1121 nHit++;
1122 }
1123 db_finalize(&q);
1124 if( nHit && verbose ){
1125 fossil_print("\n"
1126 " The exceptions are server certificates that the Fossil client\n"
1127 " is unable to verify using root certificates, but which should be\n"
1128 " accepted anyhow.\n\n"
1129 );
1130 }
1131
1132 }else
1133 if( strncmp("remove-exception",zCmd,nCmd)==0 ){
1134 int i;
1135 Blob sql;
1136 char *zSep = "(";
@@ -673,10 +1169,54 @@
1169 db_commit_transaction();
1170 blob_reset(&sql);
1171 }else
1172 /*default*/{
1173 fossil_fatal("unknown sub-command \"%s\".\nshould be one of:"
1174 " clear-certs load-certs remove-exception scrub show",
1175 zCmd);
1176 }
1177 }
1178
1179 /*
1180 ** WEBPAGE: .well-known
1181 **
1182 ** If the "--acme" option was supplied to "fossil server" or "fossil http" or
1183 ** similar, then this page returns the content of files found in the
1184 ** ".well-known" subdirectory of the same directory that contains the
1185 ** repository file. This facilitates Automated Certificate
1186 ** Management using tools like "certbot".
1187 **
1188 ** The content is returned directly, without any interpretation, using
1189 ** a generic mimetype.
1190 */
1191 void wellknown_page(void){
1192 char *zPath = 0;
1193 const char *zTail = P("name");
1194 Blob content;
1195 int i;
1196 char c;
1197 if( !g.fAllowACME ) goto wellknown_notfound;
1198 if( g.zRepositoryName==0 ) goto wellknown_notfound;
1199 if( zTail==0 ) goto wellknown_notfound;
1200 zPath = mprintf("%z/.well-known/%s", file_dirname(g.zRepositoryName), zTail);
1201 for(i=0; (c = zTail[i])!=0; i++){
1202 if( fossil_isalnum(c) ) continue;
1203 if( c=='.' ){
1204 if( i==0 || zTail[i-1]=='/' || zTail[i-1]=='.' ) goto wellknown_notfound;
1205 continue;
1206 }
1207 if( c==',' || c!='-' || c=='/' || c==':' || c=='_' || c=='~' ) continue;
1208 goto wellknown_notfound;
1209 }
1210 if( strstr("/..", zPath)!=0 ) goto wellknown_notfound;
1211 if( !file_isfile(zPath, ExtFILE) ) goto wellknown_notfound;
1212 blob_read_from_file(&content, zPath, ExtFILE);
1213 cgi_set_content(&content);
1214 cgi_set_content_type(mimetype_from_name(zPath));
1215 cgi_reply();
1216 return;
1217
1218 wellknown_notfound:
1219 fossil_free(zPath);
1220 webpage_notfound_error(0);
1221 return;
1222 }
1223
--- src/http_transport.c
+++ src/http_transport.c
@@ -170,11 +170,11 @@
170170
if( pUrlData->isSsh ){
171171
rc = transport_ssh_open(pUrlData);
172172
if( rc==0 ) transport.isOpen = 1;
173173
}else if( pUrlData->isHttps ){
174174
#ifdef FOSSIL_ENABLE_SSL
175
- rc = ssl_open(pUrlData);
175
+ rc = ssl_open_client(pUrlData);
176176
if( rc==0 ) transport.isOpen = 1;
177177
#else
178178
socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support");
179179
rc = 1;
180180
#endif
@@ -213,11 +213,11 @@
213213
}
214214
if( pUrlData->isSsh ){
215215
transport_ssh_close();
216216
}else if( pUrlData->isHttps ){
217217
#ifdef FOSSIL_ENABLE_SSL
218
- ssl_close();
218
+ ssl_close_client();
219219
#endif
220220
}else if( pUrlData->isFile ){
221221
if( transport.pFile ){
222222
fclose(transport.pFile);
223223
transport.pFile = 0;
224224
--- src/http_transport.c
+++ src/http_transport.c
@@ -170,11 +170,11 @@
170 if( pUrlData->isSsh ){
171 rc = transport_ssh_open(pUrlData);
172 if( rc==0 ) transport.isOpen = 1;
173 }else if( pUrlData->isHttps ){
174 #ifdef FOSSIL_ENABLE_SSL
175 rc = ssl_open(pUrlData);
176 if( rc==0 ) transport.isOpen = 1;
177 #else
178 socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support");
179 rc = 1;
180 #endif
@@ -213,11 +213,11 @@
213 }
214 if( pUrlData->isSsh ){
215 transport_ssh_close();
216 }else if( pUrlData->isHttps ){
217 #ifdef FOSSIL_ENABLE_SSL
218 ssl_close();
219 #endif
220 }else if( pUrlData->isFile ){
221 if( transport.pFile ){
222 fclose(transport.pFile);
223 transport.pFile = 0;
224
--- src/http_transport.c
+++ src/http_transport.c
@@ -170,11 +170,11 @@
170 if( pUrlData->isSsh ){
171 rc = transport_ssh_open(pUrlData);
172 if( rc==0 ) transport.isOpen = 1;
173 }else if( pUrlData->isHttps ){
174 #ifdef FOSSIL_ENABLE_SSL
175 rc = ssl_open_client(pUrlData);
176 if( rc==0 ) transport.isOpen = 1;
177 #else
178 socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support");
179 rc = 1;
180 #endif
@@ -213,11 +213,11 @@
213 }
214 if( pUrlData->isSsh ){
215 transport_ssh_close();
216 }else if( pUrlData->isHttps ){
217 #ifdef FOSSIL_ENABLE_SSL
218 ssl_close_client();
219 #endif
220 }else if( pUrlData->isFile ){
221 if( transport.pFile ){
222 fclose(transport.pFile);
223 transport.pFile = 0;
224
+145 -32
--- src/main.c
+++ src/main.c
@@ -166,10 +166,11 @@
166166
int fCgiTrace; /* True if --cgitrace is enabled */
167167
int fQuiet; /* True if -quiet flag is present */
168168
int fJail; /* True if running with a chroot jail */
169169
int fHttpTrace; /* Trace outbound HTTP requests */
170170
int fAnyTrace; /* Any kind of tracing */
171
+ int fAllowACME; /* Deliver files from .well-known */
171172
char *zHttpAuth; /* HTTP Authorization user:pass information */
172173
int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */
173174
int fSshTrace; /* Trace the SSH setup traffic */
174175
int fSshClient; /* HTTP client flags for SSH client */
175176
int fNoHttpCompress; /* Do not compress HTTP traffic (for debugging) */
@@ -195,10 +196,12 @@
195196
Th_Interp *interp; /* The TH1 interpreter */
196197
char *th1Setup; /* The TH1 post-creation setup script, if any */
197198
int th1Flags; /* The TH1 integration state flags */
198199
FILE *httpIn; /* Accept HTTP input from here */
199200
FILE *httpOut; /* Send HTTP output here */
201
+ int httpUseSSL; /* True to use an SSL codec for HTTP traffic */
202
+ void *httpSSLConn; /* The SSL connection */
200203
int xlinkClusterOnly; /* Set when cloning. Only process clusters */
201204
int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */
202205
int *aCommitFile; /* Array of files to be committed */
203206
int markPrivate; /* All new artifacts are private if true */
204207
char *ckinLockFail; /* Check-in lock failure received from server */
@@ -1662,10 +1665,11 @@
16621665
#if defined(_WIN32) || defined(__CYGWIN__)
16631666
if( sqlite3_strglob("/[a-zA-Z]:/*", zPathInfo)==0 ) i = 4;
16641667
#endif
16651668
}
16661669
while( 1 ){
1670
+ size_t nBase = strlen(zBase);
16671671
while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
16681672
16691673
/* The candidate repository name is some prefix of the PATH_INFO
16701674
** with ".fossil" appended */
16711675
zRepo = zToFree = mprintf("%s%.*s.fossil",zBase,i,zPathInfo);
@@ -1682,11 +1686,11 @@
16821686
** that "-" never occurs immediately after a "/" and that "." is always
16831687
** surrounded by two alphanumerics. Any character that does not
16841688
** satisfy these constraints is converted into "_".
16851689
*/
16861690
szFile = 0;
1687
- for(j=strlen(zBase)+1, k=0; zRepo[j] && k<i-1; j++, k++){
1691
+ for(j=nBase+1, k=0; zRepo[j] && k<i-1; j++, k++){
16881692
char c = zRepo[j];
16891693
if( fossil_isalnum(c) ) continue;
16901694
#if defined(_WIN32) || defined(__CYGWIN__)
16911695
/* Allow names to begin with "/X:/" on windows */
16921696
if( c==':' && j==2 && sqlite3_strglob("/[a-zA-Z]:/*", zRepo)==0 ){
@@ -1696,10 +1700,16 @@
16961700
if( c=='/' ) continue;
16971701
if( c=='_' ) continue;
16981702
if( c=='-' && zRepo[j-1]!='/' ) continue;
16991703
if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){
17001704
continue;
1705
+ }
1706
+ if( c=='.' && g.fAllowACME && j==nBase+1
1707
+ && strncmp(&zRepo[j-1],"/.well-known/",12)==0
1708
+ ){
1709
+ /* We allow .well-known as the top-level directory for ACME */
1710
+ continue;
17011711
}
17021712
/* If we reach this point, it means that the request URI contains
17031713
** an illegal character or character combination. Provoke a
17041714
** "Not Found" error. */
17051715
szFile = 1;
@@ -1755,11 +1765,11 @@
17551765
** designed to allow the delivery of a few static images or HTML
17561766
** pages.
17571767
*/
17581768
if( pFileGlob!=0
17591769
&& file_isfile(zCleanRepo, ExtFILE)
1760
- && glob_match(pFileGlob, file_cleanup_fullpath(zRepo))
1770
+ && glob_match(pFileGlob, file_cleanup_fullpath(zRepo+nBase))
17611771
&& sqlite3_strglob("*.fossil*",zRepo)!=0
17621772
&& (zMimetype = mimetype_from_name(zRepo))!=0
17631773
&& strcmp(zMimetype, "application/x-fossil-artifact")!=0
17641774
){
17651775
Blob content;
@@ -1767,10 +1777,25 @@
17671777
cgi_set_content_type(zMimetype);
17681778
cgi_set_content(&content);
17691779
cgi_reply();
17701780
return;
17711781
}
1782
+
1783
+ /* In support of the ACME protocol, files under the .well-known/
1784
+ ** directory is always accepted.
1785
+ */
1786
+ if( g.fAllowACME
1787
+ && strncmp(&zRepo[nBase],"/.well-known/",12)==0
1788
+ && file_isfile(zCleanRepo, ExtFILE)
1789
+ ){
1790
+ Blob content;
1791
+ blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE);
1792
+ cgi_set_content_type(mimetype_from_name(zRepo));
1793
+ cgi_set_content(&content);
1794
+ cgi_reply();
1795
+ return;
1796
+ }
17721797
zRepo[j] = '.';
17731798
}
17741799
17751800
/* If we reach this point, it means that the search of the PATH_INFO
17761801
** string is finished. Either zRepo contains the name of the
@@ -2554,10 +2579,29 @@
25542579
}
25552580
}
25562581
#endif
25572582
@ %d(GETPID())
25582583
}
2584
+
2585
+/*
2586
+** Check for options to "fossil server" or "fossil ui" that imply that
2587
+** SSL should be used, and initialize the SSL decoder.
2588
+*/
2589
+static void decode_ssl_options(void){
2590
+#if FOSSIL_ENABLE_SSL
2591
+ const char *zCertFile = 0;
2592
+ zCertFile = find_option("tls-cert-file",0,1);
2593
+ if( zCertFile ){
2594
+ g.httpUseSSL = 1;
2595
+ ssl_init_server(zCertFile, zCertFile);
2596
+ }
2597
+ if( find_option("tls",0,0)!=0 || find_option("ssl",0,0)!=0 ){
2598
+ g.httpUseSSL = 1;
2599
+ ssl_init_server(0,0);
2600
+ }
2601
+#endif
2602
+}
25592603
25602604
/*
25612605
** COMMAND: http*
25622606
**
25632607
** Usage: %fossil http ?REPOSITORY? ?OPTIONS?
@@ -2588,20 +2632,21 @@
25882632
** If the --localauth option is given, then automatic login is performed
25892633
** for requests coming from localhost, if the "localauth" setting is not
25902634
** enabled.
25912635
**
25922636
** Options:
2593
-** --baseurl URL base URL (useful with reverse proxies)
2594
-** --chroot DIR Use directory for chroot instead of repository path.
2595
-** --ckout-alias N Treat URIs of the form /doc/N/... as if they were
2596
-** /doc/ckout/...
2597
-** --extroot DIR document root for the /ext extension mechanism
2598
-** --files GLOB comma-separate glob patterns for static file to serve
2599
-** --host NAME specify hostname of the server
2600
-** --https signal a request coming in via https
2601
-** --in FILE Take input from FILE instead of standard input
2602
-** --ipaddr ADDR Assume the request comes from the given IP address
2637
+** --acme Deliver files from the ".well-known" subdirectory
2638
+** --baseurl URL base URL (useful with reverse proxies)
2639
+** --chroot DIR Use directory for chroot instead of repository path.
2640
+** --ckout-alias N Treat URIs of the form /doc/N/... as if they were
2641
+** /doc/ckout/...
2642
+** --extroot DIR document root for the /ext extension mechanism
2643
+** --files GLOB comma-separate glob patterns for static file to serve
2644
+** --host NAME specify hostname of the server
2645
+** --https signal a request coming in via https
2646
+** --in FILE Take input from FILE instead of standard input
2647
+** --ipaddr ADDR Assume the request comes from the given IP address
26032648
** --jsmode MODE Determine how JavaScript is delivered with pages.
26042649
** Mode can be one of:
26052650
** inline All JavaScript is inserted inline at
26062651
** one or more points in the HTML file.
26072652
** separate Separate HTTP requests are made for
@@ -2611,25 +2656,29 @@
26112656
** concatenate scripts together.
26122657
** Depending on the needs of any given page, inline
26132658
** and bundled modes might result in a single
26142659
** amalgamated script or several, but both approaches
26152660
** result in fewer HTTP requests than the separate mode.
2616
-** --localauth enable automatic login for local connections
2617
-** --nocompress do not compress HTTP replies
2618
-** --nodelay omit backoffice processing if it would delay process exit
2619
-** --nojail drop root privilege but do not enter the chroot jail
2620
-** --nossl signal that no SSL connections are available
2621
-** --notfound URL use URL as "HTTP 404, object not found" page.
2622
-** --out FILE write results to FILE instead of to standard output
2623
-** --repolist If REPOSITORY is directory, URL "/" lists all repos
2624
-** --scgi Interpret input as SCGI rather than HTTP
2625
-** --skin LABEL Use override skin LABEL
2626
-** --th-trace trace TH1 execution (for debugging purposes)
2627
-** --mainmenu FILE Override the mainmenu config setting with the contents
2628
-** of the given file.
2629
-** --usepidkey Use saved encryption key from parent process. This is
2630
-** only necessary when using SEE on Windows.
2661
+** --localauth enable automatic login for local connections
2662
+** --mainmenu FILE Override the mainmenu config setting with the contents
2663
+** of the given file.
2664
+** --nocompress do not compress HTTP replies
2665
+** --nodelay omit backoffice processing if it would delay
2666
+** process exit
2667
+** --nojail drop root privilege but do not enter the chroot jail
2668
+** --nossl signal that no SSL connections are available
2669
+** --notfound URL use URL as "HTTP 404, object not found" page.
2670
+** --out FILE write results to FILE instead of to standard output
2671
+** --repolist If REPOSITORY is directory, URL "/" lists all repos
2672
+** --scgi Interpret input as SCGI rather than HTTP
2673
+** --skin LABEL Use override skin LABEL
2674
+** --ssl Use TLS (HTTPS) encryption. Alias for --tls
2675
+** --th-trace trace TH1 execution (for debugging purposes)
2676
+** --tls Use TLS (HTTPS) encryption.
2677
+** --tls-cert-file FN Read the TLS certificate and private key from FN
2678
+** --usepidkey Use saved encryption key from parent process. This is
2679
+** only necessary when using SEE on Windows.
26312680
**
26322681
** See also: [[cgi]], [[server]], [[winsrv]]
26332682
*/
26342683
void cmd_http(void){
26352684
const char *zIpAddr = 0;
@@ -2697,13 +2746,27 @@
26972746
if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost);
26982747
g.zMainMenuFile = find_option("mainmenu",0,1);
26992748
if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
27002749
fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
27012750
}
2751
+ decode_ssl_options();
2752
+ if( find_option("acme",0,0)!=0 ) g.fAllowACME = 1;
27022753
27032754
/* We should be done with options.. */
27042755
verify_all_options();
2756
+ if( g.httpUseSSL ){
2757
+ if( useSCGI ){
2758
+ fossil_fatal("SSL not (yet) supported for SCGI");
2759
+ }
2760
+ if( g.fSshClient & CGI_SSH_CLIENT ){
2761
+ fossil_fatal("SSL not compatible with SSH");
2762
+ }
2763
+ if( zInFile || zOutFile ){
2764
+ fossil_fatal("SSL usable only on a socket");
2765
+ }
2766
+ cgi_replace_parameter("HTTPS","on");
2767
+ }
27052768
27062769
if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
27072770
g.cgiOutput = 1;
27082771
g.fullHttpReply = 1;
27092772
find_server_repository(2, 0);
@@ -2721,13 +2784,24 @@
27212784
if( useSCGI ){
27222785
cgi_handle_scgi_request();
27232786
}else if( g.fSshClient & CGI_SSH_CLIENT ){
27242787
ssh_request_loop(zIpAddr, glob_create(zFileGlob));
27252788
}else{
2789
+#if FOSSIL_ENABLE_SSL
2790
+ if( g.httpUseSSL ){
2791
+ g.httpSSLConn = ssl_new_server(0,-1);
2792
+ }
2793
+#endif
27262794
cgi_handle_http_request(zIpAddr);
27272795
}
27282796
process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
2797
+#if FOSSIL_ENABLE_SSL
2798
+ if( g.httpUseSSL && g.httpSSLConn ){
2799
+ ssl_close_server(g.httpSSLConn);
2800
+ g.httpSSLConn = 0;
2801
+ }
2802
+#endif /* FOSSIL_ENABLE_SSL */
27292803
}
27302804
27312805
/*
27322806
** Process all requests in a single SSH connection if possible.
27332807
*/
@@ -2890,10 +2964,11 @@
28902964
** --localauth option is present and the "localauth" setting is off and the
28912965
** connection is from localhost. The "ui" command also enables --repolist
28922966
** by default.
28932967
**
28942968
** Options:
2969
+** --acme Deliver files from the ".well-known" subdirectory.
28952970
** --baseurl URL Use URL as the base (useful for reverse proxies)
28962971
** --chroot DIR Use directory for chroot instead of repository path.
28972972
** --ckout-alias NAME Treat URIs of the form /doc/NAME/... as if they were
28982973
** /doc/ckout/...
28992974
** --create Create a new REPOSITORY if it does not already exist
@@ -2931,11 +3006,14 @@
29313006
** --page PAGE Start "ui" on PAGE. ex: --page "timeline?y=ci"
29323007
** -P|--port TCPPORT listen to request on port TCPPORT
29333008
** --repolist If REPOSITORY is dir, URL "/" lists repos.
29343009
** --scgi Accept SCGI rather than HTTP
29353010
** --skin LABEL Use override skin LABEL
3011
+** --ssl Use TLS (HTTPS) encryption. Alias for --tls
29363012
** --th-trace trace TH1 execution (for debugging purposes)
3013
+** --tls Use TLS (HTTPS) encryption.
3014
+** --tls-cert-file FN Read the TLS certificate and private key from FN
29373015
** --usepidkey Use saved encryption key from parent process. This is
29383016
** only necessary when using SEE on Windows.
29393017
**
29403018
** See also: [[cgi]], [[http]], [[winsrv]]
29413019
*/
@@ -3008,25 +3086,44 @@
30083086
if( zAltBase ){
30093087
set_base_url(zAltBase);
30103088
}
30113089
g.sslNotAvailable = find_option("nossl", 0, 0)!=0 || isUiCmd;
30123090
fNoBrowser = find_option("nobrowser", 0, 0)!=0;
3013
- if( find_option("https",0,0)!=0 ){
3091
+ decode_ssl_options();
3092
+ if( find_option("https",0,0)!=0 || g.httpUseSSL ){
30143093
cgi_replace_parameter("HTTPS","on");
30153094
}
30163095
if( find_option("localhost", 0, 0)!=0 ){
30173096
flags |= HTTP_SERVER_LOCALHOST;
30183097
}
30193098
g.zCkoutAlias = find_option("ckout-alias",0,1);
30203099
g.zMainMenuFile = find_option("mainmenu",0,1);
30213100
if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
30223101
fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
3102
+ }
3103
+ if( find_option("acme",0,0)!=0 ) g.fAllowACME = 1;
3104
+
3105
+ /* Undocumented option: --debug-nofork
3106
+ **
3107
+ ** This sets the HTTP_SERVER_NOFORK flag, which causes only the
3108
+ ** very first incoming TCP/IP connection to be processed. Used for
3109
+ ** debugging, since debugging across a fork() can be tricky
3110
+ */
3111
+ if( find_option("debug-nofork",0,0)!=0 ){
3112
+ flags |= HTTP_SERVER_NOFORK;
3113
+#if !defined(_WIN32)
3114
+ /* Disable the timeout during debugging */
3115
+ zTimeout = "100000000";
3116
+#endif
30233117
}
30243118
/* We should be done with options.. */
30253119
verify_all_options();
30263120
30273121
if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
3122
+ if( g.httpUseSSL && (flags & HTTP_SERVER_SCGI)!=0 ){
3123
+ fossil_fatal("SCGI does not (yet) support TLS-encrypted connections");
3124
+ }
30283125
if( isUiCmd && 3==g.argc && file_isdir(g.argv[2], ExtFILE)>0 ){
30293126
/* If REPOSITORY arg is the root of a checkout,
30303127
** chdir to that checkout so that the current version
30313128
** gets highlighted in the timeline by default. */
30323129
const char * zDir = g.argv[2];
@@ -3086,18 +3183,19 @@
30863183
iPort = db_get_int("http-port", 8080);
30873184
mxPort = iPort+100;
30883185
}
30893186
if( isUiCmd && !fNoBrowser ){
30903187
char *zBrowserArg;
3188
+ const char *zProtocol = g.httpUseSSL ? "https" : "http";
30913189
if( zRemote ) db_open_config(0,0);
30923190
zBrowser = fossil_web_browser();
30933191
if( zIpAddr==0 ){
3094
- zBrowserArg = mprintf("http://localhost:%%d/%s", zInitPage);
3192
+ zBrowserArg = mprintf("%s://localhost:%%d/%s", zProtocol, zInitPage);
30953193
}else if( strchr(zIpAddr,':') ){
3096
- zBrowserArg = mprintf("http://[%s]:%%d/%s", zIpAddr, zInitPage);
3194
+ zBrowserArg = mprintf("%s://[%s]:%%d/%s", zProtocol, zIpAddr, zInitPage);
30973195
}else{
3098
- zBrowserArg = mprintf("http://%s:%%d/%s", zIpAddr, zInitPage);
3196
+ zBrowserArg = mprintf("%s://%s:%%d/%s", zProtocol, zIpAddr, zInitPage);
30993197
}
31003198
zBrowserCmd = mprintf("%s %!$ &", zBrowser, zBrowserArg);
31013199
fossil_free(zBrowserArg);
31023200
}
31033201
if( zRemote ){
@@ -3186,20 +3284,35 @@
31863284
g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
31873285
}
31883286
}
31893287
if( flags & HTTP_SERVER_SCGI ){
31903288
cgi_handle_scgi_request();
3289
+ }else if( g.httpUseSSL ){
3290
+#if FOSSIL_ENABLE_SSL
3291
+ g.httpSSLConn = ssl_new_server(0,-1);
3292
+#endif
3293
+ cgi_handle_http_request(0);
31913294
}else{
31923295
cgi_handle_http_request(0);
31933296
}
31943297
process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
31953298
if( g.fAnyTrace ){
31963299
fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n",
31973300
getpid());
31983301
}
3199
-#else
3302
+#if FOSSIL_ENABLE_SSL
3303
+ if( g.httpUseSSL && g.httpSSLConn ){
3304
+ ssl_close_server(g.httpSSLConn);
3305
+ g.httpSSLConn = 0;
3306
+ }
3307
+#endif /* FOSSIL_ENABLE_SSL */
3308
+
3309
+#else /* WIN32 */
32003310
/* Win32 implementation */
3311
+ if( g.httpUseSSL ){
3312
+ fossil_fatal("TLS-encrypted server is not (yet) supported on Windows");
3313
+ }
32013314
if( allowRepoList ){
32023315
flags |= HTTP_SERVER_REPOLIST;
32033316
}
32043317
if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
32053318
win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
32063319
--- src/main.c
+++ src/main.c
@@ -166,10 +166,11 @@
166 int fCgiTrace; /* True if --cgitrace is enabled */
167 int fQuiet; /* True if -quiet flag is present */
168 int fJail; /* True if running with a chroot jail */
169 int fHttpTrace; /* Trace outbound HTTP requests */
170 int fAnyTrace; /* Any kind of tracing */
 
171 char *zHttpAuth; /* HTTP Authorization user:pass information */
172 int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */
173 int fSshTrace; /* Trace the SSH setup traffic */
174 int fSshClient; /* HTTP client flags for SSH client */
175 int fNoHttpCompress; /* Do not compress HTTP traffic (for debugging) */
@@ -195,10 +196,12 @@
195 Th_Interp *interp; /* The TH1 interpreter */
196 char *th1Setup; /* The TH1 post-creation setup script, if any */
197 int th1Flags; /* The TH1 integration state flags */
198 FILE *httpIn; /* Accept HTTP input from here */
199 FILE *httpOut; /* Send HTTP output here */
 
 
200 int xlinkClusterOnly; /* Set when cloning. Only process clusters */
201 int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */
202 int *aCommitFile; /* Array of files to be committed */
203 int markPrivate; /* All new artifacts are private if true */
204 char *ckinLockFail; /* Check-in lock failure received from server */
@@ -1662,10 +1665,11 @@
1662 #if defined(_WIN32) || defined(__CYGWIN__)
1663 if( sqlite3_strglob("/[a-zA-Z]:/*", zPathInfo)==0 ) i = 4;
1664 #endif
1665 }
1666 while( 1 ){
 
1667 while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
1668
1669 /* The candidate repository name is some prefix of the PATH_INFO
1670 ** with ".fossil" appended */
1671 zRepo = zToFree = mprintf("%s%.*s.fossil",zBase,i,zPathInfo);
@@ -1682,11 +1686,11 @@
1682 ** that "-" never occurs immediately after a "/" and that "." is always
1683 ** surrounded by two alphanumerics. Any character that does not
1684 ** satisfy these constraints is converted into "_".
1685 */
1686 szFile = 0;
1687 for(j=strlen(zBase)+1, k=0; zRepo[j] && k<i-1; j++, k++){
1688 char c = zRepo[j];
1689 if( fossil_isalnum(c) ) continue;
1690 #if defined(_WIN32) || defined(__CYGWIN__)
1691 /* Allow names to begin with "/X:/" on windows */
1692 if( c==':' && j==2 && sqlite3_strglob("/[a-zA-Z]:/*", zRepo)==0 ){
@@ -1696,10 +1700,16 @@
1696 if( c=='/' ) continue;
1697 if( c=='_' ) continue;
1698 if( c=='-' && zRepo[j-1]!='/' ) continue;
1699 if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){
1700 continue;
 
 
 
 
 
 
1701 }
1702 /* If we reach this point, it means that the request URI contains
1703 ** an illegal character or character combination. Provoke a
1704 ** "Not Found" error. */
1705 szFile = 1;
@@ -1755,11 +1765,11 @@
1755 ** designed to allow the delivery of a few static images or HTML
1756 ** pages.
1757 */
1758 if( pFileGlob!=0
1759 && file_isfile(zCleanRepo, ExtFILE)
1760 && glob_match(pFileGlob, file_cleanup_fullpath(zRepo))
1761 && sqlite3_strglob("*.fossil*",zRepo)!=0
1762 && (zMimetype = mimetype_from_name(zRepo))!=0
1763 && strcmp(zMimetype, "application/x-fossil-artifact")!=0
1764 ){
1765 Blob content;
@@ -1767,10 +1777,25 @@
1767 cgi_set_content_type(zMimetype);
1768 cgi_set_content(&content);
1769 cgi_reply();
1770 return;
1771 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1772 zRepo[j] = '.';
1773 }
1774
1775 /* If we reach this point, it means that the search of the PATH_INFO
1776 ** string is finished. Either zRepo contains the name of the
@@ -2554,10 +2579,29 @@
2554 }
2555 }
2556 #endif
2557 @ %d(GETPID())
2558 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2559
2560 /*
2561 ** COMMAND: http*
2562 **
2563 ** Usage: %fossil http ?REPOSITORY? ?OPTIONS?
@@ -2588,20 +2632,21 @@
2588 ** If the --localauth option is given, then automatic login is performed
2589 ** for requests coming from localhost, if the "localauth" setting is not
2590 ** enabled.
2591 **
2592 ** Options:
2593 ** --baseurl URL base URL (useful with reverse proxies)
2594 ** --chroot DIR Use directory for chroot instead of repository path.
2595 ** --ckout-alias N Treat URIs of the form /doc/N/... as if they were
2596 ** /doc/ckout/...
2597 ** --extroot DIR document root for the /ext extension mechanism
2598 ** --files GLOB comma-separate glob patterns for static file to serve
2599 ** --host NAME specify hostname of the server
2600 ** --https signal a request coming in via https
2601 ** --in FILE Take input from FILE instead of standard input
2602 ** --ipaddr ADDR Assume the request comes from the given IP address
 
2603 ** --jsmode MODE Determine how JavaScript is delivered with pages.
2604 ** Mode can be one of:
2605 ** inline All JavaScript is inserted inline at
2606 ** one or more points in the HTML file.
2607 ** separate Separate HTTP requests are made for
@@ -2611,25 +2656,29 @@
2611 ** concatenate scripts together.
2612 ** Depending on the needs of any given page, inline
2613 ** and bundled modes might result in a single
2614 ** amalgamated script or several, but both approaches
2615 ** result in fewer HTTP requests than the separate mode.
2616 ** --localauth enable automatic login for local connections
2617 ** --nocompress do not compress HTTP replies
2618 ** --nodelay omit backoffice processing if it would delay process exit
2619 ** --nojail drop root privilege but do not enter the chroot jail
2620 ** --nossl signal that no SSL connections are available
2621 ** --notfound URL use URL as "HTTP 404, object not found" page.
2622 ** --out FILE write results to FILE instead of to standard output
2623 ** --repolist If REPOSITORY is directory, URL "/" lists all repos
2624 ** --scgi Interpret input as SCGI rather than HTTP
2625 ** --skin LABEL Use override skin LABEL
2626 ** --th-trace trace TH1 execution (for debugging purposes)
2627 ** --mainmenu FILE Override the mainmenu config setting with the contents
2628 ** of the given file.
2629 ** --usepidkey Use saved encryption key from parent process. This is
2630 ** only necessary when using SEE on Windows.
 
 
 
 
2631 **
2632 ** See also: [[cgi]], [[server]], [[winsrv]]
2633 */
2634 void cmd_http(void){
2635 const char *zIpAddr = 0;
@@ -2697,13 +2746,27 @@
2697 if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost);
2698 g.zMainMenuFile = find_option("mainmenu",0,1);
2699 if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
2700 fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
2701 }
 
 
2702
2703 /* We should be done with options.. */
2704 verify_all_options();
 
 
 
 
 
 
 
 
 
 
 
 
2705
2706 if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
2707 g.cgiOutput = 1;
2708 g.fullHttpReply = 1;
2709 find_server_repository(2, 0);
@@ -2721,13 +2784,24 @@
2721 if( useSCGI ){
2722 cgi_handle_scgi_request();
2723 }else if( g.fSshClient & CGI_SSH_CLIENT ){
2724 ssh_request_loop(zIpAddr, glob_create(zFileGlob));
2725 }else{
 
 
 
 
 
2726 cgi_handle_http_request(zIpAddr);
2727 }
2728 process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
 
 
 
 
 
 
2729 }
2730
2731 /*
2732 ** Process all requests in a single SSH connection if possible.
2733 */
@@ -2890,10 +2964,11 @@
2890 ** --localauth option is present and the "localauth" setting is off and the
2891 ** connection is from localhost. The "ui" command also enables --repolist
2892 ** by default.
2893 **
2894 ** Options:
 
2895 ** --baseurl URL Use URL as the base (useful for reverse proxies)
2896 ** --chroot DIR Use directory for chroot instead of repository path.
2897 ** --ckout-alias NAME Treat URIs of the form /doc/NAME/... as if they were
2898 ** /doc/ckout/...
2899 ** --create Create a new REPOSITORY if it does not already exist
@@ -2931,11 +3006,14 @@
2931 ** --page PAGE Start "ui" on PAGE. ex: --page "timeline?y=ci"
2932 ** -P|--port TCPPORT listen to request on port TCPPORT
2933 ** --repolist If REPOSITORY is dir, URL "/" lists repos.
2934 ** --scgi Accept SCGI rather than HTTP
2935 ** --skin LABEL Use override skin LABEL
 
2936 ** --th-trace trace TH1 execution (for debugging purposes)
 
 
2937 ** --usepidkey Use saved encryption key from parent process. This is
2938 ** only necessary when using SEE on Windows.
2939 **
2940 ** See also: [[cgi]], [[http]], [[winsrv]]
2941 */
@@ -3008,25 +3086,44 @@
3008 if( zAltBase ){
3009 set_base_url(zAltBase);
3010 }
3011 g.sslNotAvailable = find_option("nossl", 0, 0)!=0 || isUiCmd;
3012 fNoBrowser = find_option("nobrowser", 0, 0)!=0;
3013 if( find_option("https",0,0)!=0 ){
 
3014 cgi_replace_parameter("HTTPS","on");
3015 }
3016 if( find_option("localhost", 0, 0)!=0 ){
3017 flags |= HTTP_SERVER_LOCALHOST;
3018 }
3019 g.zCkoutAlias = find_option("ckout-alias",0,1);
3020 g.zMainMenuFile = find_option("mainmenu",0,1);
3021 if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
3022 fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3023 }
3024 /* We should be done with options.. */
3025 verify_all_options();
3026
3027 if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
 
 
 
3028 if( isUiCmd && 3==g.argc && file_isdir(g.argv[2], ExtFILE)>0 ){
3029 /* If REPOSITORY arg is the root of a checkout,
3030 ** chdir to that checkout so that the current version
3031 ** gets highlighted in the timeline by default. */
3032 const char * zDir = g.argv[2];
@@ -3086,18 +3183,19 @@
3086 iPort = db_get_int("http-port", 8080);
3087 mxPort = iPort+100;
3088 }
3089 if( isUiCmd && !fNoBrowser ){
3090 char *zBrowserArg;
 
3091 if( zRemote ) db_open_config(0,0);
3092 zBrowser = fossil_web_browser();
3093 if( zIpAddr==0 ){
3094 zBrowserArg = mprintf("http://localhost:%%d/%s", zInitPage);
3095 }else if( strchr(zIpAddr,':') ){
3096 zBrowserArg = mprintf("http://[%s]:%%d/%s", zIpAddr, zInitPage);
3097 }else{
3098 zBrowserArg = mprintf("http://%s:%%d/%s", zIpAddr, zInitPage);
3099 }
3100 zBrowserCmd = mprintf("%s %!$ &", zBrowser, zBrowserArg);
3101 fossil_free(zBrowserArg);
3102 }
3103 if( zRemote ){
@@ -3186,20 +3284,35 @@
3186 g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
3187 }
3188 }
3189 if( flags & HTTP_SERVER_SCGI ){
3190 cgi_handle_scgi_request();
 
 
 
 
 
3191 }else{
3192 cgi_handle_http_request(0);
3193 }
3194 process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
3195 if( g.fAnyTrace ){
3196 fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n",
3197 getpid());
3198 }
3199 #else
 
 
 
 
 
 
 
3200 /* Win32 implementation */
 
 
 
3201 if( allowRepoList ){
3202 flags |= HTTP_SERVER_REPOLIST;
3203 }
3204 if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
3205 win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
3206
--- src/main.c
+++ src/main.c
@@ -166,10 +166,11 @@
166 int fCgiTrace; /* True if --cgitrace is enabled */
167 int fQuiet; /* True if -quiet flag is present */
168 int fJail; /* True if running with a chroot jail */
169 int fHttpTrace; /* Trace outbound HTTP requests */
170 int fAnyTrace; /* Any kind of tracing */
171 int fAllowACME; /* Deliver files from .well-known */
172 char *zHttpAuth; /* HTTP Authorization user:pass information */
173 int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */
174 int fSshTrace; /* Trace the SSH setup traffic */
175 int fSshClient; /* HTTP client flags for SSH client */
176 int fNoHttpCompress; /* Do not compress HTTP traffic (for debugging) */
@@ -195,10 +196,12 @@
196 Th_Interp *interp; /* The TH1 interpreter */
197 char *th1Setup; /* The TH1 post-creation setup script, if any */
198 int th1Flags; /* The TH1 integration state flags */
199 FILE *httpIn; /* Accept HTTP input from here */
200 FILE *httpOut; /* Send HTTP output here */
201 int httpUseSSL; /* True to use an SSL codec for HTTP traffic */
202 void *httpSSLConn; /* The SSL connection */
203 int xlinkClusterOnly; /* Set when cloning. Only process clusters */
204 int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */
205 int *aCommitFile; /* Array of files to be committed */
206 int markPrivate; /* All new artifacts are private if true */
207 char *ckinLockFail; /* Check-in lock failure received from server */
@@ -1662,10 +1665,11 @@
1665 #if defined(_WIN32) || defined(__CYGWIN__)
1666 if( sqlite3_strglob("/[a-zA-Z]:/*", zPathInfo)==0 ) i = 4;
1667 #endif
1668 }
1669 while( 1 ){
1670 size_t nBase = strlen(zBase);
1671 while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
1672
1673 /* The candidate repository name is some prefix of the PATH_INFO
1674 ** with ".fossil" appended */
1675 zRepo = zToFree = mprintf("%s%.*s.fossil",zBase,i,zPathInfo);
@@ -1682,11 +1686,11 @@
1686 ** that "-" never occurs immediately after a "/" and that "." is always
1687 ** surrounded by two alphanumerics. Any character that does not
1688 ** satisfy these constraints is converted into "_".
1689 */
1690 szFile = 0;
1691 for(j=nBase+1, k=0; zRepo[j] && k<i-1; j++, k++){
1692 char c = zRepo[j];
1693 if( fossil_isalnum(c) ) continue;
1694 #if defined(_WIN32) || defined(__CYGWIN__)
1695 /* Allow names to begin with "/X:/" on windows */
1696 if( c==':' && j==2 && sqlite3_strglob("/[a-zA-Z]:/*", zRepo)==0 ){
@@ -1696,10 +1700,16 @@
1700 if( c=='/' ) continue;
1701 if( c=='_' ) continue;
1702 if( c=='-' && zRepo[j-1]!='/' ) continue;
1703 if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){
1704 continue;
1705 }
1706 if( c=='.' && g.fAllowACME && j==nBase+1
1707 && strncmp(&zRepo[j-1],"/.well-known/",12)==0
1708 ){
1709 /* We allow .well-known as the top-level directory for ACME */
1710 continue;
1711 }
1712 /* If we reach this point, it means that the request URI contains
1713 ** an illegal character or character combination. Provoke a
1714 ** "Not Found" error. */
1715 szFile = 1;
@@ -1755,11 +1765,11 @@
1765 ** designed to allow the delivery of a few static images or HTML
1766 ** pages.
1767 */
1768 if( pFileGlob!=0
1769 && file_isfile(zCleanRepo, ExtFILE)
1770 && glob_match(pFileGlob, file_cleanup_fullpath(zRepo+nBase))
1771 && sqlite3_strglob("*.fossil*",zRepo)!=0
1772 && (zMimetype = mimetype_from_name(zRepo))!=0
1773 && strcmp(zMimetype, "application/x-fossil-artifact")!=0
1774 ){
1775 Blob content;
@@ -1767,10 +1777,25 @@
1777 cgi_set_content_type(zMimetype);
1778 cgi_set_content(&content);
1779 cgi_reply();
1780 return;
1781 }
1782
1783 /* In support of the ACME protocol, files under the .well-known/
1784 ** directory is always accepted.
1785 */
1786 if( g.fAllowACME
1787 && strncmp(&zRepo[nBase],"/.well-known/",12)==0
1788 && file_isfile(zCleanRepo, ExtFILE)
1789 ){
1790 Blob content;
1791 blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE);
1792 cgi_set_content_type(mimetype_from_name(zRepo));
1793 cgi_set_content(&content);
1794 cgi_reply();
1795 return;
1796 }
1797 zRepo[j] = '.';
1798 }
1799
1800 /* If we reach this point, it means that the search of the PATH_INFO
1801 ** string is finished. Either zRepo contains the name of the
@@ -2554,10 +2579,29 @@
2579 }
2580 }
2581 #endif
2582 @ %d(GETPID())
2583 }
2584
2585 /*
2586 ** Check for options to "fossil server" or "fossil ui" that imply that
2587 ** SSL should be used, and initialize the SSL decoder.
2588 */
2589 static void decode_ssl_options(void){
2590 #if FOSSIL_ENABLE_SSL
2591 const char *zCertFile = 0;
2592 zCertFile = find_option("tls-cert-file",0,1);
2593 if( zCertFile ){
2594 g.httpUseSSL = 1;
2595 ssl_init_server(zCertFile, zCertFile);
2596 }
2597 if( find_option("tls",0,0)!=0 || find_option("ssl",0,0)!=0 ){
2598 g.httpUseSSL = 1;
2599 ssl_init_server(0,0);
2600 }
2601 #endif
2602 }
2603
2604 /*
2605 ** COMMAND: http*
2606 **
2607 ** Usage: %fossil http ?REPOSITORY? ?OPTIONS?
@@ -2588,20 +2632,21 @@
2632 ** If the --localauth option is given, then automatic login is performed
2633 ** for requests coming from localhost, if the "localauth" setting is not
2634 ** enabled.
2635 **
2636 ** Options:
2637 ** --acme Deliver files from the ".well-known" subdirectory
2638 ** --baseurl URL base URL (useful with reverse proxies)
2639 ** --chroot DIR Use directory for chroot instead of repository path.
2640 ** --ckout-alias N Treat URIs of the form /doc/N/... as if they were
2641 ** /doc/ckout/...
2642 ** --extroot DIR document root for the /ext extension mechanism
2643 ** --files GLOB comma-separate glob patterns for static file to serve
2644 ** --host NAME specify hostname of the server
2645 ** --https signal a request coming in via https
2646 ** --in FILE Take input from FILE instead of standard input
2647 ** --ipaddr ADDR Assume the request comes from the given IP address
2648 ** --jsmode MODE Determine how JavaScript is delivered with pages.
2649 ** Mode can be one of:
2650 ** inline All JavaScript is inserted inline at
2651 ** one or more points in the HTML file.
2652 ** separate Separate HTTP requests are made for
@@ -2611,25 +2656,29 @@
2656 ** concatenate scripts together.
2657 ** Depending on the needs of any given page, inline
2658 ** and bundled modes might result in a single
2659 ** amalgamated script or several, but both approaches
2660 ** result in fewer HTTP requests than the separate mode.
2661 ** --localauth enable automatic login for local connections
2662 ** --mainmenu FILE Override the mainmenu config setting with the contents
2663 ** of the given file.
2664 ** --nocompress do not compress HTTP replies
2665 ** --nodelay omit backoffice processing if it would delay
2666 ** process exit
2667 ** --nojail drop root privilege but do not enter the chroot jail
2668 ** --nossl signal that no SSL connections are available
2669 ** --notfound URL use URL as "HTTP 404, object not found" page.
2670 ** --out FILE write results to FILE instead of to standard output
2671 ** --repolist If REPOSITORY is directory, URL "/" lists all repos
2672 ** --scgi Interpret input as SCGI rather than HTTP
2673 ** --skin LABEL Use override skin LABEL
2674 ** --ssl Use TLS (HTTPS) encryption. Alias for --tls
2675 ** --th-trace trace TH1 execution (for debugging purposes)
2676 ** --tls Use TLS (HTTPS) encryption.
2677 ** --tls-cert-file FN Read the TLS certificate and private key from FN
2678 ** --usepidkey Use saved encryption key from parent process. This is
2679 ** only necessary when using SEE on Windows.
2680 **
2681 ** See also: [[cgi]], [[server]], [[winsrv]]
2682 */
2683 void cmd_http(void){
2684 const char *zIpAddr = 0;
@@ -2697,13 +2746,27 @@
2746 if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost);
2747 g.zMainMenuFile = find_option("mainmenu",0,1);
2748 if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
2749 fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
2750 }
2751 decode_ssl_options();
2752 if( find_option("acme",0,0)!=0 ) g.fAllowACME = 1;
2753
2754 /* We should be done with options.. */
2755 verify_all_options();
2756 if( g.httpUseSSL ){
2757 if( useSCGI ){
2758 fossil_fatal("SSL not (yet) supported for SCGI");
2759 }
2760 if( g.fSshClient & CGI_SSH_CLIENT ){
2761 fossil_fatal("SSL not compatible with SSH");
2762 }
2763 if( zInFile || zOutFile ){
2764 fossil_fatal("SSL usable only on a socket");
2765 }
2766 cgi_replace_parameter("HTTPS","on");
2767 }
2768
2769 if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
2770 g.cgiOutput = 1;
2771 g.fullHttpReply = 1;
2772 find_server_repository(2, 0);
@@ -2721,13 +2784,24 @@
2784 if( useSCGI ){
2785 cgi_handle_scgi_request();
2786 }else if( g.fSshClient & CGI_SSH_CLIENT ){
2787 ssh_request_loop(zIpAddr, glob_create(zFileGlob));
2788 }else{
2789 #if FOSSIL_ENABLE_SSL
2790 if( g.httpUseSSL ){
2791 g.httpSSLConn = ssl_new_server(0,-1);
2792 }
2793 #endif
2794 cgi_handle_http_request(zIpAddr);
2795 }
2796 process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
2797 #if FOSSIL_ENABLE_SSL
2798 if( g.httpUseSSL && g.httpSSLConn ){
2799 ssl_close_server(g.httpSSLConn);
2800 g.httpSSLConn = 0;
2801 }
2802 #endif /* FOSSIL_ENABLE_SSL */
2803 }
2804
2805 /*
2806 ** Process all requests in a single SSH connection if possible.
2807 */
@@ -2890,10 +2964,11 @@
2964 ** --localauth option is present and the "localauth" setting is off and the
2965 ** connection is from localhost. The "ui" command also enables --repolist
2966 ** by default.
2967 **
2968 ** Options:
2969 ** --acme Deliver files from the ".well-known" subdirectory.
2970 ** --baseurl URL Use URL as the base (useful for reverse proxies)
2971 ** --chroot DIR Use directory for chroot instead of repository path.
2972 ** --ckout-alias NAME Treat URIs of the form /doc/NAME/... as if they were
2973 ** /doc/ckout/...
2974 ** --create Create a new REPOSITORY if it does not already exist
@@ -2931,11 +3006,14 @@
3006 ** --page PAGE Start "ui" on PAGE. ex: --page "timeline?y=ci"
3007 ** -P|--port TCPPORT listen to request on port TCPPORT
3008 ** --repolist If REPOSITORY is dir, URL "/" lists repos.
3009 ** --scgi Accept SCGI rather than HTTP
3010 ** --skin LABEL Use override skin LABEL
3011 ** --ssl Use TLS (HTTPS) encryption. Alias for --tls
3012 ** --th-trace trace TH1 execution (for debugging purposes)
3013 ** --tls Use TLS (HTTPS) encryption.
3014 ** --tls-cert-file FN Read the TLS certificate and private key from FN
3015 ** --usepidkey Use saved encryption key from parent process. This is
3016 ** only necessary when using SEE on Windows.
3017 **
3018 ** See also: [[cgi]], [[http]], [[winsrv]]
3019 */
@@ -3008,25 +3086,44 @@
3086 if( zAltBase ){
3087 set_base_url(zAltBase);
3088 }
3089 g.sslNotAvailable = find_option("nossl", 0, 0)!=0 || isUiCmd;
3090 fNoBrowser = find_option("nobrowser", 0, 0)!=0;
3091 decode_ssl_options();
3092 if( find_option("https",0,0)!=0 || g.httpUseSSL ){
3093 cgi_replace_parameter("HTTPS","on");
3094 }
3095 if( find_option("localhost", 0, 0)!=0 ){
3096 flags |= HTTP_SERVER_LOCALHOST;
3097 }
3098 g.zCkoutAlias = find_option("ckout-alias",0,1);
3099 g.zMainMenuFile = find_option("mainmenu",0,1);
3100 if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
3101 fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
3102 }
3103 if( find_option("acme",0,0)!=0 ) g.fAllowACME = 1;
3104
3105 /* Undocumented option: --debug-nofork
3106 **
3107 ** This sets the HTTP_SERVER_NOFORK flag, which causes only the
3108 ** very first incoming TCP/IP connection to be processed. Used for
3109 ** debugging, since debugging across a fork() can be tricky
3110 */
3111 if( find_option("debug-nofork",0,0)!=0 ){
3112 flags |= HTTP_SERVER_NOFORK;
3113 #if !defined(_WIN32)
3114 /* Disable the timeout during debugging */
3115 zTimeout = "100000000";
3116 #endif
3117 }
3118 /* We should be done with options.. */
3119 verify_all_options();
3120
3121 if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
3122 if( g.httpUseSSL && (flags & HTTP_SERVER_SCGI)!=0 ){
3123 fossil_fatal("SCGI does not (yet) support TLS-encrypted connections");
3124 }
3125 if( isUiCmd && 3==g.argc && file_isdir(g.argv[2], ExtFILE)>0 ){
3126 /* If REPOSITORY arg is the root of a checkout,
3127 ** chdir to that checkout so that the current version
3128 ** gets highlighted in the timeline by default. */
3129 const char * zDir = g.argv[2];
@@ -3086,18 +3183,19 @@
3183 iPort = db_get_int("http-port", 8080);
3184 mxPort = iPort+100;
3185 }
3186 if( isUiCmd && !fNoBrowser ){
3187 char *zBrowserArg;
3188 const char *zProtocol = g.httpUseSSL ? "https" : "http";
3189 if( zRemote ) db_open_config(0,0);
3190 zBrowser = fossil_web_browser();
3191 if( zIpAddr==0 ){
3192 zBrowserArg = mprintf("%s://localhost:%%d/%s", zProtocol, zInitPage);
3193 }else if( strchr(zIpAddr,':') ){
3194 zBrowserArg = mprintf("%s://[%s]:%%d/%s", zProtocol, zIpAddr, zInitPage);
3195 }else{
3196 zBrowserArg = mprintf("%s://%s:%%d/%s", zProtocol, zIpAddr, zInitPage);
3197 }
3198 zBrowserCmd = mprintf("%s %!$ &", zBrowser, zBrowserArg);
3199 fossil_free(zBrowserArg);
3200 }
3201 if( zRemote ){
@@ -3186,20 +3284,35 @@
3284 g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
3285 }
3286 }
3287 if( flags & HTTP_SERVER_SCGI ){
3288 cgi_handle_scgi_request();
3289 }else if( g.httpUseSSL ){
3290 #if FOSSIL_ENABLE_SSL
3291 g.httpSSLConn = ssl_new_server(0,-1);
3292 #endif
3293 cgi_handle_http_request(0);
3294 }else{
3295 cgi_handle_http_request(0);
3296 }
3297 process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
3298 if( g.fAnyTrace ){
3299 fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n",
3300 getpid());
3301 }
3302 #if FOSSIL_ENABLE_SSL
3303 if( g.httpUseSSL && g.httpSSLConn ){
3304 ssl_close_server(g.httpSSLConn);
3305 g.httpSSLConn = 0;
3306 }
3307 #endif /* FOSSIL_ENABLE_SSL */
3308
3309 #else /* WIN32 */
3310 /* Win32 implementation */
3311 if( g.httpUseSSL ){
3312 fossil_fatal("TLS-encrypted server is not (yet) supported on Windows");
3313 }
3314 if( allowRepoList ){
3315 flags |= HTTP_SERVER_REPOLIST;
3316 }
3317 if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
3318 win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
3319
+3 -1
--- src/rebuild.c
+++ src/rebuild.c
@@ -916,10 +916,11 @@
916916
delete_private_content();
917917
}
918918
if( !privateOnly ){
919919
db_unprotect(PROTECT_ALL);
920920
db_multi_exec(
921
+ "PRAGMA secure_delete=ON;"
921922
"UPDATE user SET pw='';"
922923
"DELETE FROM config WHERE name IN"
923924
"(WITH pattern(x) AS (VALUES"
924925
" ('baseurl:*'),"
925926
" ('cert:*'),"
@@ -933,11 +934,12 @@
933934
" ('peer-*'),"
934935
" ('skin:*'),"
935936
" ('subrepo:*'),"
936937
" ('sync-*'),"
937938
" ('syncfrom:*'),"
938
- " ('syncwith:*')"
939
+ " ('syncwith:*'),"
940
+ " ('ssl-*')"
939941
") SELECT name FROM config, pattern WHERE name GLOB x);"
940942
);
941943
if( bVerily ){
942944
db_multi_exec(
943945
"DELETE FROM concealed;\n"
944946
--- src/rebuild.c
+++ src/rebuild.c
@@ -916,10 +916,11 @@
916 delete_private_content();
917 }
918 if( !privateOnly ){
919 db_unprotect(PROTECT_ALL);
920 db_multi_exec(
 
921 "UPDATE user SET pw='';"
922 "DELETE FROM config WHERE name IN"
923 "(WITH pattern(x) AS (VALUES"
924 " ('baseurl:*'),"
925 " ('cert:*'),"
@@ -933,11 +934,12 @@
933 " ('peer-*'),"
934 " ('skin:*'),"
935 " ('subrepo:*'),"
936 " ('sync-*'),"
937 " ('syncfrom:*'),"
938 " ('syncwith:*')"
 
939 ") SELECT name FROM config, pattern WHERE name GLOB x);"
940 );
941 if( bVerily ){
942 db_multi_exec(
943 "DELETE FROM concealed;\n"
944
--- src/rebuild.c
+++ src/rebuild.c
@@ -916,10 +916,11 @@
916 delete_private_content();
917 }
918 if( !privateOnly ){
919 db_unprotect(PROTECT_ALL);
920 db_multi_exec(
921 "PRAGMA secure_delete=ON;"
922 "UPDATE user SET pw='';"
923 "DELETE FROM config WHERE name IN"
924 "(WITH pattern(x) AS (VALUES"
925 " ('baseurl:*'),"
926 " ('cert:*'),"
@@ -933,11 +934,12 @@
934 " ('peer-*'),"
935 " ('skin:*'),"
936 " ('subrepo:*'),"
937 " ('sync-*'),"
938 " ('syncfrom:*'),"
939 " ('syncwith:*'),"
940 " ('ssl-*')"
941 ") SELECT name FROM config, pattern WHERE name GLOB x);"
942 );
943 if( bVerily ){
944 db_multi_exec(
945 "DELETE FROM concealed;\n"
946

Keyboard Shortcuts

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