]> code.citadel.org Git - citadel.git/blob - citadel/client_crypto.c
3da98af1d408df7b65eb8d3ecaf3b55b4be3dc62
[citadel.git] / citadel / client_crypto.c
1 /* $Id$ */
2
3 #include "sysdep.h"
4 #ifdef HAVE_OPENSSL
5 #include <openssl/ssl.h>
6 #include <openssl/err.h>
7 #include <openssl/rand.h>
8 #endif
9 #ifdef HAVE_PTHREAD_H
10 #include <pthread.h>
11 #endif
12 #include <unistd.h>
13 #include <sys/types.h>
14 #include "citadel.h"
15 #include "client_crypto.h"
16 #include "citadel_ipc.h"
17 #include "ipc.h"
18
19 #ifdef HAVE_OPENSSL
20 SSL *ssl;
21 SSL_CTX *ssl_ctx;
22 int ssl_is_connected = 0;
23 char arg_encrypt;
24 char rc_encrypt;
25 #ifdef THREADED_CLIENT
26 pthread_mutex_t **Critters;                     /* Things that need locking */
27 #endif /* THREADED_CLIENT */
28
29 extern int serv_sock;
30 extern int server_is_local;
31 #endif /* HAVE_OPENSSL */
32
33
34 static void (*status_hook)(char *s) = NULL;
35
36 void setCryptoStatusHook(void (*hook)(char *s)) {
37         status_hook = hook;
38 }
39
40
41 #ifdef HAVE_OPENSSL
42 /*
43  * input binary data from encrypted connection
44  */
45 void serv_read_ssl(char *buf, int bytes)
46 {
47         int len, rlen;
48         char junk[1];
49
50         len = 0;
51         while (len < bytes) {
52                 if (SSL_want_read(ssl)) {
53                         if ((SSL_write(ssl, junk, 0)) < 1) {
54                                 error_printf("SSL_write in serv_read:\n");
55                                 ERR_print_errors_fp(stderr);
56                         }
57                 }
58                 rlen = SSL_read(ssl, &buf[len], bytes - len);
59                 if (rlen < 1) {
60                         long errval;
61
62                         errval = SSL_get_error(ssl, rlen);
63                         if (errval == SSL_ERROR_WANT_READ ||
64                                         errval == SSL_ERROR_WANT_WRITE) {
65                                 sleep(1);
66                                 continue;
67                         }
68                         if (errval == SSL_ERROR_ZERO_RETURN ||
69                                         errval == SSL_ERROR_SSL) {
70                                 endtls();
71                                 serv_read(&buf[len], bytes - len);
72                                 return;
73                         }
74                         error_printf("SSL_read in serv_read:\n");
75                         ERR_print_errors_fp(stderr);
76                         connection_died();
77                         return;
78                 }
79                 len += rlen;
80         }
81 }
82
83
84 /*
85  * send binary to server encrypted
86  */
87 void serv_write_ssl(char *buf, int nbytes)
88 {
89         int bytes_written = 0;
90         int retval;
91         char junk[1];
92
93         while (bytes_written < nbytes) {
94                 if (SSL_want_write(ssl)) {
95                         if ((SSL_read(ssl, junk, 0)) < 1) {
96                                 error_printf("SSL_read in serv_write:\n");
97                                 ERR_print_errors_fp(stderr);
98                         }
99                 }
100                 retval = SSL_write(ssl, &buf[bytes_written],
101                                 nbytes - bytes_written);
102                 if (retval < 1) {
103                         long errval;
104
105                         errval = SSL_get_error(ssl, retval);
106                         if (errval == SSL_ERROR_WANT_READ ||
107                                         errval == SSL_ERROR_WANT_WRITE) {
108                                 sleep(1);
109                                 continue;
110                         }
111                         if (errval == SSL_ERROR_ZERO_RETURN ||
112                                         errval == SSL_ERROR_SSL) {
113                                 endtls();
114                                 serv_write(&buf[bytes_written],
115                                                 nbytes - bytes_written);
116                                 return;
117                         }
118                         error_printf("SSL_write in serv_write:\n");
119                         ERR_print_errors_fp(stderr);
120                         connection_died();
121                         return;
122                 }
123                 bytes_written += retval;
124         }
125 }
126
127
128 void ssl_lock(int mode, int n, const char *file, int line)
129 {
130 #ifdef THREADED_CLIENT
131         if (mode & CRYPTO_LOCK)
132                 pthread_mutex_lock(Critters[n]);
133         else
134                 pthread_mutex_unlock(Critters[n]);
135 #endif /* THREADED_CLIENT */
136 }
137 #endif /* HAVE_OPENSSL */
138
139 #if defined(THREADED_CLIENT) && defined(HAVE_OPENSSL)
140 static unsigned long id_callback(void) {
141         return (unsigned long)pthread_self();
142 }
143 #endif
144
145 /*
146  * starttls() starts SSL/TLS if possible
147  * Returns 1 if the session is encrypted, 0 otherwise
148  */
149 int starttls(void)
150 {
151 #ifdef HAVE_OPENSSL
152         int a;
153         /* int r; */                            /* IPC response code */
154         char buf[SIZ];
155         SSL_METHOD *ssl_method;
156         DH *dh;
157         
158         /* Figure out whether to encrypt the session based on user options */
159         /* User request to disable encryption */
160         if (arg_encrypt == RC_NO || rc_encrypt == RC_NO) {
161                 return 0;
162         }
163         /* User expressed no preference */
164         else if (rc_encrypt == RC_DEFAULT && arg_encrypt == RC_DEFAULT &&
165                         server_is_local) {
166                 return 0;
167         }
168
169         /* Get started */
170         ssl = NULL;
171         ssl_ctx = NULL;
172         dh = NULL;
173         SSL_load_error_strings();
174         SSLeay_add_ssl_algorithms();
175
176         /* Set up the SSL context in which we will oeprate */
177         ssl_method = SSLv23_client_method();
178         ssl_ctx = SSL_CTX_new(ssl_method);
179         if (!ssl_ctx) {
180                 error_printf("SSL_CTX_new failed: %s\n",
181                                 ERR_reason_error_string(ERR_get_error()));
182                 return 0;
183         }
184         /* Any reasonable cipher we can get */
185         if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
186                 error_printf("No ciphers available for encryption\n");
187                 SSL_CTX_free(ssl_ctx);
188                 ssl_ctx = NULL;
189                 return 0;
190         }
191         SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
192         
193         /* Load DH parameters into the context */
194         dh = DH_new();
195         if (!dh) {
196                 error_printf("Can't allocate a DH object: %s\n",
197                                 ERR_reason_error_string(ERR_get_error()));
198                 return 0;
199         }
200         if (!(BN_hex2bn(&(dh->p), DH_P))) {
201                 error_printf("Can't assign DH_P: %s\n",
202                                 ERR_reason_error_string(ERR_get_error()));
203                 return 0;
204         }
205         if (!(BN_hex2bn(&(dh->g), DH_G))) {
206                 error_printf("Can't assign DH_G: %s\n",
207                                 ERR_reason_error_string(ERR_get_error()));
208                 return 0;
209         }
210         dh->length = DH_L;
211         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
212         DH_free(dh);
213
214 #ifdef THREADED_CLIENT
215         /* OpenSSL requires callbacks for threaded clients */
216         CRYPTO_set_locking_callback(ssl_lock);
217         CRYPTO_set_id_callback(id_callback);
218
219         /* OpenSSL requires us to do semaphores for threaded clients */
220         Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
221         if (!Critters) {
222                 perror("malloc failed");
223                 exit(1);
224         } else {
225                 for (a = 0; a < CRYPTO_num_locks(); a++) {
226                         Critters[a] = malloc(sizeof (pthread_mutex_t));
227                         if (!Critters[a]) {
228                                 perror("malloc failed");
229                                 exit(1);
230                         }
231                         pthread_mutex_init(Critters[a], NULL);
232                 }
233         }
234 #endif /* THREADED_CLIENT */
235
236         /* New SSL object */
237         ssl = SSL_new(ssl_ctx);
238         if (!ssl) {
239                 error_printf("SSL_new failed: %s\n",
240                                 ERR_reason_error_string(ERR_get_error()));
241                 SSL_CTX_free(ssl_ctx);
242                 ssl_ctx = NULL;
243                 return 0;
244         }
245         /* Pointless flag waving */
246 #if SSLEAY_VERSION_NUMBER >= 0x0922
247         SSL_set_session_id_context(ssl, "Citadel/UX SID", 14);
248 #endif
249
250         if (!access("/var/run/egd-pool", F_OK))
251                 RAND_egd("/var/run/egd-pool");
252
253         if (!RAND_status()) {
254                 error_printf("PRNG not properly seeded\n");
255                 return 0;
256         }
257
258         /* Associate network connection with SSL object */
259         if (SSL_set_fd(ssl, serv_sock) < 1) {
260                 error_printf("SSL_set_fd failed: %s\n",
261                                 ERR_reason_error_string(ERR_get_error()));
262                 SSL_CTX_free(ssl_ctx);
263                 ssl_ctx = NULL;
264                 SSL_free(ssl);
265                 ssl = NULL;
266                 return 0;
267         }
268
269         if (status_hook != NULL)
270                 status_hook("Requesting encryption...\r");
271
272         /* Ready to start SSL/TLS */
273         serv_puts("STLS");
274         serv_gets(buf);
275         if (buf[0] != '2') {
276                 error_printf("Server can't start TLS: %s\n", buf);
277                 return 0;
278         }
279
280         /* New code
281         r = CtdlIPCStartEncryption(buf);
282         if (r / 100 != 2) {
283                 error_printf("Server can't start TLS: %s\n", buf);
284                 return 0;
285         }
286         */
287
288         /* Do SSL/TLS handshake */
289         if ((a = SSL_connect(ssl)) < 1) {
290                 error_printf("SSL_connect failed: %s\n",
291                                 ERR_reason_error_string(ERR_get_error()));
292                 SSL_CTX_free(ssl_ctx);
293                 ssl_ctx = NULL;
294                 SSL_free(ssl);
295                 ssl = NULL;
296                 return 0;
297         }
298         BIO_set_close(ssl->rbio, BIO_NOCLOSE);
299         {
300                 int bits, alg_bits;
301
302                 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ssl), &alg_bits);
303                 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
304                                 SSL_CIPHER_get_version(SSL_get_current_cipher(ssl)),
305                                 SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)),
306                                 bits, alg_bits);
307         }
308         ssl_is_connected = 1;
309         return 1;
310 #else
311         return 0;
312 #endif /* HAVE_OPENSSL */
313 }
314
315
316 /*
317  * void endtls() - end SSL/TLS session
318  */
319 void endtls(void)
320 {
321 #ifdef HAVE_OPENSSL
322         if (ssl) {
323                 SSL_shutdown(ssl);
324                 SSL_free(ssl);
325                 ssl = NULL;
326         }
327         ssl_is_connected = 0;
328         if (ssl_ctx) {
329                 SSL_CTX_free(ssl_ctx);
330                 ssl_ctx = NULL;
331         }
332 #endif
333 }