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