0d0f080b0dfed63f0a17cf48c3ff9a7b1db720a6
[citadel.git] / webcit / crypto.c
1 // Copyright (c) 1996-2021 by the citadel.org team
2 //
3 // This program is open source software.  You can redistribute it and/or
4 // modify it under the terms of the GNU General Public License, version 3.
5 // 
6 // This program is distributed in the hope that it will be useful,
7 // but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9 // GNU General Public License for more details.
10
11 #include "sysdep.h"
12 #ifdef HAVE_OPENSSL
13
14 #include "webcit.h"
15 #include "webserver.h"
16
17 /* where to find the keys */
18 #define CTDL_CRYPTO_DIR         ctdl_key_dir
19 #define CTDL_KEY_PATH           file_crpt_file_key
20 #define CTDL_CSR_PATH           file_crpt_file_csr
21 #define CTDL_CER_PATH           file_crpt_file_cer
22 #define SIGN_DAYS               3650                    // how long our self-signed certificate should live
23
24 SSL_CTX *ssl_ctx;               // Global SSL context
25 char *ssl_cipher_list = DEFAULT_SSL_CIPHER_LIST;
26
27 pthread_key_t ThreadSSL;        // Per-thread SSL context
28
29 void shutdown_ssl(void) {
30         ERR_free_strings();
31 }
32
33
34 void generate_key(char *keyfilename) {
35         int ret = 0;
36         RSA *rsa = NULL;
37         BIGNUM *bne = NULL;
38         int bits = 2048;
39         unsigned long e = RSA_F4;
40         FILE *fp;
41
42         if (access(keyfilename, R_OK) == 0) {           // We already have a key -- don't generate a new one.
43                 return;
44         }
45
46         syslog(LOG_INFO, "crypto: generating RSA key pair");
47  
48         // generate rsa key
49         bne = BN_new();
50         ret = BN_set_word(bne,e);
51         if (ret != 1) {
52                 goto free_all;
53         }
54  
55         rsa = RSA_new();
56         ret = RSA_generate_key_ex(rsa, bits, bne, NULL);
57         if (ret != 1) {
58                 goto free_all;
59         }
60
61         // write the key file
62         fp = fopen(keyfilename, "w");
63         if (fp != NULL) {
64                 chmod(file_crpt_file_key, 0600);
65                 if (PEM_write_RSAPrivateKey(fp, // the file
66                                         rsa,    // the key
67                                         NULL,   // no enc
68                                         NULL,   // no passphrase
69                                         0,      // no passphrase
70                                         NULL,   // no callback
71                                         NULL    // no callback
72                 ) != 1) {
73                         syslog(LOG_ERR, "crypto: cannot write key: %s", ERR_reason_error_string(ERR_get_error()));
74                         unlink(keyfilename);
75                 }
76                 fclose(fp);
77         }
78
79         // Free the memory we used
80 free_all:
81         RSA_free(rsa);
82         BN_free(bne);
83 }
84
85
86 // initialize ssl engine, load certs and initialize openssl internals
87 void init_ssl(void) {
88         const SSL_METHOD *ssl_method;
89         RSA *rsa=NULL;
90         X509_REQ *req = NULL;
91         X509 *cer = NULL;
92         EVP_PKEY *pk = NULL;
93         EVP_PKEY *req_pkey = NULL;
94         X509_NAME *name = NULL;
95         FILE *fp;
96         char buf[SIZ];
97         int rv = 0;
98
99 #ifndef OPENSSL_NO_EGD
100         if (!access("/var/run/egd-pool", F_OK)) {
101                 RAND_egd("/var/run/egd-pool");
102         }
103 #endif
104
105         if (!RAND_status()) {
106                 syslog(LOG_WARNING, "PRNG not adequately seeded, won't do SSL/TLS");
107                 return;
108         }
109
110         // Initialize SSL transport layer
111         SSL_library_init();
112         SSL_load_error_strings();
113         ssl_method = SSLv23_server_method();
114         if (!(ssl_ctx = SSL_CTX_new(ssl_method))) {
115                 syslog(LOG_WARNING, "SSL_CTX_new failed: %s", ERR_reason_error_string(ERR_get_error()));
116                 return;
117         }
118
119         syslog(LOG_INFO, "Requesting cipher list: %s", ssl_cipher_list);
120         if (!(SSL_CTX_set_cipher_list(ssl_ctx, ssl_cipher_list))) {
121                 syslog(LOG_WARNING, "SSL_CTX_set_cipher_list failed: %s", ERR_reason_error_string(ERR_get_error()));
122                 return;
123         }
124
125         CRYPTO_set_locking_callback(ssl_lock);
126         CRYPTO_set_id_callback(id_callback);
127
128         // Get our certificates in order.
129         // First, create the key/cert directory if it's not there already...
130         mkdir(CTDL_CRYPTO_DIR, 0700);
131
132         // Before attempting to generate keys/certificates, first try
133         // link to them from the Citadel server if it's on the same host.
134         // We ignore any error return because it either meant that there
135         // was nothing in Citadel to link from (in which case we just
136         // generate new files) or the target files already exist (which
137         // is not fatal either).
138         if (!strcasecmp(ctdlhost, "uds")) {
139                 sprintf(buf, "%s/keys/citadel.key", ctdlport);
140                 rv = symlink(buf, CTDL_KEY_PATH);
141                 if (!rv) {
142                         syslog(LOG_DEBUG, "%s", strerror(errno));
143                 }
144                 sprintf(buf, "%s/keys/citadel.csr", ctdlport);
145                 rv = symlink(buf, CTDL_CSR_PATH);
146                 if (!rv) {
147                         syslog(LOG_DEBUG, "%s", strerror(errno));
148                 }
149                 sprintf(buf, "%s/keys/citadel.cer", ctdlport);
150                 rv = symlink(buf, CTDL_CER_PATH);
151                 if (!rv) {
152                         syslog(LOG_DEBUG, "%s", strerror(errno));
153                 }
154         }
155
156         // If we still don't have a private key, generate one.
157         generate_key(CTDL_KEY_PATH);
158
159         // If there is no certificate file on disk, we will be generating a self-signed certificate
160         // in the next step.  Therefore, if we have neither a CSR nor a certificate, generate
161         // the CSR in this step so that the next step may commence.
162         if ( (access(CTDL_CER_PATH, R_OK) != 0) && (access(CTDL_CSR_PATH, R_OK) != 0) ) {
163                 syslog(LOG_INFO, "Generating a certificate signing request.");
164
165                 // Read our key from the file.  No, we don't just keep this
166                 // in memory from the above key-generation function, because
167                 // there is the possibility that the key was already on disk
168                 // and we didn't just generate it now.
169                 fp = fopen(CTDL_KEY_PATH, "r");
170                 if (fp) {
171                         rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
172                         fclose(fp);
173                 }
174
175                 if (rsa) {
176                         // Create a public key from the private key
177                         if (pk=EVP_PKEY_new(), pk != NULL) {
178                                 EVP_PKEY_assign_RSA(pk, rsa);
179                                 if (req = X509_REQ_new(), req != NULL) {
180                                         const char *env;
181                                         // Set the public key
182                                         X509_REQ_set_pubkey(req, pk);
183                                         X509_REQ_set_version(req, 0L);
184
185                                         name = X509_REQ_get_subject_name(req);
186
187                                         // Tell it who we are
188                                         env = getenv("O");
189                                         if (env == NULL) {
190                                                 env = "Organization name";
191                                         }
192
193                                         X509_NAME_add_entry_by_txt(
194                                                 name, "O",
195                                                 MBSTRING_ASC, 
196                                                 (unsigned char*)env, 
197                                                 -1, -1, 0
198                                         );
199
200                                         env = getenv("OU");
201                                         if (env == NULL) {
202                                                 env = "Citadel server";
203                                         }
204
205                                         X509_NAME_add_entry_by_txt(
206                                                 name, "OU",
207                                                 MBSTRING_ASC, 
208                                                 (unsigned char*)env, 
209                                                 -1, -1, 0
210                                         );
211
212                                         env = getenv("CN");
213                                         if (env == NULL)
214                                                 env = "*";
215
216                                         X509_NAME_add_entry_by_txt(
217                                                 name, "CN",
218                                                 MBSTRING_ASC, 
219                                                 (unsigned char*)env,
220                                                 -1, -1, 0
221                                         );
222                                 
223                                         X509_REQ_set_subject_name(req, name);
224
225                                         // Sign the CSR
226                                         if (!X509_REQ_sign(req, pk, EVP_md5())) {
227                                                 syslog(LOG_WARNING, "X509_REQ_sign(): error");
228                                         }
229                                         else {
230                                                 // Write it to disk.
231                                                 fp = fopen(CTDL_CSR_PATH, "w");
232                                                 if (fp != NULL) {
233                                                         chmod(CTDL_CSR_PATH, 0600);
234                                                         PEM_write_X509_REQ(fp, req);
235                                                         fclose(fp);
236                                                 }
237                                                 else {
238                                                         syslog(LOG_WARNING, "Cannot write key: %s", CTDL_CSR_PATH);
239                                                         ShutDownWebcit();
240                                                         exit(0);
241                                                 }
242                                         }
243
244                                         X509_REQ_free(req);
245                                 }
246                         }
247                         RSA_free(rsa);
248                 }
249                 else {
250                         syslog(LOG_WARNING, "Unable to read private key.");
251                 }
252         }
253
254         // Generate a self-signed certificate if we don't have one.
255         if (access(CTDL_CER_PATH, R_OK) != 0) {
256                 syslog(LOG_INFO, "Generating a self-signed certificate.\n");
257
258                 // Same deal as before: always read the key from disk because
259                 // it may or may not have just been generated.
260                 fp = fopen(CTDL_KEY_PATH, "r");
261                 if (fp) {
262                         rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
263                         fclose(fp);
264                 }
265
266                 // This also holds true for the CSR.
267                 req = NULL;
268                 cer = NULL;
269                 pk = NULL;
270                 if (rsa) {
271                         if (pk=EVP_PKEY_new(), pk != NULL) {
272                                 EVP_PKEY_assign_RSA(pk, rsa);
273                         }
274
275                         fp = fopen(CTDL_CSR_PATH, "r");
276                         if (fp) {
277                                 req = PEM_read_X509_REQ(fp, NULL, NULL, NULL);
278                                 fclose(fp);
279                         }
280
281                         if (req) {
282                                 if (cer = X509_new(), cer != NULL) {
283
284                                         ASN1_INTEGER_set(X509_get_serialNumber(cer), 0);
285                                         X509_set_issuer_name(cer, X509_REQ_get_subject_name(req));
286                                         X509_set_subject_name(cer, X509_REQ_get_subject_name(req));
287                                         X509_gmtime_adj(X509_get_notBefore(cer), 0);
288                                         X509_gmtime_adj(X509_get_notAfter(cer),(long)60*60*24*SIGN_DAYS);
289
290                                         req_pkey = X509_REQ_get_pubkey(req);
291                                         X509_set_pubkey(cer, req_pkey);
292                                         EVP_PKEY_free(req_pkey);
293                                         
294                                         // Sign the cert
295                                         if (!X509_sign(cer, pk, EVP_md5())) {
296                                                 syslog(LOG_WARNING, "X509_sign(): error");
297                                         }
298                                         else {
299                                                 // Write it to disk.
300                                                 fp = fopen(CTDL_CER_PATH, "w");
301                                                 if (fp != NULL) {
302                                                         chmod(CTDL_CER_PATH, 0600);
303                                                         PEM_write_X509(fp, cer);
304                                                         fclose(fp);
305                                                 }
306                                                 else {
307                                                         syslog(LOG_WARNING, "Cannot write key: %s", CTDL_CER_PATH);
308                                                         ShutDownWebcit();
309                                                         exit(0);
310                                                 }
311                                         }
312                                         X509_free(cer);
313                                 }
314                         }
315                         RSA_free(rsa);
316                 }
317         }
318
319         // Now try to bind to the key and certificate.
320         // Note that we use SSL_CTX_use_certificate_chain_file() which allows
321         // the certificate file to contain intermediate certificates.
322         SSL_CTX_use_certificate_chain_file(ssl_ctx, CTDL_CER_PATH);
323         SSL_CTX_use_PrivateKey_file(ssl_ctx, CTDL_KEY_PATH, SSL_FILETYPE_PEM);
324         if ( !SSL_CTX_check_private_key(ssl_ctx) ) {
325                 syslog(LOG_WARNING, "crypto: cannot install certificate: %s", ERR_reason_error_string(ERR_get_error()));
326         }
327 }
328
329
330 // starts SSL/TLS encryption for the current session.
331 int starttls(int sock) {
332         int retval, bits, alg_bits;
333         SSL *newssl;
334
335         pthread_setspecific(ThreadSSL, NULL);
336
337         if (!ssl_ctx) {
338                 return(1);
339         }
340         if (!(newssl = SSL_new(ssl_ctx))) {
341                 syslog(LOG_WARNING, "SSL_new failed: %s", ERR_reason_error_string(ERR_get_error()));
342                 return(2);
343         }
344         if (!(SSL_set_fd(newssl, sock))) {
345                 syslog(LOG_WARNING, "SSL_set_fd failed: %s", ERR_reason_error_string(ERR_get_error()));
346                 SSL_free(newssl);
347                 return(3);
348         }
349         retval = SSL_accept(newssl);
350         if (retval < 1) {
351                 // Can't notify the client of an error here; they will
352                 // discover the problem at the SSL layer and should
353                 // revert to unencrypted communications.
354                 long errval;
355                 const char *ssl_error_reason = NULL;
356
357                 errval = SSL_get_error(newssl, retval);
358                 ssl_error_reason = ERR_reason_error_string(ERR_get_error());
359                 if (ssl_error_reason == NULL) {
360                         syslog(LOG_WARNING, "SSL_accept failed: errval=%ld, retval=%d %s", errval, retval, strerror(errval));
361                 }
362                 else {
363                         syslog(LOG_WARNING, "SSL_accept failed: %s", ssl_error_reason);
364                 }
365                 sleeeeeeeeeep(1);
366                 retval = SSL_accept(newssl);
367         }
368         if (retval < 1) {
369                 long errval;
370                 const char *ssl_error_reason = NULL;
371
372                 errval = SSL_get_error(newssl, retval);
373                 ssl_error_reason = ERR_reason_error_string(ERR_get_error());
374                 if (ssl_error_reason == NULL) {
375                         syslog(LOG_WARNING, "SSL_accept failed: errval=%ld, retval=%d (%s)", errval, retval, strerror(errval));
376                 }
377                 else {
378                         syslog(LOG_WARNING, "SSL_accept failed: %s", ssl_error_reason);
379                 }
380                 SSL_free(newssl);
381                 newssl = NULL;
382                 return(4);
383         }
384         else {
385                 syslog(LOG_INFO, "SSL_accept success");
386         }
387         /*r = */BIO_set_close(SSL_get_rbio(newssl), BIO_NOCLOSE);
388         bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(newssl), &alg_bits);
389         syslog(LOG_INFO, "SSL/TLS using %s on %s (%d of %d bits)",
390                 SSL_CIPHER_get_name(SSL_get_current_cipher(newssl)),
391                 SSL_CIPHER_get_version(SSL_get_current_cipher(newssl)),
392                 bits, alg_bits);
393
394         pthread_setspecific(ThreadSSL, newssl);
395         syslog(LOG_INFO, "SSL started");
396         return(0);
397 }
398
399
400 // shuts down the TLS connection
401 //
402 // WARNING:  This may make your session vulnerable to a known plaintext
403 // attack in the current implmentation.
404 void endtls(void) {
405
406         if (THREADSSL == NULL) {
407                 return;
408         }
409
410         syslog(LOG_INFO, "Ending SSL/TLS");
411         SSL_shutdown(THREADSSL);
412         SSL_get_SSL_CTX(THREADSSL);
413         SSL_free(THREADSSL);
414         pthread_setspecific(ThreadSSL, NULL);
415 }
416
417
418 // Send binary data to the client encrypted.
419 int client_write_ssl(const StrBuf *Buf) {
420         const char *buf;
421         int retval;
422         int nremain;
423         long nbytes;
424         char junk[1];
425
426         if (THREADSSL == NULL) return -1;
427
428         nbytes = nremain = StrLength(Buf);
429         buf = ChrPtr(Buf);
430
431         while (nremain > 0) {
432                 if (SSL_want_write(THREADSSL)) {
433                         if ((SSL_read(THREADSSL, junk, 0)) < 1) {
434                                 syslog(LOG_WARNING, "SSL_read in client_write: %s", ERR_reason_error_string(ERR_get_error()));
435                         }
436                 }
437                 retval = SSL_write(THREADSSL, &buf[nbytes - nremain], nremain);
438                 if (retval < 1) {
439                         long errval;
440
441                         errval = SSL_get_error(THREADSSL, retval);
442                         if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
443                                 sleeeeeeeeeep(1);
444                                 continue;
445                         }
446                         syslog(LOG_WARNING, "SSL_write got error %ld, ret %d", errval, retval);
447                         if (retval == -1) {
448                                 syslog(LOG_WARNING, "errno is %d\n", errno);
449                         }
450                         endtls();
451                         return -1;
452                 }
453                 nremain -= retval;
454         }
455         return 0;
456 }
457
458
459 // read data from the encrypted layer.
460 int client_read_sslbuffer(StrBuf *buf, int timeout) {
461         char sbuf[16384];       // OpenSSL communicates in 16k blocks, so let's speak its native tongue.
462         int rlen;
463         char junk[1];
464         SSL *pssl = THREADSSL;
465
466         if (pssl == NULL) return(-1);
467
468         while (1) {
469                 if (SSL_want_read(pssl)) {
470                         if ((SSL_write(pssl, junk, 0)) < 1) {
471                                 syslog(LOG_WARNING, "SSL_write in client_read");
472                         }
473                 }
474                 rlen = SSL_read(pssl, sbuf, sizeof(sbuf));
475                 if (rlen < 1) {
476                         long errval;
477
478                         errval = SSL_get_error(pssl, rlen);
479                         if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
480                                 sleeeeeeeeeep(1);
481                                 continue;
482                         }
483                         syslog(LOG_WARNING, "SSL_read got error %ld", errval);
484                         endtls();
485                         return (-1);
486                 }
487                 StrBufAppendBufPlain(buf, sbuf, rlen, 0);
488                 return rlen;
489         }
490         return (0);
491 }
492
493 #endif                          /* HAVE_OPENSSL */