Implemented client TLS stream
Showing
1 changed file
with
203 additions
and
10 deletions
... | @@ -32,6 +32,8 @@ | ... | @@ -32,6 +32,8 @@ |
32 | #include <mailutils/nls.h> | 32 | #include <mailutils/nls.h> |
33 | #include <mailutils/stream.h> | 33 | #include <mailutils/stream.h> |
34 | 34 | ||
35 | #include <lbuf.h> | ||
36 | |||
35 | #ifdef WITH_TLS | 37 | #ifdef WITH_TLS |
36 | 38 | ||
37 | #include <gnutls/gnutls.h> | 39 | #include <gnutls/gnutls.h> |
... | @@ -44,9 +46,10 @@ static char *ssl_cert = NULL; | ... | @@ -44,9 +46,10 @@ static char *ssl_cert = NULL; |
44 | static char *ssl_key = NULL; | 46 | static char *ssl_key = NULL; |
45 | static char *ssl_cafile = NULL; | 47 | static char *ssl_cafile = NULL; |
46 | 48 | ||
47 | #define ARG_SSL_CERT 1 | 49 | #define ARG_TLS 1 |
48 | #define ARG_SSL_KEY 2 | 50 | #define ARG_SSL_CERT 2 |
49 | #define ARG_SSL_CAFILE 3 | 51 | #define ARG_SSL_KEY 3 |
52 | #define ARG_SSL_CAFILE 4 | ||
50 | 53 | ||
51 | static struct argp_option _tls_argp_options[] = { | 54 | static struct argp_option _tls_argp_options[] = { |
52 | {NULL, 0, NULL, 0, N_("Encryption options"), 0}, | 55 | {NULL, 0, NULL, 0, N_("Encryption options"), 0}, |
... | @@ -62,8 +65,17 @@ static struct argp_option _tls_argp_options[] = { | ... | @@ -62,8 +65,17 @@ static struct argp_option _tls_argp_options[] = { |
62 | static error_t | 65 | static error_t |
63 | _tls_argp_parser (int key, char *arg, struct argp_state *state) | 66 | _tls_argp_parser (int key, char *arg, struct argp_state *state) |
64 | { | 67 | { |
68 | static int tls_enable = 1; | ||
69 | |||
65 | switch (key) | 70 | switch (key) |
66 | { | 71 | { |
72 | case ARG_TLS: | ||
73 | if (!arg || strcasecmp (arg, "yes") == 0) | ||
74 | tls_enable = 1; | ||
75 | else if (strcasecmp (arg, "no") == 0) | ||
76 | tls_enable = 0; | ||
77 | break; | ||
78 | |||
67 | case ARG_SSL_CERT: | 79 | case ARG_SSL_CERT: |
68 | ssl_cert = arg; | 80 | ssl_cert = arg; |
69 | break; | 81 | break; |
... | @@ -76,6 +88,11 @@ _tls_argp_parser (int key, char *arg, struct argp_state *state) | ... | @@ -76,6 +88,11 @@ _tls_argp_parser (int key, char *arg, struct argp_state *state) |
76 | ssl_cafile = arg; | 88 | ssl_cafile = arg; |
77 | break; | 89 | break; |
78 | 90 | ||
91 | case ARGP_KEY_FINI: | ||
92 | if (tls_enable) | ||
93 | mu_init_tls_libs (); | ||
94 | break; | ||
95 | |||
79 | default: | 96 | default: |
80 | return ARGP_ERR_UNKNOWN; | 97 | return ARGP_ERR_UNKNOWN; |
81 | } | 98 | } |
... | @@ -104,6 +121,34 @@ mu_tls_init_argp () | ... | @@ -104,6 +121,34 @@ mu_tls_init_argp () |
104 | } | 121 | } |
105 | } | 122 | } |
106 | 123 | ||
124 | static struct argp_option _tls_argp_client_options[] = { | ||
125 | {"tls", ARG_TLS, N_("BOOL"), OPTION_ARG_OPTIONAL, | ||
126 | N_("Enable TLS support") }, | ||
127 | {NULL, 0, NULL, 0, NULL, 0} | ||
128 | }; | ||
129 | |||
130 | static struct argp _tls_client_argp = { | ||
131 | _tls_argp_client_options, | ||
132 | _tls_argp_parser | ||
133 | }; | ||
134 | |||
135 | static struct argp_child _tls_argp_client_child = { | ||
136 | &_tls_client_argp, | ||
137 | 0, | ||
138 | NULL, | ||
139 | 0 | ||
140 | }; | ||
141 | |||
142 | void | ||
143 | mu_tls_init_client_argp () | ||
144 | { | ||
145 | if (mu_register_capa ("tls", &_tls_argp_client_child)) | ||
146 | { | ||
147 | mu_error (_("INTERNAL ERROR: cannot register argp capability tls")); | ||
148 | abort (); | ||
149 | } | ||
150 | } | ||
151 | |||
107 | int | 152 | int |
108 | mu_check_tls_environment (void) | 153 | mu_check_tls_environment (void) |
109 | { | 154 | { |
... | @@ -142,16 +187,22 @@ mu_check_tls_environment (void) | ... | @@ -142,16 +187,22 @@ mu_check_tls_environment (void) |
142 | return 1; | 187 | return 1; |
143 | } | 188 | } |
144 | 189 | ||
190 | int mu_tls_enable = 0; | ||
191 | |||
145 | int | 192 | int |
146 | mu_init_tls_libs (void) | 193 | mu_init_tls_libs (void) |
147 | { | 194 | { |
148 | return !gnutls_global_init (); /* Returns 1 on success */ | 195 | if (!mu_tls_enable) |
196 | mu_tls_enable = !gnutls_global_init (); /* Returns 1 on success */ | ||
197 | return mu_tls_enable; | ||
149 | } | 198 | } |
150 | 199 | ||
151 | void | 200 | void |
152 | mu_deinit_tls_libs (void) | 201 | mu_deinit_tls_libs (void) |
153 | { | 202 | { |
203 | if (mu_tls_enable) | ||
154 | gnutls_global_deinit (); | 204 | gnutls_global_deinit (); |
205 | mu_tls_enable = 0; | ||
155 | } | 206 | } |
156 | 207 | ||
157 | static void | 208 | static void |
... | @@ -194,8 +245,10 @@ struct _tls_stream { | ... | @@ -194,8 +245,10 @@ struct _tls_stream { |
194 | int ifd; | 245 | int ifd; |
195 | int ofd; | 246 | int ofd; |
196 | int last_err; | 247 | int last_err; |
248 | struct _line_buffer *lb; | ||
197 | enum tls_stream_state state; | 249 | enum tls_stream_state state; |
198 | gnutls_session session; | 250 | gnutls_session session; |
251 | stream_t tcp_str; | ||
199 | }; | 252 | }; |
200 | 253 | ||
201 | 254 | ||
... | @@ -203,11 +256,16 @@ static void | ... | @@ -203,11 +256,16 @@ static void |
203 | _tls_destroy (stream_t stream) | 256 | _tls_destroy (stream_t stream) |
204 | { | 257 | { |
205 | struct _tls_stream *s = stream_get_owner (stream); | 258 | struct _tls_stream *s = stream_get_owner (stream); |
259 | if (x509_cred) | ||
260 | gnutls_certificate_free_credentials (x509_cred); | ||
206 | if (s->session && s->state == state_closed) | 261 | if (s->session && s->state == state_closed) |
207 | { | 262 | { |
208 | gnutls_deinit (s->session); | 263 | gnutls_deinit (s->session); |
209 | s->state = state_destroyed; | 264 | s->state = state_destroyed; |
210 | } | 265 | } |
266 | if (s->tcp_str) | ||
267 | stream_destroy (&s->tcp_str, stream_get_owner (s->tcp_str)); | ||
268 | _auth_lb_destroy (&s->lb); | ||
211 | free (s); | 269 | free (s); |
212 | } | 270 | } |
213 | 271 | ||
... | @@ -242,7 +300,8 @@ _tls_readline (stream_t stream, char *optr, size_t osize, | ... | @@ -242,7 +300,8 @@ _tls_readline (stream_t stream, char *optr, size_t osize, |
242 | if (!stream || s->state != state_open || osize < 2) | 300 | if (!stream || s->state != state_open || osize < 2) |
243 | return EINVAL; | 301 | return EINVAL; |
244 | 302 | ||
245 | osize--; /* Allow for terminating zero */ | 303 | if (_auth_lb_level (s->lb) == 0) |
304 | { | ||
246 | ptr = optr; | 305 | ptr = optr; |
247 | rdsize = 0; | 306 | rdsize = 0; |
248 | do | 307 | do |
... | @@ -257,11 +316,14 @@ _tls_readline (stream_t stream, char *optr, size_t osize, | ... | @@ -257,11 +316,14 @@ _tls_readline (stream_t stream, char *optr, size_t osize, |
257 | } | 316 | } |
258 | while (osize > rdsize && rc > 0 && ptr[rdsize-1] != '\n'); | 317 | while (osize > rdsize && rc > 0 && ptr[rdsize-1] != '\n'); |
259 | 318 | ||
260 | ptr[rdsize] = 0; | 319 | _auth_lb_grow (s->lb, ptr, rdsize); |
320 | } | ||
261 | 321 | ||
322 | osize--; /* Allow for terminating zero */ | ||
323 | rdsize = _auth_lb_readline (s->lb, optr, osize); | ||
324 | optr[rdsize] = 0; | ||
262 | if (nbytes) | 325 | if (nbytes) |
263 | *nbytes = rdsize; | 326 | *nbytes = rdsize; |
264 | |||
265 | return 0; | 327 | return 0; |
266 | } | 328 | } |
267 | 329 | ||
... | @@ -359,6 +421,81 @@ _tls_open (stream_t stream) | ... | @@ -359,6 +421,81 @@ _tls_open (stream_t stream) |
359 | return 0; | 421 | return 0; |
360 | } | 422 | } |
361 | 423 | ||
424 | static int | ||
425 | prepare_client_session (struct _tls_stream *s) | ||
426 | { | ||
427 | int rc; | ||
428 | static int protocol_priority[] = {GNUTLS_TLS1, GNUTLS_SSL3, 0}; | ||
429 | static int kx_priority[] = {GNUTLS_KX_RSA, 0}; | ||
430 | static int cipher_priority[] = {GNUTLS_CIPHER_3DES_CBC, | ||
431 | GNUTLS_CIPHER_ARCFOUR_128, | ||
432 | 0}; | ||
433 | static int comp_priority[] = {GNUTLS_COMP_NULL, 0}; | ||
434 | static int mac_priority[] = {GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0}; | ||
435 | |||
436 | gnutls_init (&s->session, GNUTLS_CLIENT); | ||
437 | gnutls_protocol_set_priority (s->session, protocol_priority); | ||
438 | gnutls_cipher_set_priority (s->session, cipher_priority); | ||
439 | gnutls_compression_set_priority (s->session, comp_priority); | ||
440 | gnutls_kx_set_priority (s->session, kx_priority); | ||
441 | gnutls_mac_set_priority (s->session, mac_priority); | ||
442 | |||
443 | gnutls_certificate_allocate_credentials (&x509_cred); | ||
444 | if (ssl_cafile) | ||
445 | { | ||
446 | rc = gnutls_certificate_set_x509_trust_file (x509_cred, | ||
447 | ssl_cafile, | ||
448 | GNUTLS_X509_FMT_PEM); | ||
449 | if (rc < 0) | ||
450 | { | ||
451 | s->last_err = rc; | ||
452 | return -1; | ||
453 | } | ||
454 | } | ||
455 | |||
456 | gnutls_credentials_set (s->session, GNUTLS_CRD_CERTIFICATE, x509_cred); | ||
457 | |||
458 | gnutls_transport_set_ptr2 (s->session, (gnutls_transport_ptr) s->ifd, | ||
459 | (gnutls_transport_ptr) s->ofd); | ||
460 | |||
461 | return 0; | ||
462 | } | ||
463 | |||
464 | static int | ||
465 | _tls_open_client (stream_t stream) | ||
466 | { | ||
467 | struct _tls_stream *s = stream_get_owner (stream); | ||
468 | int rc = 0; | ||
469 | |||
470 | switch (s->state) | ||
471 | { | ||
472 | case state_closed: | ||
473 | gnutls_certificate_free_credentials (x509_cred); | ||
474 | if (s->session) | ||
475 | gnutls_deinit (s->session); | ||
476 | /* FALLTHROUGH */ | ||
477 | |||
478 | case state_init: | ||
479 | prepare_client_session (s); | ||
480 | rc = gnutls_handshake (s->session); | ||
481 | if (rc < 0) | ||
482 | { | ||
483 | s->last_err = rc; | ||
484 | gnutls_deinit (s->session); | ||
485 | s->state = state_init; | ||
486 | return -1; | ||
487 | } | ||
488 | break; | ||
489 | |||
490 | default: | ||
491 | return -1; | ||
492 | } | ||
493 | |||
494 | /* FIXME: if (ssl_cafile) verify_certificate (s->session); */ | ||
495 | s->state = state_open; | ||
496 | return 0; | ||
497 | } | ||
498 | |||
362 | int | 499 | int |
363 | _tls_strerror (stream_t stream, char **pstr) | 500 | _tls_strerror (stream_t stream, char **pstr) |
364 | { | 501 | { |
... | @@ -367,12 +504,12 @@ _tls_strerror (stream_t stream, char **pstr) | ... | @@ -367,12 +504,12 @@ _tls_strerror (stream_t stream, char **pstr) |
367 | return 0; | 504 | return 0; |
368 | } | 505 | } |
369 | 506 | ||
370 | /* FIXME: It returns only input fd */ | ||
371 | int | 507 | int |
372 | _tls_get_fd (stream_t stream, int *pfd) | 508 | _tls_get_fd (stream_t stream, int *pfd1, int *pfd2) |
373 | { | 509 | { |
374 | struct _tls_stream *s = stream_get_owner (stream); | 510 | struct _tls_stream *s = stream_get_owner (stream); |
375 | *pfd = s->ifd; | 511 | *pfd1 = s->ifd; |
512 | *pfd2 = s->ofd; | ||
376 | return 0; | 513 | return 0; |
377 | } | 514 | } |
378 | 515 | ||
... | @@ -408,11 +545,67 @@ tls_stream_create (stream_t *stream, int in_fd, int out_fd, int flags) | ... | @@ -408,11 +545,67 @@ tls_stream_create (stream_t *stream, int in_fd, int out_fd, int flags) |
408 | stream_set_destroy (*stream, _tls_destroy, s); | 545 | stream_set_destroy (*stream, _tls_destroy, s); |
409 | stream_set_strerror (*stream, _tls_strerror, s); | 546 | stream_set_strerror (*stream, _tls_strerror, s); |
410 | stream_set_fd (*stream, _tls_get_fd, s); | 547 | stream_set_fd (*stream, _tls_get_fd, s); |
548 | _auth_lb_create (&s->lb); | ||
549 | |||
550 | s->state = state_init; | ||
551 | return 0; | ||
552 | } | ||
553 | |||
554 | int | ||
555 | tls_stream_create_client (stream_t *stream, int in_fd, int out_fd, int flags) | ||
556 | { | ||
557 | struct _tls_stream *s; | ||
558 | int rc; | ||
559 | |||
560 | if (stream == NULL) | ||
561 | return EINVAL; | ||
562 | |||
563 | s = calloc (1, sizeof (*s)); | ||
564 | if (s == NULL) | ||
565 | return ENOMEM; | ||
566 | |||
567 | s->ifd = in_fd; | ||
568 | s->ofd = out_fd; | ||
569 | |||
570 | rc = stream_create (stream, flags|MU_STREAM_NO_CHECK, s); | ||
571 | if (rc) | ||
572 | { | ||
573 | free (s); | ||
574 | return rc; | ||
575 | } | ||
576 | |||
577 | stream_set_open (*stream, _tls_open_client, s); | ||
578 | stream_set_close (*stream, _tls_close, s); | ||
579 | stream_set_read (*stream, _tls_read, s); | ||
580 | stream_set_readline (*stream, _tls_readline, s); | ||
581 | stream_set_write (*stream, _tls_write, s); | ||
582 | stream_set_flush (*stream, _tls_flush, s); | ||
583 | stream_set_destroy (*stream, _tls_destroy, s); | ||
584 | stream_set_strerror (*stream, _tls_strerror, s); | ||
585 | stream_set_fd (*stream, _tls_get_fd, s); | ||
586 | _auth_lb_create (&s->lb); | ||
411 | 587 | ||
412 | s->state = state_init; | 588 | s->state = state_init; |
413 | return 0; | 589 | return 0; |
414 | } | 590 | } |
415 | 591 | ||
592 | int | ||
593 | tls_stream_create_client_from_tcp (stream_t *stream, stream_t tcp_str, | ||
594 | int flags) | ||
595 | { | ||
596 | int rc, fd; | ||
597 | |||
598 | stream_get_fd (tcp_str, &fd); | ||
599 | rc = tls_stream_create_client (stream, fd, fd, flags); | ||
600 | if (rc == 0) | ||
601 | { | ||
602 | struct _tls_stream *s = stream_get_owner (*stream); | ||
603 | s->tcp_str = tcp_str; | ||
604 | } | ||
605 | return rc; | ||
606 | } | ||
607 | |||
608 | |||
416 | #endif /* WITH_TLS */ | 609 | #endif /* WITH_TLS */ |
417 | 610 | ||
418 | /* EOF */ | 611 | /* EOF */ | ... | ... |
-
Please register or sign in to post a comment