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