misc style cleanup
[citadel.git] / citadel / server / modules / crypto / serv_crypto.c
1 // Copyright (c) 1987-2022 by the citadel.org team
2 // This program is open source software.  Use, duplication, or disclosure is subject to the GNU General Public License v3.
3
4 #include <string.h>
5 #include <unistd.h>
6 #include <sys/stat.h>
7 #include <sys/types.h>
8 #include "../../sysdep.h"
9
10 #ifdef HAVE_OPENSSL
11 #include <openssl/ssl.h>
12 #include <openssl/err.h>
13 #include <openssl/rand.h>
14 #endif
15
16 #include <time.h>
17
18 #ifdef HAVE_PTHREAD_H
19 #include <pthread.h>
20 #endif
21
22 #ifdef HAVE_SYS_SELECT_H
23 #include <sys/select.h>
24 #endif
25
26 #include <stdio.h>
27 #include <libcitadel.h>
28 #include "../../server.h"
29 #include "serv_crypto.h"
30 #include "../../sysdep_decls.h"
31 #include "../../citadel_defs.h"
32 #include "../../config.h"
33 #include "../../ctdl_module.h"
34
35 #ifdef HAVE_OPENSSL
36
37 SSL_CTX *ssl_ctx = NULL;                // This SSL context is used for all sessions.
38 char *ssl_cipher_list = CIT_CIPHERS;
39
40 // If a private key does not exist, generate one now.
41 void generate_key(char *keyfilename) {
42         int ret = 0;
43         RSA *rsa = NULL;
44         BIGNUM *bne = NULL;
45         int bits = 2048;
46         unsigned long e = RSA_F4;
47         FILE *fp;
48
49         if (access(keyfilename, R_OK) == 0) {   // Already have one.
50                 syslog(LOG_INFO, "crypto: %s exists and is readable", keyfilename);
51                 return;
52         }
53
54         syslog(LOG_INFO, "crypto: generating RSA key pair");
55  
56         // generate rsa key
57         bne = BN_new();
58         ret = BN_set_word(bne,e);
59         if (ret != 1) {
60                 goto free_all;
61         }
62  
63         rsa = RSA_new();
64         ret = RSA_generate_key_ex(rsa, bits, bne, NULL);
65         if (ret != 1) {
66                 goto free_all;
67         }
68
69         // write the key file
70         fp = fopen(keyfilename, "w");
71         if (fp != NULL) {
72                 chmod(keyfilename, 0600);
73                 if (PEM_write_RSAPrivateKey(fp, // the file
74                                         rsa,    // the key
75                                         NULL,   // no enc
76                                         NULL,   // no passphrase
77                                         0,      // no passphrase
78                                         NULL,   // no callback
79                                         NULL    // no callback
80                 ) != 1) {
81                         syslog(LOG_ERR, "crypto: cannot write key: %s", ERR_reason_error_string(ERR_get_error()));
82                         unlink(keyfilename);
83                 }
84                 fclose(fp);
85         }
86
87         // free the memory we used
88 free_all:
89         RSA_free(rsa);
90         BN_free(bne);
91 }
92
93
94 // If a certificate does not exist, generate a self-signed one now.
95 void generate_certificate(char *keyfilename, char *certfilename) {
96         RSA *private_key = NULL;
97         EVP_PKEY *public_key = NULL;
98         X509_REQ *certificate_signing_request = NULL;
99         X509_NAME *name = NULL;
100         X509 *certificate = NULL;
101         FILE *fp;
102
103         if (access(certfilename, R_OK) == 0) {                  // already have one.
104                 syslog(LOG_INFO, "crypto: %s exists and is readable", certfilename);
105                 return;
106         }
107
108         syslog(LOG_INFO, "crypto: generating a self-signed certificate");
109
110         // Read in our private key.
111         fp = fopen(keyfilename, "r");
112         if (fp) {
113                 private_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
114                 fclose(fp);
115         }
116
117         if (!private_key) {
118                 syslog(LOG_ERR, "crypto: cannot read the private key");
119                 return;
120         }
121
122         // Create a public key from the private key
123         public_key = EVP_PKEY_new();
124         if (!public_key) {
125                 syslog(LOG_ERR, "crypto: cannot allocate public key");
126                 RSA_free(private_key);
127                 return;
128         }
129         EVP_PKEY_assign_RSA(public_key, private_key);
130
131         // Create a certificate signing request
132         certificate_signing_request = X509_REQ_new();
133         if (!certificate_signing_request) {
134                 syslog(LOG_ERR, "crypto: cannot allocate certificate signing request");
135                 RSA_free(private_key);
136                 EVP_PKEY_free(public_key);
137                 return;
138         }
139
140         // Assign the public key to the certificate signing request
141         X509_REQ_set_pubkey(certificate_signing_request, public_key);
142         X509_REQ_set_version(certificate_signing_request, 0L);
143
144         // Tell it who we are
145         name = X509_REQ_get_subject_name(certificate_signing_request);
146         X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned const char *)"ZZ", -1, -1, 0);
147         X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, (unsigned const char *)"The World", -1, -1, 0);
148         X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, (unsigned const char *)"My Location", -1, -1, 0);
149         X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned const char *)"Generic certificate", -1, -1, 0);
150         X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, (unsigned const char *)"Citadel server", -1, -1, 0);
151         X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned const char *)"*", -1, -1, 0);
152         X509_REQ_set_subject_name(certificate_signing_request, name);
153
154         // Sign the CSR
155         //if (!X509_REQ_sign(certificate_signing_request, public_key, EVP_md5())) {
156         if (!X509_REQ_sign(certificate_signing_request, public_key, EVP_md5()) , 0) {
157                 syslog(LOG_ERR, "crypto: X509_REQ_sign(): error");
158                 X509_REQ_free(certificate_signing_request);
159                 RSA_free(private_key);
160                 EVP_PKEY_free(public_key);
161                 return;
162         }
163
164         // Generate the self-signed certificate
165         certificate = X509_new();
166         if (!certificate) {
167                 syslog(LOG_ERR, "crypto: cannot allocate X.509 certificate");
168                 X509_REQ_free(certificate_signing_request);
169                 RSA_free(private_key);
170                 EVP_PKEY_free(public_key);
171                 return;
172         }
173
174         ASN1_INTEGER_set(X509_get_serialNumber(certificate), 0);
175         X509_set_issuer_name(certificate, X509_REQ_get_subject_name(certificate_signing_request));
176         X509_set_subject_name(certificate, X509_REQ_get_subject_name(certificate_signing_request));
177         X509_gmtime_adj(X509_get_notBefore(certificate), 0);
178         X509_gmtime_adj(X509_get_notAfter(certificate), (long)60*60*24*SIGN_DAYS);
179         X509_set_pubkey(certificate, public_key);
180         X509_REQ_free(certificate_signing_request);             // We're done with the CSR; free it
181
182         // Finally, sign the certificate with our private key.
183         if (!X509_sign(certificate, public_key, EVP_md5())) {
184                 syslog(LOG_ERR, "crypto: X509_sign() error");
185                 X509_free(certificate);
186                 RSA_free(private_key);
187                 EVP_PKEY_free(public_key);
188                 return;
189         }
190
191         // Write the certificate to disk
192         fp = fopen(certfilename, "w");
193         if (fp != NULL) {
194                 chmod(certfilename, 0600);
195                 PEM_write_X509(fp, certificate);
196                 fclose(fp);
197         }
198         else {
199                 syslog(LOG_ERR, "crypto: %s: %m", certfilename);
200         }
201
202         X509_free(certificate);
203         EVP_PKEY_free(public_key);
204         // do not RSA_free(private_key); because it was freed by EVP_PKEY_free() above
205 }
206
207
208 // Set the private key and certificate chain for the global SSL Context.
209 // This is called during initialization, and can be called again later if the certificate changes.
210 void bind_to_key_and_certificate(void) {
211
212         SSL_CTX *old_ctx = NULL;
213         SSL_CTX *new_ctx = NULL;
214
215         const SSL_METHOD *method = SSLv23_server_method();
216         if (!method) {
217                 syslog(LOG_ERR, "crypto: SSLv23_server_method() failed: %s", ERR_reason_error_string(ERR_get_error()));
218                 return;
219         }
220
221         new_ctx = SSL_CTX_new(method);
222         if (!new_ctx) {
223                 syslog(LOG_ERR, "crypto: SSL_CTX_new failed: %s", ERR_reason_error_string(ERR_get_error()));
224                 return;
225         }
226
227         if (!(SSL_CTX_set_cipher_list(new_ctx, ssl_cipher_list))) {
228                 syslog(LOG_ERR, "crypto: SSL_CTX_set_cipher_list failed: %s", ERR_reason_error_string(ERR_get_error()));
229                 return;
230         }
231
232         syslog(LOG_DEBUG, "crypto: using certificate chain %s", file_crpt_file_cer);
233         if (!SSL_CTX_use_certificate_chain_file(new_ctx, file_crpt_file_cer)) {
234                 syslog(LOG_ERR, "crypto: SSL_CTX_use_certificate_chain_file failed: %s", ERR_reason_error_string(ERR_get_error()));
235                 return;
236         }
237
238         syslog(LOG_DEBUG, "crypto: using private key %s", file_crpt_file_key);
239         if (!SSL_CTX_use_PrivateKey_file(new_ctx, file_crpt_file_key, SSL_FILETYPE_PEM)) {
240                 syslog(LOG_ERR, "crypto: SSL_CTX_use_PrivateKey_file failed: %s", ERR_reason_error_string(ERR_get_error()));
241                 return;
242         }
243
244         old_ctx = ssl_ctx;
245         ssl_ctx = new_ctx;              // All future binds will use the new certificate
246
247         if (old_ctx != NULL) {
248                 sleep(1);               // Hopefully wait until all in-progress binds to the old certificate have completed
249                 SSL_CTX_free(old_ctx);
250         }
251 }
252
253
254 // Check the modification time of the key and certificate files.  Reload if either one changed.
255 void update_key_and_cert_if_needed(void) {
256         static time_t previous_mtime = 0;
257         struct stat keystat;
258         struct stat certstat;
259
260         if (stat(file_crpt_file_key, &keystat) != 0) {
261                 syslog(LOG_ERR, "%s: %s", file_crpt_file_key, strerror(errno));
262                 return;
263         }
264         if (stat(file_crpt_file_cer, &certstat) != 0) {
265                 syslog(LOG_ERR, "%s: %s", file_crpt_file_cer, strerror(errno));
266                 return;
267         }
268
269         if ((keystat.st_mtime + certstat.st_mtime) != previous_mtime) {
270                 begin_critical_section(S_OPENSSL);
271                 bind_to_key_and_certificate();
272                 end_critical_section(S_OPENSSL);
273                 previous_mtime = keystat.st_mtime + certstat.st_mtime;
274         }
275 }
276
277
278 // Initialize the TLS subsystem.
279 void init_ssl(void) {
280
281         // Initialize the OpenSSL library
282         SSL_library_init();
283         SSL_load_error_strings();
284
285         // Load (or generate) a key and certificate
286         mkdir(ctdl_key_dir, 0700);                                      // If the keys directory does not exist, create it
287         generate_key(file_crpt_file_key);                               // If a private key does not exist, create it
288         generate_certificate(file_crpt_file_key, file_crpt_file_cer);   // If a certificate does not exist, create it
289         bind_to_key_and_certificate();                                  // Load key and cert from disk, and bind to them.
290
291         // Register some Citadel protocol commands for dealing with encrypted sessions
292         CtdlRegisterProtoHook(cmd_stls, "STLS", "Start TLS session");
293         CtdlRegisterProtoHook(cmd_gtls, "GTLS", "Get TLS session status");
294         CtdlRegisterSessionHook(endtls, EVT_STOP, PRIO_STOP + 10);
295 }
296
297
298 // client_write_ssl() Send binary data to the client encrypted.
299 void client_write_ssl(const char *buf, int nbytes) {
300         int retval;
301         int nremain;
302         char junk[1];
303
304         nremain = nbytes;
305
306         while (nremain > 0) {
307                 if (SSL_want_write(CC->ssl)) {
308                         if ((SSL_read(CC->ssl, junk, 0)) < 1) {
309                                 syslog(LOG_DEBUG, "crypto: SSL_read in client_write: %s", ERR_reason_error_string(ERR_get_error()));
310                         }
311                 }
312                 retval =
313                     SSL_write(CC->ssl, &buf[nbytes - nremain], nremain);
314                 if (retval < 1) {
315                         long errval;
316
317                         errval = SSL_get_error(CC->ssl, retval);
318                         if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
319                                 sleep(1);
320                                 continue;
321                         }
322                         syslog(LOG_DEBUG, "crypto: SSL_write got error %ld, ret %d", errval, retval);
323                         if (retval == -1) {
324                                 syslog(LOG_DEBUG, "crypto: errno is %d", errno);
325                         }
326                         endtls();
327                         client_write(&buf[nbytes - nremain], nremain);
328                         return;
329                 }
330                 nremain -= retval;
331         }
332 }
333
334
335 // read data from the encrypted layer.
336 int client_read_sslbuffer(StrBuf *buf, int timeout) {
337         char sbuf[16384];       // OpenSSL communicates in 16k blocks, so let's speak its native tongue.
338         int rlen;
339         char junk[1];
340         SSL *pssl = CC->ssl;
341
342         if (pssl == NULL) return(-1);
343
344         while (1) {
345                 if (SSL_want_read(pssl)) {
346                         if ((SSL_write(pssl, junk, 0)) < 1) {
347                                 syslog(LOG_DEBUG, "crypto: SSL_write in client_read");
348                         }
349                 }
350                 rlen = SSL_read(pssl, sbuf, sizeof(sbuf));
351                 if (rlen < 1) {
352                         long errval;
353
354                         errval = SSL_get_error(pssl, rlen);
355                         if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
356                                 sleep(1);
357                                 continue;
358                         }
359                         syslog(LOG_DEBUG, "crypto: SSL_read got error %ld", errval);
360                         endtls();
361                         return (-1);
362                 }
363                 StrBufAppendBufPlain(buf, sbuf, rlen, 0);
364                 return rlen;
365         }
366         return (0);
367 }
368
369
370 int client_readline_sslbuffer(StrBuf *Line, StrBuf *IOBuf, const char **Pos, int timeout) {
371         const char *pos = NULL;
372         const char *pLF;
373         int len, rlen;
374         int nSuccessLess = 0;
375         const char *pch = NULL;
376         
377         if ((Line == NULL) || (Pos == NULL) || (IOBuf == NULL)) {
378                 if (Pos != NULL) {
379                         *Pos = NULL;
380                 }
381                 return -1;
382         }
383
384         pos = *Pos;
385         if ((StrLength(IOBuf) > 0) && (pos != NULL) && (pos < ChrPtr(IOBuf) + StrLength(IOBuf))) {
386                 pch = pos;
387                 pch = strchr(pch, '\n');
388                 
389                 if (pch == NULL) {
390                         StrBufAppendBufPlain(Line, pos, StrLength(IOBuf) - (pos - ChrPtr(IOBuf)), 0);
391                         FlushStrBuf(IOBuf);
392                         *Pos = NULL;
393                 }
394                 else {
395                         int n = 0;
396                         if ((pch > ChrPtr(IOBuf)) && (*(pch - 1) == '\r')) {
397                                 n = 1;
398                         }
399                         StrBufAppendBufPlain(Line, pos, (pch - pos - n), 0);
400
401                         if (StrLength(IOBuf) <= (pch - ChrPtr(IOBuf) + 1)) {
402                                 FlushStrBuf(IOBuf);
403                                 *Pos = NULL;
404                         }
405                         else {
406                                 *Pos = pch + 1;
407                         }
408                         return StrLength(Line);
409                 }
410         }
411
412         pLF = NULL;
413         while ((nSuccessLess < timeout) && (pLF == NULL) && (CC->ssl != NULL)) {
414
415                 rlen = client_read_sslbuffer(IOBuf, timeout);
416                 if (rlen < 1) {
417                         return -1;
418                 }
419                 else if (rlen > 0) {
420                         pLF = strchr(ChrPtr(IOBuf), '\n');
421                 }
422         }
423         *Pos = NULL;
424         if (pLF != NULL) {
425                 pos = ChrPtr(IOBuf);
426                 len = pLF - pos;
427                 if (len > 0 && (*(pLF - 1) == '\r') )
428                         len --;
429                 StrBufAppendBufPlain(Line, pos, len, 0);
430                 if (pLF + 1 >= ChrPtr(IOBuf) + StrLength(IOBuf)) {
431                         FlushStrBuf(IOBuf);
432                 }
433                 else  {
434                         *Pos = pLF + 1;
435                 }
436                 return StrLength(Line);
437         }
438         return -1;
439 }
440
441
442 int client_read_sslblob(StrBuf *Target, long bytes, int timeout) {
443         long baselen;
444         long RemainRead;
445         int retval = 0;
446
447         baselen = StrLength(Target);
448
449         if (StrLength(CC->RecvBuf.Buf) > 0) {
450                 long RemainLen;
451                 long TotalLen;
452                 const char *pchs;
453
454                 if (CC->RecvBuf.ReadWritePointer == NULL) {
455                         CC->RecvBuf.ReadWritePointer = ChrPtr(CC->RecvBuf.Buf);
456                 }
457                 pchs = ChrPtr(CC->RecvBuf.Buf);
458                 TotalLen = StrLength(CC->RecvBuf.Buf);
459                 RemainLen = TotalLen - (pchs - CC->RecvBuf.ReadWritePointer);
460                 if (RemainLen > bytes) {
461                         RemainLen = bytes;
462                 }
463                 if (RemainLen > 0) {
464                         StrBufAppendBufPlain(Target, CC->RecvBuf.ReadWritePointer, RemainLen, 0);
465                         CC->RecvBuf.ReadWritePointer += RemainLen;
466                 }
467                 if ((ChrPtr(CC->RecvBuf.Buf) + StrLength(CC->RecvBuf.Buf)) <= CC->RecvBuf.ReadWritePointer) {
468                         CC->RecvBuf.ReadWritePointer = NULL;
469                         FlushStrBuf(CC->RecvBuf.Buf);
470                 }
471         }
472
473         if (StrLength(Target) >= bytes + baselen) {
474                 return 1;
475         }
476
477         CC->RecvBuf.ReadWritePointer = NULL;
478
479         while ((StrLength(Target) < bytes + baselen) && (retval >= 0)) {
480                 retval = client_read_sslbuffer(CC->RecvBuf.Buf, timeout);
481                 if (retval >= 0) {
482                         RemainRead = bytes - (StrLength (Target) - baselen);
483                         if (RemainRead < StrLength(CC->RecvBuf.Buf)) {
484                                 StrBufAppendBufPlain(
485                                         Target, 
486                                         ChrPtr(CC->RecvBuf.Buf), 
487                                         RemainRead, 0);
488                                 CC->RecvBuf.ReadWritePointer = ChrPtr(CC->RecvBuf.Buf) + RemainRead;
489                                 break;
490                         }
491                         StrBufAppendBuf(Target, CC->RecvBuf.Buf, 0);
492                         FlushStrBuf(CC->RecvBuf.Buf);
493                 }
494                 else {
495                         FlushStrBuf(CC->RecvBuf.Buf);
496                         return -1;
497         
498                 }
499         }
500         return 1;
501 }
502
503
504 // CtdlStartTLS() starts TLS encryption for the current session.  It
505 // must be supplied with pre-generated strings for responses of "ok," "no
506 // support for TLS," and "error" so that we can use this in any protocol.
507 void CtdlStartTLS(char *ok_response, char *nosup_response, char *error_response) {
508         int retval, bits, alg_bits;
509
510         if (CC->redirect_ssl) {
511                 syslog(LOG_ERR, "crypto: attempt to begin SSL on an already encrypted connection");
512                 if (error_response != NULL) {
513                         cprintf("%s", error_response);
514                 }
515                 return;
516         }
517
518         if (!ssl_ctx) {
519                 syslog(LOG_ERR, "crypto: SSL failed: ../../context.has not been initialized");
520                 if (nosup_response != NULL) {
521                         cprintf("%s", nosup_response);
522                 }
523                 return;
524         }
525
526         update_key_and_cert_if_needed();                // did someone update the key or cert?  if so, re-bind them
527
528         if (!(CC->ssl = SSL_new(ssl_ctx))) {
529                 syslog(LOG_ERR, "crypto: SSL_new failed: %s", ERR_reason_error_string(ERR_get_error()));
530                 if (error_response != NULL) {
531                         cprintf("%s", error_response);
532                 }
533                 return;
534         }
535         if (!(SSL_set_fd(CC->ssl, CC->client_socket))) {
536                 syslog(LOG_ERR, "crypto: SSL_set_fd failed: %s", ERR_reason_error_string(ERR_get_error()));
537                 SSL_free(CC->ssl);
538                 CC->ssl = NULL;
539                 if (error_response != NULL) {
540                         cprintf("%s", error_response);
541                 }
542                 return;
543         }
544         if (ok_response != NULL) {
545                 cprintf("%s", ok_response);
546         }
547         retval = SSL_accept(CC->ssl);
548         if (retval < 1) {
549                 // Can't notify the client of an error here; they will
550                 // discover the problem at the SSL layer and should
551                 // revert to unencrypted communications.
552                 syslog(LOG_ERR, "crypto: SSL_accept failed: %s", ERR_reason_error_string(ERR_get_error()));
553                 SSL_free(CC->ssl);
554                 CC->ssl = NULL;
555                 return;
556         }
557         bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(CC->ssl), &alg_bits);
558         syslog(LOG_INFO, "crypto: TLS using %s on %s (%d of %d bits)",
559                 SSL_CIPHER_get_name(SSL_get_current_cipher(CC->ssl)),
560                 SSL_CIPHER_get_version(SSL_get_current_cipher(CC->ssl)),
561                 bits, alg_bits
562         );
563         CC->redirect_ssl = 1;
564 }
565
566
567 // cmd_stls() starts TLS encryption for the current session
568 void cmd_stls(char *params) {
569         char ok_response[SIZ];
570         char nosup_response[SIZ];
571         char error_response[SIZ];
572
573         unbuffer_output();
574
575         sprintf(ok_response, "%d Begin TLS negotiation now\n", CIT_OK);
576         sprintf(nosup_response, "%d TLS not supported here\n", ERROR + CMD_NOT_SUPPORTED);
577         sprintf(error_response, "%d TLS negotiation error\n", ERROR + INTERNAL_ERROR);
578
579         CtdlStartTLS(ok_response, nosup_response, error_response);
580 }
581
582
583 // cmd_gtls() returns status info about the TLS connection
584 void cmd_gtls(char *params) {
585         int bits, alg_bits;
586
587         if (!CC->ssl || !CC->redirect_ssl) {
588                 cprintf("%d Session is not encrypted.\n", ERROR);
589                 return;
590         }
591         bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(CC->ssl), &alg_bits);
592         cprintf("%d %s|%s|%d|%d\n", CIT_OK,
593                 SSL_CIPHER_get_version(SSL_get_current_cipher(CC->ssl)),
594                 SSL_CIPHER_get_name(SSL_get_current_cipher(CC->ssl)),
595                 alg_bits, bits);
596 }
597
598
599 // endtls() shuts down the TLS connection
600 void endtls(void) {
601         if (!CC->ssl) {
602                 CC->redirect_ssl = 0;
603                 return;
604         }
605
606         syslog(LOG_INFO, "crypto: ending TLS on this session");
607         SSL_shutdown(CC->ssl);
608         SSL_free(CC->ssl);
609         CC->ssl = NULL;
610         CC->redirect_ssl = 0;
611 }
612
613 #endif  // HAVE_OPENSSL