]> code.citadel.org Git - citadel.git/blob - citadel/serv_crypto.c
* Genericized the Citadel API for TLS-enabling protocols
[citadel.git] / citadel / serv_crypto.c
1 /* $Id$ */
2
3 #include <string.h>
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include "sysdep.h"
7
8 #ifdef HAVE_OPENSSL
9 #include <openssl/ssl.h>
10 #include <openssl/err.h>
11 #include <openssl/rand.h>
12 #endif
13
14 #if TIME_WITH_SYS_TIME
15 # include <sys/time.h>
16 # include <time.h>
17 #else
18 # if HAVE_SYS_TIME_H
19 #  include <sys/time.h>
20 # else
21 #  include <time.h>
22 # endif
23 #endif
24
25 #ifdef HAVE_PTHREAD_H
26 #include <pthread.h>
27 #endif
28
29 #ifdef HAVE_SYS_SELECT_H
30 #include <sys/select.h>
31 #endif
32
33 #include <stdio.h>
34 #include "server.h"
35 #include "serv_crypto.h"
36 #include "sysdep_decls.h"
37 #include "serv_extensions.h"
38
39
40 #ifdef HAVE_OPENSSL
41 SSL_CTX *ssl_ctx;               /* SSL context */
42 pthread_mutex_t **SSLCritters;  /* Things needing locking */
43
44 static unsigned long id_callback(void)
45 {
46         return (unsigned long) pthread_self();
47 }
48
49  /*
50   * Set up the cert things on the server side. We do need both the
51   * private key (in key_file) and the cert (in cert_file).
52   * Both files may be identical.
53   *
54   * This function is taken from OpenSSL apps/s_cb.c
55   */
56
57 static int set_cert_stuff(SSL_CTX * ctx,
58                           const char *cert_file, const char *key_file)
59 {
60         if (cert_file != NULL) {
61                 if (SSL_CTX_use_certificate_file(ctx, cert_file,
62                                                  SSL_FILETYPE_PEM) <= 0) {
63                         lprintf(3, "unable to get certificate from '%s'",
64                                 cert_file);
65                         return (0);
66                 }
67                 if (key_file == NULL)
68                         key_file = cert_file;
69                 if (SSL_CTX_use_PrivateKey_file(ctx, key_file,
70                                                 SSL_FILETYPE_PEM) <= 0) {
71                         lprintf(3, "unable to get private key from '%s'",
72                                 key_file);
73                         return (0);
74                 }
75                 /* Now we know that a key and cert have been set against
76                  * the SSL context */
77                 if (!SSL_CTX_check_private_key(ctx)) {
78                         lprintf(3,
79                                 "Private key does not match the certificate public key");
80                         return (0);
81                 }
82         }
83         return (1);
84 }
85
86
87 void init_ssl(void)
88 {
89         SSL_METHOD *ssl_method;
90         DH *dh;
91
92         if (!access("/var/run/egd-pool", F_OK))
93                 RAND_egd("/var/run/egd-pool");
94
95         if (!RAND_status()) {
96                 lprintf(2,
97                         "PRNG not adequately seeded, won't do SSL/TLS\n");
98                 return;
99         }
100         SSLCritters =
101             mallok(CRYPTO_num_locks() * sizeof(pthread_mutex_t *));
102         if (!SSLCritters) {
103                 lprintf(1, "citserver: can't allocate memory!!\n");
104                 /* Nothing's been initialized, just die */
105                 exit(1);
106         } else {
107                 int a;
108
109                 for (a = 0; a < CRYPTO_num_locks(); a++) {
110                         SSLCritters[a] = mallok(sizeof(pthread_mutex_t));
111                         if (!SSLCritters[a]) {
112                                 lprintf(1,
113                                         "citserver: can't allocate memory!!\n");
114                                 /* Nothing's been initialized, just die */
115                                 exit(1);
116                         }
117                         pthread_mutex_init(SSLCritters[a], NULL);
118                 }
119         }
120
121         /*
122          * Initialize SSL transport layer
123          */
124         SSL_library_init();
125         SSL_load_error_strings();
126         ssl_method = SSLv23_server_method();
127         if (!(ssl_ctx = SSL_CTX_new(ssl_method))) {
128                 lprintf(2, "SSL_CTX_new failed: %s\n",
129                         ERR_reason_error_string(ERR_get_error()));
130                 return;
131         }
132         if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
133                 lprintf(2, "SSL: No ciphers available\n");
134                 SSL_CTX_free(ssl_ctx);
135                 ssl_ctx = NULL;
136                 return;
137         }
138 #if 0
139 #if SSLEAY_VERSION_NUMBER >= 0x00906000L
140         SSL_CTX_set_mode(ssl_ctx, SSL_CTX_get_mode(ssl_ctx) |
141                          SSL_MODE_AUTO_RETRY);
142 #endif
143 #endif
144
145         CRYPTO_set_locking_callback(ssl_lock);
146         CRYPTO_set_id_callback(id_callback);
147
148         /* Load DH parameters into the context */
149         dh = DH_new();
150         if (!dh) {
151                 lprintf(2, "init_ssl() can't allocate a DH object: %s\n",
152                         ERR_reason_error_string(ERR_get_error()));
153                 SSL_CTX_free(ssl_ctx);
154                 ssl_ctx = NULL;
155                 return;
156         }
157         if (!(BN_hex2bn(&(dh->p), DH_P))) {
158                 lprintf(2, "init_ssl() can't assign DH_P: %s\n",
159                         ERR_reason_error_string(ERR_get_error()));
160                 SSL_CTX_free(ssl_ctx);
161                 ssl_ctx = NULL;
162                 return;
163         }
164         if (!(BN_hex2bn(&(dh->g), DH_G))) {
165                 lprintf(2, "init_ssl() can't assign DH_G: %s\n",
166                         ERR_reason_error_string(ERR_get_error()));
167                 SSL_CTX_free(ssl_ctx);
168                 ssl_ctx = NULL;
169                 return;
170         }
171         dh->length = DH_L;
172         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
173         DH_free(dh);
174
175         /* Get our certificates in order */
176         if (set_cert_stuff(ssl_ctx,
177                            BBSDIR "/keys/citadel.cer",
178                            BBSDIR "/keys/citadel.key") != 1) {
179
180                 lprintf(3, "SSL ERROR: cert is bad!\n");
181
182         }
183
184         /* Finally let the server know we're here */
185         CtdlRegisterProtoHook(cmd_stls, "STLS", "Start SSL/TLS session");
186         CtdlRegisterProtoHook(cmd_gtls, "GTLS",
187                               "Get SSL/TLS session status");
188         CtdlRegisterSessionHook(endtls, EVT_STOP);
189 }
190
191
192 /*
193  * client_write_ssl() Send binary data to the client encrypted.
194  */
195 void client_write_ssl(char *buf, int nbytes)
196 {
197         int retval;
198         int nremain;
199         char junk[1];
200
201         nremain = nbytes;
202
203         while (nremain > 0) {
204                 if (SSL_want_write(CC->ssl)) {
205                         if ((SSL_read(CC->ssl, junk, 0)) < 1) {
206                                 lprintf(9, "SSL_read in client_write:\n");
207                                 ERR_print_errors_fp(stderr);
208                         }
209                 }
210                 retval =
211                     SSL_write(CC->ssl, &buf[nbytes - nremain], nremain);
212                 if (retval < 1) {
213                         long errval;
214
215                         errval = SSL_get_error(CC->ssl, retval);
216                         if (errval == SSL_ERROR_WANT_READ ||
217                             errval == SSL_ERROR_WANT_WRITE) {
218                                 sleep(1);
219                                 continue;
220                         }
221                         lprintf(9, "SSL_write got error %ld, ret %d\n", errval, retval);
222                         if (retval == -1)
223                                 lprintf(9, "errno is %d\n", errno);
224                         endtls();
225                         client_write(&buf[nbytes - nremain], nremain);
226                         return;
227                 }
228                 nremain -= retval;
229         }
230 }
231
232
233 /*
234  * client_read_ssl() - read data from the encrypted layer.
235  */
236 int client_read_ssl(char *buf, int bytes, int timeout)
237 {
238 #if 0
239         fd_set rfds;
240         struct timeval tv;
241         int retval;
242         int s;
243 #endif
244         int len, rlen;
245         char junk[1];
246
247         len = 0;
248         while (len < bytes) {
249 #if 0
250                 /*
251                  * This code is disabled because we don't need it when
252                  * using blocking reads (which we are). -IO
253                  */
254                 FD_ZERO(&rfds);
255                 s = BIO_get_fd(CC->ssl->rbio, NULL);
256                 FD_SET(s, &rfds);
257                 tv.tv_sec = timeout;
258                 tv.tv_usec = 0;
259
260                 retval = select(s + 1, &rfds, NULL, NULL, &tv);
261
262                 if (FD_ISSET(s, &rfds) == 0) {
263                         return (0);
264                 }
265
266 #endif
267                 if (SSL_want_read(CC->ssl)) {
268                         if ((SSL_write(CC->ssl, junk, 0)) < 1) {
269                                 lprintf(9, "SSL_write in client_read:\n");
270                                 ERR_print_errors_fp(stderr);
271                         }
272                 }
273                 rlen = SSL_read(CC->ssl, &buf[len], bytes - len);
274                 if (rlen < 1) {
275                         long errval;
276
277                         errval = SSL_get_error(CC->ssl, rlen);
278                         if (errval == SSL_ERROR_WANT_READ ||
279                             errval == SSL_ERROR_WANT_WRITE) {
280                                 sleep(1);
281                                 continue;
282                         }
283                         lprintf(9, "SSL_read got error %ld\n", errval);
284                         endtls();
285                         return (client_read_to
286                                 (&buf[len], bytes - len, timeout));
287                 }
288                 len += rlen;
289         }
290         return (1);
291 }
292
293
294 /*
295  * CtdlStartTLS() starts SSL/TLS encryption for the current session.  It
296  * must be supplied with pre-generated strings for responses of "ok," "no
297  * support for TLS," and "error" so that we can use this in any protocol.
298  */
299 void CtdlStartTLS(char *ok_response, char *nosup_response,
300                         char *error_response) {
301
302         int retval, bits, alg_bits;
303
304         if (!ssl_ctx) {
305                 cprintf("%s", nosup_response);
306                 return;
307         }
308         if (!(CC->ssl = SSL_new(ssl_ctx))) {
309                 lprintf(2, "SSL_new failed: %s\n",
310                                 ERR_reason_error_string(ERR_get_error()));
311                 cprintf("%s", error_response);
312                 return;
313         }
314         if (!(SSL_set_fd(CC->ssl, CC->client_socket))) {
315                 lprintf(2, "SSL_set_fd failed: %s\n",
316                         ERR_reason_error_string(ERR_get_error()));
317                 SSL_free(CC->ssl);
318                 CC->ssl = NULL;
319                 cprintf("%s", error_response);
320                 return;
321         }
322         cprintf("%s", ok_response);
323         retval = SSL_accept(CC->ssl);
324         if (retval < 1) {
325                 /*
326                  * Can't notify the client of an error here; they will
327                  * discover the problem at the SSL layer and should
328                  * revert to unencrypted communications.
329                  */
330                 long errval;
331
332                 errval = SSL_get_error(CC->ssl, retval);
333                 lprintf(2, "SSL_accept failed: %s\n",
334                         ERR_reason_error_string(ERR_get_error()));
335                 SSL_free(CC->ssl);
336                 CC->ssl = NULL;
337                 return;
338         }
339         BIO_set_close(CC->ssl->rbio, BIO_NOCLOSE);
340         bits =
341             SSL_CIPHER_get_bits(SSL_get_current_cipher(CC->ssl),
342                                 &alg_bits);
343         lprintf(3, "SSL/TLS using %s on %s (%d of %d bits)\n",
344                 SSL_CIPHER_get_name(SSL_get_current_cipher(CC->ssl)),
345                 SSL_CIPHER_get_version(SSL_get_current_cipher(CC->ssl)),
346                 bits, alg_bits);
347         CC->redirect_ssl = 1;
348 }
349
350
351 /*
352  * cmd_stls() starts SSL/TLS encryption for the current session
353  */
354 void cmd_stls(char *params)
355 {
356         char ok_response[SIZ];
357         char nosup_response[SIZ];
358         char error_response[SIZ];
359
360         sprintf(ok_response,
361                 "%d Begin TLS negotiation now\n",
362                 CIT_OK);
363         sprintf(nosup_response,
364                 "%d TLS not supported here\n",
365                 ERROR + CMD_NOT_SUPPORTED);
366         sprintf(error_response,
367                 "%d TLS negotiation error\n",
368                 ERROR + INTERNAL_ERROR);
369
370         CtdlStartTLS(ok_response, nosup_response, error_response);
371 }
372
373
374 /*
375  * cmd_gtls() returns status info about the TLS connection
376  */
377 void cmd_gtls(char *params)
378 {
379         int bits, alg_bits;
380
381         if (!CC->ssl || !CC->redirect_ssl) {
382                 cprintf("%d Session is not encrypted.\n", ERROR);
383                 return;
384         }
385         bits =
386             SSL_CIPHER_get_bits(SSL_get_current_cipher(CC->ssl),
387                                 &alg_bits);
388         cprintf("%d %s|%s|%d|%d\n", CIT_OK,
389                 SSL_CIPHER_get_version(SSL_get_current_cipher(CC->ssl)),
390                 SSL_CIPHER_get_name(SSL_get_current_cipher(CC->ssl)),
391                 alg_bits, bits);
392 }
393
394
395 /*
396  * endtls() shuts down the TLS connection
397  *
398  * WARNING:  This may make your session vulnerable to a known plaintext
399  * attack in the current implmentation.
400  */
401 void endtls(void)
402 {
403         lprintf(7, "Ending SSL/TLS\n");
404
405         if (!CC->ssl) {
406                 CC->redirect_ssl = 0;
407                 return;
408         }
409
410         SSL_shutdown(CC->ssl);
411         SSL_free(CC->ssl);
412         CC->ssl = NULL;
413         CC->redirect_ssl = 0;
414 }
415
416
417 /*
418  * ssl_lock() callback for OpenSSL mutex locks
419  */
420 void ssl_lock(int mode, int n, const char *file, int line)
421 {
422         if (mode & CRYPTO_LOCK)
423                 pthread_mutex_lock(SSLCritters[n]);
424         else
425                 pthread_mutex_unlock(SSLCritters[n]);
426 }
427 #endif                          /* HAVE_OPENSSL */