Commit cae52d8a cae52d8a1a051188177906842ab6b51959f2d5be by Sergey Poznyakoff

Implemented TLS streams. All tls-specific details

are now hidden within this module.
1 parent 0ff215de
Showing 1 changed file with 201 additions and 30 deletions
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
19 # include <config.h> 19 # include <config.h>
20 #endif 20 #endif
21 21
22 #include <stdio.h>
22 #include <unistd.h> 23 #include <unistd.h>
23 #include <sys/types.h> 24 #include <sys/types.h>
24 #include <sys/stat.h> 25 #include <sys/stat.h>
...@@ -28,9 +29,12 @@ ...@@ -28,9 +29,12 @@
28 #include <mailutils/mu_auth.h> 29 #include <mailutils/mu_auth.h>
29 #include <mailutils/tls.h> 30 #include <mailutils/tls.h>
30 #include <mailutils/nls.h> 31 #include <mailutils/nls.h>
32 #include <mailutils/stream.h>
31 33
32 #ifdef WITH_TLS 34 #ifdef WITH_TLS
33 35
36 #include <gnutls/gnutls.h>
37
34 #define DH_BITS 768 38 #define DH_BITS 768
35 39
36 static gnutls_dh_params dh_params; 40 static gnutls_dh_params dh_params;
...@@ -145,14 +149,7 @@ mu_check_tls_environment (void) ...@@ -145,14 +149,7 @@ mu_check_tls_environment (void)
145 int 149 int
146 mu_init_tls_libs (void) 150 mu_init_tls_libs (void)
147 { 151 {
148 int rs = gnutls_global_init (); 152 return !gnutls_global_init (); /* Returns 1 on success */
149
150 if (rs == 0) /* Reverse for tls_available */
151 rs = 1;
152 else
153 rs = 0;
154
155 return rs; /* Returns 1 on success */
156 } 153 }
157 154
158 int 155 int
...@@ -185,14 +182,153 @@ initialize_tls_session (void) ...@@ -185,14 +182,153 @@ initialize_tls_session (void)
185 gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST); 182 gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);
186 gnutls_dh_set_prime_bits (session, DH_BITS); 183 gnutls_dh_set_prime_bits (session, DH_BITS);
187 184
188 return (gnutls_session) session; 185 return session;
189 } 186 }
190 187
191 gnutls_session 188 /* ************************* TLS Stream Support **************************** */
192 mu_init_tls_server (int fd_in, int fd_out) 189
190 enum tls_stream_state {
191 state_init,
192 state_open,
193 state_closed,
194 state_destroyed
195 };
196
197 struct _tls_stream {
198 int ifd;
199 int ofd;
200 int last_err;
201 enum tls_stream_state state;
202 gnutls_session session;
203 };
204
205
206 static void
207 _tls_destroy (stream_t stream)
193 { 208 {
194 int rs = 0; 209 struct _tls_stream *s = stream_get_owner (stream);
195 gnutls_session session = 0; 210 if (s->session && s->state == state_closed)
211 {
212 gnutls_deinit (s->session);
213 s->state = state_destroyed;
214 }
215 free (s);
216 }
217
218 static int
219 _tls_read (stream_t stream, char *optr, size_t osize,
220 off_t offset, size_t *nbytes)
221 {
222 struct _tls_stream *s = stream_get_owner (stream);
223 int rc;
224
225 if (!stream || s->state != state_open)
226 return EINVAL;
227 rc = gnutls_record_recv (s->session, optr, osize);
228 if (rc >= 0)
229 {
230 *nbytes = rc;
231 return 0;
232 }
233 s->last_err = rc;
234 return EIO;
235 }
236
237 static int
238 _tls_readline (stream_t stream, char *optr, size_t osize,
239 off_t offset, size_t *nbytes)
240 {
241 struct _tls_stream *s = stream_get_owner (stream);
242 int rc;
243 char *ptr;
244 size_t rdsize;
245
246 if (!stream || s->state != state_open || osize < 2)
247 return EINVAL;
248
249 osize--; /* Allow for terminating zero */
250 ptr = optr;
251 rdsize = 0;
252 do
253 {
254 rc = gnutls_record_recv (s->session, ptr + rdsize, osize - rdsize);
255 if (rc < 0)
256 {
257 s->last_err = rc;
258 return EIO;
259 }
260 rdsize += rc;
261 }
262 while (osize > rdsize && rc > 0 && ptr[rdsize-1] != '\n');
263
264 ptr[rdsize] = 0;
265
266 if (nbytes)
267 *nbytes = rdsize;
268
269 return 0;
270 }
271
272 static int
273 _tls_write (stream_t stream, const char *iptr, size_t isize,
274 off_t offset, size_t *nbytes)
275 {
276 struct _tls_stream *s = stream_get_owner (stream);
277 int rc;
278
279 if (!stream || s->state != state_open)
280 return EINVAL;
281
282 /* gnutls_record_send() docs say:
283 If the EINTR is returned by the internal push function (write())
284 then GNUTLS_E_INTERRUPTED, will be returned. If GNUTLS_E_INTERRUPTED or
285 GNUTLS_E_AGAIN is returned you must call this function again, with the
286 same parameters. Otherwise the write operation will be
287 corrupted and the connection will be terminated. */
288
289 do
290 rc = gnutls_record_send (s->session, iptr, isize);
291 while (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN);
292
293 if (rc < 0)
294 {
295 s->last_err = rc;
296 return EIO;
297 }
298
299 if (nbytes)
300 *nbytes = rc;
301
302 return 0;
303 }
304
305 static int
306 _tls_flush (stream_t stream)
307 {
308 struct _tls_stream *s = stream_get_owner (stream);
309 /* noop */
310 return 0;
311 }
312
313 static int
314 _tls_close (stream_t stream)
315 {
316 struct _tls_stream *s = stream_get_owner (stream);
317 if (s->session && s->state == state_open)
318 {
319 gnutls_bye (s->session, GNUTLS_SHUT_RDWR);
320 s->state = state_closed;
321 }
322 }
323
324 static int
325 _tls_open (stream_t stream)
326 {
327 struct _tls_stream *s = stream_get_owner (stream);
328 int rc = 0;
329
330 if (!stream || s->state != state_init)
331 return EINVAL;
196 332
197 gnutls_certificate_allocate_credentials (&x509_cred); 333 gnutls_certificate_allocate_credentials (&x509_cred);
198 334
...@@ -200,39 +336,74 @@ mu_init_tls_server (int fd_in, int fd_out) ...@@ -200,39 +336,74 @@ mu_init_tls_server (int fd_in, int fd_out)
200 gnutls_certificate_set_x509_trust_file (x509_cred, ssl_cafile, 336 gnutls_certificate_set_x509_trust_file (x509_cred, ssl_cafile,
201 GNUTLS_X509_FMT_PEM); 337 GNUTLS_X509_FMT_PEM);
202 338
203 rs = gnutls_certificate_set_x509_key_file (x509_cred, 339 rc = gnutls_certificate_set_x509_key_file (x509_cred,
204 ssl_cert, ssl_key, 340 ssl_cert, ssl_key,
205 GNUTLS_X509_FMT_PEM); 341 GNUTLS_X509_FMT_PEM);
206 if (rs < 0) 342 if (rc < 0)
207 { 343 {
208 mu_error (_("cannot parse certificate/key: %s"), gnutls_strerror (rs)); 344 s->last_err = rc;
209 return 0; 345 return EIO;
210 } 346 }
211 347
212 generate_dh_params (); 348 generate_dh_params ();
213 gnutls_certificate_set_dh_params (x509_cred, dh_params); 349 gnutls_certificate_set_dh_params (x509_cred, dh_params);
214 350
215 session = initialize_tls_session (); 351 s->session = initialize_tls_session ();
216 gnutls_transport_set_ptr2 (session, fd_in, fd_out); 352 gnutls_transport_set_ptr2 (s->session, s->ifd, s->ofd);
217 353
218 rs = gnutls_handshake (session); 354 rc = gnutls_handshake (s->session);
219 if (rs < 0) 355 if (rc < 0)
220 { 356 {
221 gnutls_deinit (session); 357 gnutls_deinit (s->session);
222 mu_error (_("TLS/SSL handshake failed: %s"), gnutls_strerror (rs)); 358 s->last_err = rc;
223 return 0; /* failed */ 359 return EIO;
224 } 360 }
225 return (gnutls_session) session; 361 s->state = state_open;
362 return 0;
226 } 363 }
227 364
228 void 365 int
229 mu_deinit_tls_server (gnutls_session session) 366 _tls_strerror (stream_t stream, const char **pstr)
230 { 367 {
231 if (session) 368 struct _tls_stream *s = stream_get_owner (stream);
369 *pstr = gnutls_strerror (s->last_err);
370 return 0;
371 }
372
373 int
374 tls_stream_create (stream_t *stream, int in_fd, int out_fd, int flags)
375 {
376 struct _tls_stream *s;
377 int rc;
378
379 if (stream == NULL)
380 return EINVAL;
381
382 s = calloc (1, sizeof (*s));
383 if (s == NULL)
384 return ENOMEM;
385
386 s->ifd = in_fd;
387 s->ofd = out_fd;
388
389 rc = stream_create (stream, flags|MU_STREAM_NO_CHECK, s);
390 if (rc)
232 { 391 {
233 gnutls_bye (session, GNUTLS_SHUT_RDWR); 392 free (s);
234 gnutls_deinit (session); 393 return rc;
235 } 394 }
395
396 stream_set_open (*stream, _tls_open, s);
397 stream_set_close (*stream, _tls_close, s);
398 stream_set_read (*stream, _tls_read, s);
399 stream_set_readline (*stream, _tls_readline, s);
400 stream_set_write (*stream, _tls_write, s);
401 stream_set_flush (*stream, _tls_flush, s);
402 stream_set_destroy (*stream, _tls_destroy, s);
403 stream_set_strerror (*stream, _tls_strerror, s);
404
405 s->state = state_init;
406 return 0;
236 } 407 }
237 408
238 #endif /* WITH_TLS */ 409 #endif /* WITH_TLS */
......