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