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