]> code.citadel.org Git - citadel.git/blob - citadel/serv_crypto.c
* Remove some unnecessary and possibly hazardous debugging code leftover
[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  * cmd_stls() starts SSL/TLS encryption for the current session
296  */
297 void cmd_stls(char *params)
298 {
299         int retval, bits, alg_bits;
300
301         if (!ssl_ctx) {
302                 cprintf("%d No SSL_CTX available\n", ERROR + CMD_NOT_SUPPORTED);
303                 return;
304         }
305         if (!(CC->ssl = SSL_new(ssl_ctx))) {
306                 lprintf(2, "SSL_new failed: %s\n",
307                                 ERR_reason_error_string(ERR_peek_error()));
308                 cprintf("%d SSL_new: %s\n", ERROR + INTERNAL_ERROR,
309                                 ERR_reason_error_string(ERR_get_error()));
310                 return;
311         }
312         if (!(SSL_set_fd(CC->ssl, CC->client_socket))) {
313                 lprintf(2, "SSL_set_fd failed: %s\n",
314                         ERR_reason_error_string(ERR_peek_error()));
315                 SSL_free(CC->ssl);
316                 CC->ssl = NULL;
317                 cprintf("%d SSL_set_fd: %s\n", ERROR + INTERNAL_ERROR,
318                                 ERR_reason_error_string(ERR_get_error()));
319                 return;
320         }
321         cprintf("%d \n", CIT_OK);
322         retval = SSL_accept(CC->ssl);
323         if (retval < 1) {
324                 /*
325                  * Can't notify the client of an error here; they will
326                  * discover the problem at the SSL layer and should
327                  * revert to unencrypted communications.
328                  */
329                 long errval;
330
331                 errval = SSL_get_error(CC->ssl, retval);
332                 lprintf(2, "SSL_accept failed: %s\n",
333                         ERR_reason_error_string(ERR_get_error()));
334                 SSL_free(CC->ssl);
335                 CC->ssl = NULL;
336                 return;
337         }
338         BIO_set_close(CC->ssl->rbio, BIO_NOCLOSE);
339         bits =
340             SSL_CIPHER_get_bits(SSL_get_current_cipher(CC->ssl),
341                                 &alg_bits);
342         lprintf(3, "SSL/TLS using %s on %s (%d of %d bits)\n",
343                 SSL_CIPHER_get_name(SSL_get_current_cipher(CC->ssl)),
344                 SSL_CIPHER_get_version(SSL_get_current_cipher(CC->ssl)),
345                 bits, alg_bits);
346         CC->redirect_ssl = 1;
347 }
348
349
350 /*
351  * cmd_gtls() returns status info about the TLS connection
352  */
353 void cmd_gtls(char *params)
354 {
355         int bits, alg_bits;
356
357         if (!CC->ssl || !CC->redirect_ssl) {
358                 cprintf("%d Session is not encrypted.\n", ERROR);
359                 return;
360         }
361         bits =
362             SSL_CIPHER_get_bits(SSL_get_current_cipher(CC->ssl),
363                                 &alg_bits);
364         cprintf("%d %s|%s|%d|%d\n", CIT_OK,
365                 SSL_CIPHER_get_version(SSL_get_current_cipher(CC->ssl)),
366                 SSL_CIPHER_get_name(SSL_get_current_cipher(CC->ssl)),
367                 alg_bits, bits);
368 }
369
370
371 /*
372  * endtls() 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         lprintf(7, "Ending SSL/TLS\n");
380
381         if (!CC->ssl) {
382                 CC->redirect_ssl = 0;
383                 return;
384         }
385
386         SSL_shutdown(CC->ssl);
387         SSL_free(CC->ssl);
388         CC->ssl = NULL;
389         CC->redirect_ssl = 0;
390 }
391
392
393 /*
394  * ssl_lock() callback for OpenSSL mutex locks
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 #endif                          /* HAVE_OPENSSL */