]> code.citadel.org Git - citadel.git/blob - citadel/serv_crypto.c
* SSL/TLS support for the Citadel/UX wire protocol
[citadel.git] / citadel / serv_crypto.c
1 /* $Id$ */
2
3 #include <unistd.h>
4 #include <sys/types.h>
5 #include "sysdep.h"
6 #ifdef HAVE_OPENSSL
7 #include <openssl/ssl.h>
8 #include <openssl/err.h>
9 #include <openssl/rand.h>
10 #endif
11 #ifdef HAVE_PTHREAD_H
12 #include <pthread.h>
13 #endif
14 #include "server.h"
15 #include "serv_crypto.h"
16 #include "sysdep_decls.h"
17 #include "dynloader.h"
18
19
20 #ifdef HAVE_OPENSSL
21 SSL_CTX *ssl_ctx;                               /* SSL context */
22 pthread_mutex_t **SSLCritters;                  /* Things needing locking */
23
24
25 void init_ssl(void)
26 {
27         SSL_METHOD *ssl_method;
28         DH *dh;
29         
30         if (!RAND_status()) {
31                 lprintf(2, "PRNG not adequately seeded, won't do SSL/TLS\n");
32                 return;
33         }
34         SSLCritters = mallok(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
35         if (!SSLCritters) {
36                 lprintf(1, "citserver: can't allocate memory!!\n");
37                 /* Nothing's been initialized, just die */
38                 exit(1);
39         } else {
40                 int a;
41
42                 for (a=0; a<CRYPTO_num_locks(); a++) {
43                         SSLCritters[a] = mallok(sizeof (pthread_mutex_t));
44                         if (!SSLCritters[a]) {
45                                 lprintf(1, "citserver: can't allocate memory!!\n");
46                                 /* Nothing's been initialized, just die */
47                                 exit(1);
48                         }
49                         pthread_mutex_init(SSLCritters[a], NULL);
50                 }
51         }
52
53         /*
54          * Initialize SSL transport layer
55          */
56         SSL_library_init();
57         SSL_load_error_strings();
58         ssl_method = SSLv23_server_method();
59         if (!(ssl_ctx = SSL_CTX_new(ssl_method))) {
60                 lprintf(2, "SSL_CTX_new failed: %s\n",
61                                 ERR_reason_error_string(ERR_get_error()));
62                 return;
63         }
64         if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
65                 lprintf(2, "SSL: No ciphers available\n");
66                 SSL_CTX_free(ssl_ctx);
67                 ssl_ctx = NULL;
68                 return;
69         }
70 #if 0
71 #if SSLEAY_VERSION_NUMBER >= 0x00906000L
72         SSL_CTX_set_mode(ssl_ctx, SSL_CTX_get_mode(ssl_ctx) |
73                         SSL_MODE_AUTO_RETRY);
74 #endif
75 #endif
76         CRYPTO_set_locking_callback(ssl_lock);
77         CRYPTO_set_id_callback(pthread_self);
78
79         /* Load DH parameters into the context */
80         dh = DH_new();
81         if (!dh) {
82                 lprintf(2, "init_ssl() can't allocate a DH object: %s\n",
83                                 ERR_reason_error_string(ERR_get_error()));
84                 SSL_CTX_free(ssl_ctx);
85                 ssl_ctx = NULL;
86                 return;
87         }
88         if (!(BN_hex2bn(&(dh->p), DH_P))) {
89                 lprintf(2, "init_ssl() can't assign DH_P: %s\n",
90                                 ERR_reason_error_string(ERR_get_error()));
91                 SSL_CTX_free(ssl_ctx);
92                 ssl_ctx = NULL;
93                 return;
94         }
95         if (!(BN_hex2bn(&(dh->g), DH_G))) {
96                 lprintf(2, "init_ssl() can't assign DH_G: %s\n",
97                                 ERR_reason_error_string(ERR_get_error()));
98                 SSL_CTX_free(ssl_ctx);
99                 ssl_ctx = NULL;
100                 return;
101         }
102         dh->length = DH_L;
103         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
104         DH_free(dh);
105
106         /* Finally let the server know we're here */
107         CtdlRegisterProtoHook(cmd_stls, "STLS", "Start SSL/TLS session");
108         CtdlRegisterProtoHook(cmd_gtls, "GTLS", "Get SSL/TLS session status");
109         CtdlRegisterProtoHook(cmd_etls, "ETLS", "End SSL/TLS session");
110         CtdlRegisterSessionHook(endtls_atlogout, EVT_STOP);
111 }
112
113
114 /*
115  * client_write_ssl() Send binary data to the client encrypted.
116  */
117 void client_write_ssl(char *buf, int nbytes)
118 {
119         int retval;
120         int nremain;
121         char junk[1];
122
123         nremain = nbytes;
124
125         while (nremain > 0) {
126                 if (SSL_want_write(CC->ssl)) {
127                         if ((SSL_read(CC->ssl, junk, 0)) < 1) {
128                                 lprintf(9, "SSL_read in client_write:\n");
129                                 ERR_print_errors_fp(stderr);
130                         }
131                 }
132                 retval = SSL_write(CC->ssl, &buf[nbytes - nremain], nremain);
133                 if (retval < 1) {
134                         long errval;
135
136                         errval = SSL_get_error(CC->ssl, retval);
137                         if (errval == SSL_ERROR_WANT_READ ||
138                                         errval == SSL_ERROR_WANT_WRITE) {
139                                 sleep(1);
140                                 continue;
141                         }
142                         lprintf(9, "SSL_write got error %ld\n", errval);
143                         endtls(1);
144                         client_write(&buf[nbytes - nremain], nremain);
145                         return;
146                 }
147                 nremain -= retval;
148         }
149 }
150
151
152 /*
153  * client_read_ssl() - read data from the encrypted layer.
154  */
155 int client_read_ssl(char *buf, int bytes, int timeout)
156 {
157         int len,rlen;
158         fd_set rfds;
159         struct timeval tv;
160         int retval;
161         int s;
162         char junk[1];
163
164         len = 0;
165         while(len<bytes) {
166                 FD_ZERO(&rfds);
167                 s = BIO_get_fd(CC->ssl->rbio, NULL);
168                 FD_SET(s, &rfds);
169                 tv.tv_sec = timeout;
170                 tv.tv_usec = 0;
171
172                 retval = select(s+1, &rfds, NULL, NULL, &tv);
173
174                 if (FD_ISSET(s, &rfds) == 0) {
175                         return(0);
176                 }
177
178                 if (SSL_want_read(CC->ssl)) {
179                         if ((SSL_write(CC->ssl, junk, 0)) < 1) {
180                                 lprintf(9, "SSL_write in client_read:\n");
181                                 ERR_print_errors_fp(stderr);
182                         }
183                 }
184                 rlen = SSL_read(CC->ssl, &buf[len], bytes-len);
185                 if (rlen<1) {
186                         long errval;
187
188                         errval = SSL_get_error(CC->ssl, rlen);
189                         if (errval == SSL_ERROR_WANT_READ ||
190                                         errval == SSL_ERROR_WANT_WRITE) {
191                                 sleep(1);
192                                 continue;
193                         }
194                         lprintf(9, "SSL_read got error %ld\n", errval);
195                         endtls(1);
196                         return (client_read_to(&buf[len], bytes - len, timeout));
197                 }
198                 len += rlen;
199         }
200         return(1);
201 }
202
203
204 /*
205  * cmd_stls() starts SSL/TLS encryption for the current session
206  */
207 void cmd_stls(char *params)
208 {
209         int retval, bits, alg_bits;
210
211         if (!ssl_ctx) {
212                 cprintf("%d No SSL_CTX available\n", ERROR);
213                 return;
214         }
215         if (!(CC->ssl = SSL_new(ssl_ctx))) {
216                 lprintf(2, "SSL_new failed: %s\n",
217                                 ERR_reason_error_string(ERR_peek_error()));
218                 cprintf("%d SSL_new: %s\n", ERROR,
219                                 ERR_reason_error_string(ERR_get_error()));
220                 return;
221         }
222         if (!(SSL_set_fd(CC->ssl, CC->client_socket))) {
223                 lprintf(2, "SSL_set_fd failed: %s\n",
224                                 ERR_reason_error_string(ERR_peek_error()));
225                 SSL_free(CC->ssl);
226                 CC->ssl = NULL;
227                 cprintf("%d SSL_set_fd: %s\n", ERROR,
228                                 ERR_reason_error_string(ERR_get_error()));
229                 return;
230         }
231         cprintf("%d \n", OK);
232         retval = SSL_accept(CC->ssl);
233         if (retval < 1) {
234                 /*
235                  * Can't notify the client of an error here; they will
236                  * discover the problem at the SSL layer and should
237                  * revert to unencrypted communications.
238                  */
239                 long errval;
240
241                 errval = SSL_get_error(CC->ssl, retval);
242                 lprintf(2, "SSL_accept failed: %s\n",
243                                 ERR_reason_error_string(ERR_get_error()));
244                 SSL_free(CC->ssl);
245                 CC->ssl = NULL;
246                 return;
247         }
248         BIO_set_close(CC->ssl->rbio, BIO_NOCLOSE);
249         bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(CC->ssl), &alg_bits);
250         lprintf(3, "Session %d using %s on %s (%d of %d bits)\n", CC->cs_pid,
251                         SSL_CIPHER_get_name(SSL_get_current_cipher(CC->ssl)),
252                         SSL_CIPHER_get_version(SSL_get_current_cipher(CC->ssl)),
253                         bits, alg_bits);
254         CC->redirect_ssl = 1;
255 }
256
257
258 /*
259  * cmd_gtls() returns status info about the TLS connection
260  */
261 void cmd_gtls(char *params)
262 {
263         int bits, alg_bits;
264         
265         if (!CC->ssl || !CC->redirect_ssl) {
266                 cprintf("%d Session is not encrypted.\n", ERROR);
267                 return;
268         }
269         bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(CC->ssl), &alg_bits);
270         cprintf("%d %s|%s|%d|%d\n", OK,
271                 SSL_CIPHER_get_version(SSL_get_current_cipher(CC->ssl)),
272                 SSL_CIPHER_get_name(SSL_get_current_cipher(CC->ssl)),
273                 alg_bits, bits);
274 }
275
276
277 /* Logout function hook */
278 void endtls_atlogout(void)
279 {
280         endtls(1);
281 }
282
283
284 /* Command function hook */
285 void cmd_etls(char *params)
286 {
287         endtls(0);
288 }
289
290
291 /*
292  * endtls() shuts down the TLS connection
293  * Parameter is NULL for client request, CitContext * for server request
294  *
295  * WARNING:  This may make your session vulnerable to a known plaintext
296  * attack in the current implmentation.
297  */
298 void endtls(int who)
299 {
300         lprintf(7, "Session %d ending SSL/TLS%s\n", CC->cs_pid,
301                         (who) ? "" : " at client request");
302
303         if (!who) {
304                 if (!CC->ssl) {
305                         cprintf("%d Connection is not encrypted.\n", ERROR);
306                         return;
307                 }
308                 cprintf("%d Now stop encryption.\n", OK);
309         } else if (!CC->ssl) {
310                 return;
311         }
312         
313         SSL_shutdown(CC->ssl);
314         SSL_free(CC->ssl);
315         CC->ssl = NULL;
316         CC->redirect_ssl = 0;
317 }
318
319
320 /*
321  * ssl_lock() callback for OpenSSL mutex locks
322  */
323 void ssl_lock(int mode, int n, const char *file, int line)
324 {
325         if (mode & CRYPTO_LOCK)
326                 pthread_mutex_lock(SSLCritters[n]);
327         else
328                 pthread_mutex_unlock(SSLCritters[n]);
329 }
330 #endif /* HAVE_OPENSSL */