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