Implemented TLS streams. All tls-specific details
are now hidden within this module.
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 */ | ... | ... |
-
Please register or sign in to post a comment