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