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