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