]> code.citadel.org Git - citadel.git/blob - citadel/client_crypto.c
* Full-screen curses support for Citadel text client
[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
132 /*
133  * starttls() starts SSL/TLS if possible
134  * Returns 1 if the session is encrypted, 0 otherwise
135  */
136 int starttls(void)
137 {
138 #ifdef HAVE_OPENSSL
139         int a;
140         char buf[SIZ];
141         SSL_METHOD *ssl_method;
142         DH *dh;
143         
144         /* Figure out whether to encrypt the session based on user options */
145         /* User request to disable encryption */
146         if (arg_encrypt == RC_NO || rc_encrypt == RC_NO) {
147                 return 0;
148         }
149         /* User expressed no preference */
150         else if (rc_encrypt == RC_DEFAULT && arg_encrypt == RC_DEFAULT &&
151                         server_is_local) {
152                 return 0;
153         }
154
155         /* Get started */
156         ssl = NULL;
157         ssl_ctx = NULL;
158         dh = NULL;
159         SSL_load_error_strings();
160         SSLeay_add_ssl_algorithms();
161
162         /* Set up the SSL context in which we will oeprate */
163         ssl_method = SSLv23_client_method();
164         ssl_ctx = SSL_CTX_new(ssl_method);
165         if (!ssl_ctx) {
166                 err_printf("SSL_CTX_new failed: %s\n",
167                                 ERR_reason_error_string(ERR_get_error()));
168                 return 0;
169         }
170         /* Any reasonable cipher we can get */
171         if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
172                 err_printf("No ciphers available for encryption\n");
173                 SSL_CTX_free(ssl_ctx);
174                 ssl_ctx = NULL;
175                 return 0;
176         }
177         SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
178         
179         /* Load DH parameters into the context */
180         dh = DH_new();
181         if (!dh) {
182                 err_printf("Can't allocate a DH object: %s\n",
183                                 ERR_reason_error_string(ERR_get_error()));
184                 return 0;
185         }
186         if (!(BN_hex2bn(&(dh->p), DH_P))) {
187                 err_printf("Can't assign DH_P: %s\n",
188                                 ERR_reason_error_string(ERR_get_error()));
189                 return 0;
190         }
191         if (!(BN_hex2bn(&(dh->g), DH_G))) {
192                 err_printf("Can't assign DH_G: %s\n",
193                                 ERR_reason_error_string(ERR_get_error()));
194                 return 0;
195         }
196         dh->length = DH_L;
197         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
198         DH_free(dh);
199
200 #ifdef THREADED_CLIENT
201         /* OpenSSL requires callbacks for threaded clients */
202         CRYPTO_set_locking_callback(ssl_lock);
203         CRYPTO_set_id_callback(pthread_self);
204
205         /* OpenSSL requires us to do semaphores for threaded clients */
206         Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
207         if (!Critters) {
208                 perror("malloc failed");
209                 exit(1);
210         } else {
211                 for (a = 0; a < CRYPTO_num_locks(); a++) {
212                         Critters[a] = malloc(sizeof (pthread_mutex_t));
213                         if (!Critters[a]) {
214                                 perror("malloc failed");
215                                 exit(1);
216                         }
217                         pthread_mutex_init(Critters[a], NULL);
218                 }
219         }
220 #endif /* THREADED_CLIENT */
221
222         /* New SSL object */
223         ssl = SSL_new(ssl_ctx);
224         if (!ssl) {
225                 err_printf("SSL_new failed: %s\n",
226                                 ERR_reason_error_string(ERR_get_error()));
227                 SSL_CTX_free(ssl_ctx);
228                 ssl_ctx = NULL;
229                 return 0;
230         }
231         /* Pointless flag waving */
232 #if SSLEAY_VERSION_NUMBER >= 0x0922
233         SSL_set_session_id_context(ssl, "Citadel/UX SID", 14);
234 #endif
235         if (!RAND_status()) {
236                 err_printf("PRNG not properly seeded\n");
237                 return 0;
238         }
239
240         /* Associate network connection with SSL object */
241         if (SSL_set_fd(ssl, serv_sock) < 1) {
242                 err_printf("SSL_set_fd failed: %s\n",
243                                 ERR_reason_error_string(ERR_get_error()));
244                 SSL_CTX_free(ssl_ctx);
245                 ssl_ctx = NULL;
246                 SSL_free(ssl);
247                 ssl = NULL;
248                 return 0;
249         }
250
251         sln_printf("Requesting encryption...\r");
252         sln_flush();
253
254         /* Ready to start SSL/TLS */
255         serv_puts("STLS");
256         serv_gets(buf);
257         if (buf[0] != '2') {
258                 err_printf("Server can't start TLS: %s\n", &buf[4]);
259                 return 0;
260         }
261
262         /* Do SSL/TLS handshake */
263         if ((a = SSL_connect(ssl)) < 1) {
264                 err_printf("SSL_connect failed: %s\n",
265                                 ERR_reason_error_string(ERR_get_error()));
266                 SSL_CTX_free(ssl_ctx);
267                 ssl_ctx = NULL;
268                 SSL_free(ssl);
269                 ssl = NULL;
270                 return 0;
271         }
272         BIO_set_close(ssl->rbio, BIO_NOCLOSE);
273         {
274                 int bits, alg_bits;
275
276                 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ssl), &alg_bits);
277                 sln_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
278                                 SSL_CIPHER_get_version(SSL_get_current_cipher(ssl)),
279                                 SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)),
280                                 bits, alg_bits);
281         }
282         ssl_is_connected = 1;
283         return 1;
284 #else
285         return 0;
286 #endif /* HAVE_OPENSSL */
287 }
288
289
290 /*
291  * void endtls() - end SSL/TLS session
292  */
293 void endtls(void)
294 {
295 #ifdef HAVE_OPENSSL
296         if (ssl) {
297                 SSL_shutdown(ssl);
298                 SSL_free(ssl);
299                 ssl = NULL;
300         }
301         ssl_is_connected = 0;
302         if (ssl_ctx) {
303                 SSL_CTX_free(ssl_ctx);
304                 ssl_ctx = NULL;
305         }
306 #endif
307 }