* crypto.c: when an SSL connection errors out (and closes) unexpectedly
[citadel.git] / webcit / crypto.c
1 /* $Id$ */
2
3 #ifdef HAVE_OPENSSL
4
5
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <string.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <pthread.h>
12
13 #include <sys/time.h>
14 #include "webcit.h"
15 #include "webserver.h"
16
17 #define CTDL_CRYPTO_DIR         WEBCITDIR "/keys"
18 #define CTDL_KEY_PATH           CTDL_CRYPTO_DIR "/citadel.key"
19 #define CTDL_CSR_PATH           CTDL_CRYPTO_DIR "/citadel.csr"
20 #define CTDL_CER_PATH           CTDL_CRYPTO_DIR "/citadel.cer"
21 #define SIGN_DAYS               365
22
23
24 /* Shared Diffie-Hellman parameters */
25 #define DH_P            "1A74527AEE4EE2568E85D4FB2E65E18C9394B9C80C42507D7A6A0DBE9A9A54B05A9A96800C34C7AA5297095B69C88901EEFD127F969DCA26A54C0E0B5C5473EBAEB00957D2633ECAE3835775425DE66C0DE6D024DBB17445E06E6B0C78415E589B8814F08531D02FD43778451E7685541079CFFB79EF0D26EFEEBBB69D1E80383"
26 #define DH_G            "2"
27 #define DH_L            1024
28
29 SSL_CTX *ssl_ctx;               /* SSL context */
30 pthread_mutex_t **SSLCritters;  /* Things needing locking */
31
32 pthread_key_t ThreadSSL;        /* Per-thread SSL context */
33
34 static unsigned long id_callback(void)
35 {
36         return (unsigned long) pthread_self();
37 }
38
39
40 void init_ssl(void)
41 {
42         SSL_METHOD *ssl_method;
43         DH *dh;
44         RSA *rsa=NULL;
45         X509_REQ *req = NULL;
46         X509 *cer = NULL;
47         EVP_PKEY *pk = NULL;
48         EVP_PKEY *req_pkey = NULL;
49         X509_NAME *name = NULL;
50         FILE *fp;
51         char buf[SIZ];
52
53         if (!access("/var/run/egd-pool", F_OK))
54                 RAND_egd("/var/run/egd-pool");
55
56         if (!RAND_status()) {
57                 lprintf(3,
58                         "PRNG not adequately seeded, won't do SSL/TLS\n");
59                 return;
60         }
61         SSLCritters =
62             malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t *));
63         if (!SSLCritters) {
64                 lprintf(1, "citserver: can't allocate memory!!\n");
65                 /* Nothing's been initialized, just die */
66                 exit(1);
67         } else {
68                 int a;
69
70                 for (a = 0; a < CRYPTO_num_locks(); a++) {
71                         SSLCritters[a] = malloc(sizeof(pthread_mutex_t));
72                         if (!SSLCritters[a]) {
73                                 lprintf(1,
74                                         "citserver: can't allocate memory!!\n");
75                                 /* Nothing's been initialized, just die */
76                                 exit(1);
77                         }
78                         pthread_mutex_init(SSLCritters[a], NULL);
79                 }
80         }
81
82         /*
83          * Initialize SSL transport layer
84          */
85         SSL_library_init();
86         OpenSSL_add_all_algorithms();
87         SSL_load_error_strings();
88         ssl_method = SSLv2_server_method();
89         if (!(ssl_ctx = SSL_CTX_new(ssl_method))) {
90                 lprintf(3, "SSL_CTX_new failed: %s\n",
91                         ERR_reason_error_string(ERR_get_error()));
92                 return;
93         }
94
95         CRYPTO_set_locking_callback(ssl_lock);
96         CRYPTO_set_id_callback(id_callback);
97
98         /* Load DH parameters into the context */
99         dh = DH_new();
100         if (!dh) {
101                 lprintf(3, "init_ssl() can't allocate a DH object: %s\n",
102                         ERR_reason_error_string(ERR_get_error()));
103                 SSL_CTX_free(ssl_ctx);
104                 ssl_ctx = NULL;
105                 return;
106         }
107         if (!(BN_hex2bn(&(dh->p), DH_P))) {
108                 lprintf(3, "init_ssl() can't assign DH_P: %s\n",
109                         ERR_reason_error_string(ERR_get_error()));
110                 SSL_CTX_free(ssl_ctx);
111                 ssl_ctx = NULL;
112                 return;
113         }
114         if (!(BN_hex2bn(&(dh->g), DH_G))) {
115                 lprintf(3, "init_ssl() can't assign DH_G: %s\n",
116                         ERR_reason_error_string(ERR_get_error()));
117                 SSL_CTX_free(ssl_ctx);
118                 ssl_ctx = NULL;
119                 return;
120         }
121         dh->length = DH_L;
122         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
123         DH_free(dh);
124
125         /* Get our certificates in order.
126          * First, create the key/cert directory if it's not there already...
127          */
128         mkdir(CTDL_CRYPTO_DIR, 0700);
129
130         /*
131          * Before attempting to generate keys/certificates, first try
132          * link to them from the Citadel server if it's on the same host.
133          * We ignore any error return because it either meant that there
134          * was nothing in Citadel to link from (in which case we just
135          * generate new files) or the target files already exist (which
136          * is not fatal either).
137          */
138         if (!strcasecmp(ctdlhost, "uds")) {
139                 sprintf(buf, "%s/keys/citadel.key", ctdlport);
140                 symlink(buf, CTDL_KEY_PATH);
141                 sprintf(buf, "%s/keys/citadel.csr", ctdlport);
142                 symlink(buf, CTDL_CSR_PATH);
143                 sprintf(buf, "%s/keys/citadel.cer", ctdlport);
144                 symlink(buf, CTDL_CER_PATH);
145         }
146
147         /*
148          * If we still don't have a private key, generate one.
149          */
150         if (access(CTDL_KEY_PATH, R_OK) != 0) {
151                 lprintf(5, "Generating RSA key pair.\n");
152                 rsa = RSA_generate_key(1024,    /* modulus size */
153                                         65537,  /* exponent */
154                                         NULL,   /* no callback */
155                                         NULL);  /* no callback */
156                 if (rsa == NULL) {
157                         lprintf(3, "Key generation failed: %s\n",
158                                 ERR_reason_error_string(ERR_get_error()));
159                 }
160                 if (rsa != NULL) {
161                         fp = fopen(CTDL_KEY_PATH, "w");
162                         if (fp != NULL) {
163                                 chmod(CTDL_KEY_PATH, 0600);
164                                 if (PEM_write_RSAPrivateKey(fp, /* the file */
165                                                         rsa,    /* the key */
166                                                         NULL,   /* no enc */
167                                                         NULL,   /* no passphr */
168                                                         0,      /* no passphr */
169                                                         NULL,   /* no callbk */
170                                                         NULL    /* no callbk */
171                                 ) != 1) {
172                                         lprintf(3, "Cannot write key: %s\n",
173                                                 ERR_reason_error_string(ERR_get_error()));
174                                         unlink(CTDL_KEY_PATH);
175                                 }
176                                 fclose(fp);
177                         }
178                         RSA_free(rsa);
179                 }
180         }
181
182         /*
183          * Generate a CSR if we don't have one.
184          */
185         if (access(CTDL_CSR_PATH, R_OK) != 0) {
186                 lprintf(5, "Generating a certificate signing request.\n");
187
188                 /*
189                  * Read our key from the file.  No, we don't just keep this
190                  * in memory from the above key-generation function, because
191                  * there is the possibility that the key was already on disk
192                  * and we didn't just generate it now.
193                  */
194                 fp = fopen(CTDL_KEY_PATH, "r");
195                 if (fp) {
196                         rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
197                         fclose(fp);
198                 }
199
200                 if (rsa) {
201
202                         /* Create a public key from the private key */
203                         if (pk=EVP_PKEY_new(), pk != NULL) {
204                                 EVP_PKEY_assign_RSA(pk, rsa);
205                                 if (req = X509_REQ_new(), req != NULL) {
206
207                                         /* Set the public key */
208                                         X509_REQ_set_pubkey(req, pk);
209                                         X509_REQ_set_version(req, 0L);
210
211                                         name = X509_REQ_get_subject_name(req);
212
213                                         /* Tell it who we are */
214
215                                         /*
216                                         X509_NAME_add_entry_by_txt(name, "C",
217                                                 MBSTRING_ASC, "US", -1, -1, 0);
218
219                                         X509_NAME_add_entry_by_txt(name, "ST",
220                                                 MBSTRING_ASC, "New York", -1, -1, 0);
221
222                                         X509_NAME_add_entry_by_txt(name, "L",
223                                                 MBSTRING_ASC, "Mount Kisco", -1, -1, 0);
224                                         */
225
226                                         X509_NAME_add_entry_by_txt(name, "O",
227                                                 MBSTRING_ASC, "FIXME.FIXME.org", -1, -1, 0);
228
229                                         X509_NAME_add_entry_by_txt(name, "OU",
230                                                 MBSTRING_ASC, "Citadel server", -1, -1, 0);
231
232                                         X509_NAME_add_entry_by_txt(name, "CN",
233                                                 MBSTRING_ASC, "FIXME.FIXME.org", -1, -1, 0);
234                                 
235                                         X509_REQ_set_subject_name(req, name);
236
237                                         /* Sign the CSR */
238                                         if (!X509_REQ_sign(req, pk, EVP_md5())) {
239                                                 lprintf(3, "X509_REQ_sign(): error\n");
240                                         }
241                                         else {
242                                                 /* Write it to disk. */ 
243                                                 fp = fopen(CTDL_CSR_PATH, "w");
244                                                 if (fp != NULL) {
245                                                         chmod(CTDL_CSR_PATH, 0600);
246                                                         PEM_write_X509_REQ(fp, req);
247                                                         fclose(fp);
248                                                 }
249                                         }
250
251                                         X509_REQ_free(req);
252                                 }
253                         }
254
255                         RSA_free(rsa);
256                 }
257
258                 else {
259                         lprintf(3, "Unable to read private key.\n");
260                 }
261         }
262
263
264
265         /*
266          * Generate a self-signed certificate if we don't have one.
267          */
268         if (access(CTDL_CER_PATH, R_OK) != 0) {
269                 lprintf(5, "Generating a self-signed certificate.\n");
270
271                 /* Same deal as before: always read the key from disk because
272                  * it may or may not have just been generated.
273                  */
274                 fp = fopen(CTDL_KEY_PATH, "r");
275                 if (fp) {
276                         rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
277                         fclose(fp);
278                 }
279
280                 /* This also holds true for the CSR. */
281                 req = NULL;
282                 cer = NULL;
283                 pk = NULL;
284                 if (rsa) {
285                         if (pk=EVP_PKEY_new(), pk != NULL) {
286                                 EVP_PKEY_assign_RSA(pk, rsa);
287                         }
288
289                         fp = fopen(CTDL_CSR_PATH, "r");
290                         if (fp) {
291                                 req = PEM_read_X509_REQ(fp, NULL, NULL, NULL);
292                                 fclose(fp);
293                         }
294
295                         if (req) {
296                                 if (cer = X509_new(), cer != NULL) {
297
298                                         ASN1_INTEGER_set(X509_get_serialNumber(cer), 0);
299                                         X509_set_issuer_name(cer, req->req_info->subject);
300                                         X509_set_subject_name(cer, req->req_info->subject);
301                                         X509_gmtime_adj(X509_get_notBefore(cer), 0);
302                                         X509_gmtime_adj(X509_get_notAfter(cer),(long)60*60*24*SIGN_DAYS);
303
304                                         req_pkey = X509_REQ_get_pubkey(req);
305                                         X509_set_pubkey(cer, req_pkey);
306                                         EVP_PKEY_free(req_pkey);
307                                         
308                                         /* Sign the cert */
309                                         if (!X509_sign(cer, pk, EVP_md5())) {
310                                                 lprintf(3, "X509_sign(): error\n");
311                                         }
312                                         else {
313                                                 /* Write it to disk. */ 
314                                                 fp = fopen(CTDL_CER_PATH, "w");
315                                                 if (fp != NULL) {
316                                                         chmod(CTDL_CER_PATH, 0600);
317                                                         PEM_write_X509(fp, cer);
318                                                         fclose(fp);
319                                                 }
320                                         }
321                                         X509_free(cer);
322                                 }
323                         }
324
325                         RSA_free(rsa);
326                 }
327         }
328
329
330         /*
331          * Now try to bind to the key and certificate.
332          */
333         SSL_CTX_use_certificate_file(ssl_ctx, CTDL_CER_PATH, SSL_FILETYPE_PEM);
334         SSL_CTX_use_PrivateKey_file(ssl_ctx, CTDL_KEY_PATH, SSL_FILETYPE_PEM);
335         if ( !SSL_CTX_check_private_key(ssl_ctx) ) {
336                 lprintf(3, "Cannot install certificate: %s\n",
337                                 ERR_reason_error_string(ERR_get_error()));
338         }
339         
340 }
341
342
343 /*
344  * starttls() starts SSL/TLS encryption for the current session.
345  */
346 int starttls(int sock) {
347         int retval, bits, alg_bits;
348         SSL *newssl;
349
350         pthread_setspecific(ThreadSSL, NULL);
351
352         if (!ssl_ctx) {
353                 return(1);
354         }
355         if (!(newssl = SSL_new(ssl_ctx))) {
356                 lprintf(3, "SSL_new failed: %s\n",
357                                 ERR_reason_error_string(ERR_get_error()));
358                 return(2);
359         }
360         if (!(SSL_set_fd(newssl, sock))) {
361                 lprintf(3, "SSL_set_fd failed: %s\n",
362                         ERR_reason_error_string(ERR_get_error()));
363                 SSL_free(newssl);
364                 return(3);
365         }
366         retval = SSL_accept(newssl);
367         if (retval < 1) {
368                 /*
369                  * Can't notify the client of an error here; they will
370                  * discover the problem at the SSL layer and should
371                  * revert to unencrypted communications.
372                  */
373                 long errval;
374
375                 errval = SSL_get_error(newssl, retval);
376                 lprintf(3, "SSL_accept failed: %s\n",
377                         ERR_reason_error_string(ERR_get_error()));
378                 SSL_free(newssl);
379                 newssl = NULL;
380                 return(4);
381         }
382         BIO_set_close(newssl->rbio, BIO_NOCLOSE);
383         bits =
384             SSL_CIPHER_get_bits(SSL_get_current_cipher(newssl),
385                                 &alg_bits);
386         lprintf(5, "SSL/TLS using %s on %s (%d of %d bits)\n",
387                 SSL_CIPHER_get_name(SSL_get_current_cipher(newssl)),
388                 SSL_CIPHER_get_version(SSL_get_current_cipher(newssl)),
389                 bits, alg_bits);
390
391         pthread_setspecific(ThreadSSL, newssl);
392         return(0);
393 }
394
395
396
397 /*
398  * endtls() shuts down the TLS connection
399  *
400  * WARNING:  This may make your session vulnerable to a known plaintext
401  * attack in the current implmentation.
402  */
403 void endtls(void)
404 {
405         lprintf(5, "Ending SSL/TLS\n");
406         SSL_shutdown(THREADSSL);
407         SSL_free(THREADSSL);
408         pthread_setspecific(ThreadSSL, NULL);
409 }
410
411
412 /*
413  * ssl_lock() callback for OpenSSL mutex locks
414  */
415 void ssl_lock(int mode, int n, const char *file, int line)
416 {
417         if (mode & CRYPTO_LOCK)
418                 pthread_mutex_lock(SSLCritters[n]);
419         else
420                 pthread_mutex_unlock(SSLCritters[n]);
421 }
422
423 /*
424  * client_write_ssl() Send binary data to the client encrypted.
425  */
426 void client_write_ssl(char *buf, int nbytes)
427 {
428         int retval;
429         int nremain;
430         char junk[1];
431
432         nremain = nbytes;
433
434         while (nremain > 0) {
435                 if (SSL_want_write(THREADSSL)) {
436                         if ((SSL_read(THREADSSL, junk, 0)) < 1) {
437                                 lprintf(9, "SSL_read in client_write: %s\n", ERR_reason_error_string(ERR_get_error()));
438                         }
439                 }
440                 retval =
441                     SSL_write(THREADSSL, &buf[nbytes - nremain], nremain);
442                 if (retval < 1) {
443                         long errval;
444
445                         errval = SSL_get_error(THREADSSL, retval);
446                         if (errval == SSL_ERROR_WANT_READ ||
447                             errval == SSL_ERROR_WANT_WRITE) {
448                                 sleep(1);
449                                 continue;
450                         }
451                         lprintf(9, "SSL_write got error %ld, ret %d\n", errval, retval);
452                         if (retval == -1)
453                                 lprintf(9, "errno is %d\n", errno);
454                         endtls();
455                         return;
456                 }
457                 nremain -= retval;
458         }
459 }
460
461
462 /*
463  * client_read_ssl() - read data from the encrypted layer.
464  */
465 int client_read_ssl(char *buf, int bytes, int timeout)
466 {
467 #if 0
468         fd_set rfds;
469         struct timeval tv;
470         int retval;
471         int s;
472 #endif
473         int len, rlen;
474         char junk[1];
475
476         len = 0;
477         while (len < bytes) {
478 #if 0
479                 /*
480                  * This code is disabled because we don't need it when
481                  * using blocking reads (which we are). -IO
482                  */
483                 FD_ZERO(&rfds);
484                 s = BIO_get_fd(THREADSSL->rbio, NULL);
485                 FD_SET(s, &rfds);
486                 tv.tv_sec = timeout;
487                 tv.tv_usec = 0;
488
489                 retval = select(s + 1, &rfds, NULL, NULL, &tv);
490
491                 if (FD_ISSET(s, &rfds) == 0) {
492                         return (0);
493                 }
494
495 #endif
496                 if (SSL_want_read(THREADSSL)) {
497                         if ((SSL_write(THREADSSL, junk, 0)) < 1) {
498                                 lprintf(9, "SSL_write in client_read: %s\n", ERR_reason_error_string(ERR_get_error()));
499                         }
500                 }
501                 rlen = SSL_read(THREADSSL, &buf[len], bytes - len);
502                 if (rlen < 1) {
503                         long errval;
504
505                         errval = SSL_get_error(THREADSSL, rlen);
506                         if (errval == SSL_ERROR_WANT_READ ||
507                             errval == SSL_ERROR_WANT_WRITE) {
508                                 sleep(1);
509                                 continue;
510                         }
511                         lprintf(9, "SSL_read got error %ld\n", errval);
512                         endtls();
513                         return (0);
514                 }
515                 len += rlen;
516         }
517         return (1);
518 }
519
520
521 #endif                          /* HAVE_OPENSSL */