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