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