Blame view

libmu_auth/tls.c 16.6 KB
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2 3
   Copyright (C) 2003-2004, 2007-2012, 2014-2015 Free Software
   Foundation, Inc.
4

5 6 7
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
8
   version 3 of the License, or (at your option) any later version.
9

10
   This library is distributed in the hope that it will be useful,
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12 13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
14

15
   You should have received a copy of the GNU Lesser General
16 17
   Public License along with this library.  If not, see
   <http://www.gnu.org/licenses/>. */
18 19 20 21 22

#if HAVE_CONFIG_H
# include <config.h>
#endif

23
#include <stdio.h>
Sergey Poznyakoff authored
24
#include <stdlib.h>
25 26 27
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
28
#include <string.h>
29 30 31 32 33

#include <mailutils/error.h>
#include <mailutils/mu_auth.h>
#include <mailutils/tls.h>
#include <mailutils/nls.h>
34
#include <mailutils/stream.h>
35
#include <mailutils/errno.h>
36
#include <mailutils/util.h>
37

Sergey Poznyakoff authored
38 39 40 41 42 43 44
struct mu_tls_module_config mu_tls_module_config = {
#ifdef WITH_TLS
  1 /* enable by default */
#else
  0
#endif
};
45
  
46
int
47
mu_tls_module_init (enum mu_gocs_op op, void *data)
48
{
49
  switch (op)
50
    {
51 52 53 54
    case mu_gocs_op_set:
      if (data)
	memcpy (&mu_tls_module_config, data, sizeof mu_tls_module_config);
      break;
55
      
56
    case mu_gocs_op_flush:
57
#ifdef WITH_TLS
58
      mu_init_tls_libs (0);
59
#endif    
Sergey Poznyakoff authored
60
      break;
61 62 63 64
    }
  return 0;
}

65
#ifdef WITH_TLS
66

67
#include <gnutls/gnutls.h>
68
#include <mailutils/sys/tls-stream.h>
69

70
static gnutls_certificate_server_credentials x509_cred;
71

72
/* Return: zero means NOT READY, one means READY */
73 74 75
int
mu_check_tls_environment (void)
{
Sergey Poznyakoff authored
76 77
  if (!mu_tls_module_config.enable)
    return 0;
78
  if (mu_tls_module_config.ssl_cert && mu_tls_module_config.ssl_key)
79
    {
80 81
      int rc = mu_file_safety_check (mu_tls_module_config.ssl_cert,
				     mu_tls_module_config.ssl_cert_safety_checks,
82
				     -1, NULL);
83
      if (rc)
84
	{
85 86
	  mu_error ("%s: %s", mu_tls_module_config.ssl_cert, 
		    mu_strerror (rc));
87 88
	  return 0;
	}
89 90
      rc = mu_file_safety_check (mu_tls_module_config.ssl_key,
				 mu_tls_module_config.ssl_key_safety_checks,
91
				 -1, NULL);
92
      if (rc)
93
	{
94 95
	  mu_error ("%s: %s", mu_tls_module_config.ssl_key, 
		    mu_strerror (rc));
96 97 98
	  return 0;
	}

99
      if (mu_tls_module_config.ssl_cafile)
100
	{
101 102 103 104 105 106 107 108 109
	  rc = mu_file_safety_check (mu_tls_module_config.ssl_cafile,
			       mu_tls_module_config.ssl_cafile_safety_checks,
				     -1, NULL);
	  if (rc)
	    {
	      mu_error ("%s: %s", mu_tls_module_config.ssl_cafile, 
			mu_strerror (rc));
	      return 0;
	    }
110 111 112 113 114 115 116 117
	}
    }
  else
    return 0;

  return 1;
}

118 119
int mu_tls_enable = 0;

Sergey Poznyakoff authored
120
#ifdef DEBUG_TLS
121 122 123 124 125 126 127
void
_mu_gtls_logger(int level, const char *text)
{
  mu_diag_output (MU_DIAG_DEBUG, "GnuTLS(%d): %s", level, text);
}
#endif

128
int
129
mu_init_tls_libs (int x509_setup)
130
{
Sergey Poznyakoff authored
131
  if (!mu_tls_enable)
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
    {
      int rc;
      if ((rc = gnutls_global_init ()) == GNUTLS_E_SUCCESS)
	mu_tls_enable = 1;
      else
	{
	  mu_error ("gnutls_global_init: %s", gnutls_strerror (rc));
	  return 0;
	}
    }

  if (x509_setup && !x509_cred)
    {
      gnutls_certificate_allocate_credentials (&x509_cred);
      if (mu_tls_module_config.ssl_cafile)
	gnutls_certificate_set_x509_trust_file (x509_cred,
						mu_tls_module_config.ssl_cafile,
						GNUTLS_X509_FMT_PEM);
  
      gnutls_certificate_set_x509_key_file (x509_cred,
					    mu_tls_module_config.ssl_cert, 
					    mu_tls_module_config.ssl_key,
					    GNUTLS_X509_FMT_PEM);
    }
  
Sergey Poznyakoff authored
157
#ifdef DEBUG_TLS
158 159 160
  gnutls_global_set_log_function (_mu_gtls_logger);
  gnutls_global_set_log_level (110);
#endif
161
  return mu_tls_enable;
162 163
}

Sergey Poznyakoff authored
164
void
165 166
mu_deinit_tls_libs (void)
{
167
  if (mu_tls_enable)
168 169 170 171 172
    {
      if (x509_cred)
	gnutls_certificate_free_credentials (x509_cred);
      gnutls_global_deinit ();
    }
173
  mu_tls_enable = 0;
174 175
}

176 177 178
static char default_priority_string[] = "NORMAL";

static gnutls_session_t
179 180
initialize_tls_session (void)
{
181
  gnutls_session_t session = 0;
182 183

  gnutls_init (&session, GNUTLS_SERVER);
184 185 186 187 188
  gnutls_priority_set_direct (session,
			      mu_tls_module_config.priorities
			        ? mu_tls_module_config.priorities
			        : default_priority_string,
			      NULL);
189 190 191
  gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred);
  gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);

192
  return session;
193 194
}

195

196 197
/* ************************* TLS Stream Support **************************** */

198 199
static int
_tls_io_close (mu_stream_t stream)
200
{
201
  struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
202
  
203
  return mu_stream_close (sp->transport);
204
}
205

206 207 208 209
static void
_tls_io_done (struct _mu_stream *stream)
{
  struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
210
  mu_stream_unref (sp->transport);
211
}
212

213
static int
214
_tls_io_flush (struct _mu_stream *stream)
215
{
216 217
  struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
  return mu_stream_flush (sp->transport);
218 219 220
}

static int
221 222
_tls_io_read (struct _mu_stream *stream, char *buf, size_t bufsize,
	      size_t *pnread)
223
{
224
  struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
225 226
  int rc;
  
227
  if (sp->up->state != state_open)
228
    return EINVAL;
229 230
  rc = gnutls_record_recv (sp->up->session, buf, bufsize);
  if (rc >= 0)
231
    {
232 233
      *pnread = rc;
      return 0;
234
    }
235 236
  sp->up->tls_err = rc;
  return EIO;
237 238 239
}

static int
240 241
_tls_io_write (struct _mu_stream *stream, const char *buf, size_t bufsize,
	    size_t *pnwrite)
242
{
243
  struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
244 245
  int rc;
  
246
  if (sp->up->state != state_open)
247 248 249 250 251 252 253 254 255 256
    return EINVAL;

  /* gnutls_record_send() docs say:
       If the EINTR is returned by the internal push function (write())
       then GNUTLS_E_INTERRUPTED, will be returned. If GNUTLS_E_INTERRUPTED or
       GNUTLS_E_AGAIN is returned you must call this function again, with the
       same parameters. Otherwise the write operation will be
       corrupted and the connection will be terminated. */
    
  do
257
    rc = gnutls_record_send (sp->up->session, buf, bufsize);
258 259 260 261
  while (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN);

  if (rc < 0)
    {
262
      sp->up->tls_err = rc;
263 264 265
      return EIO;
    }

266
  *pnwrite = rc;
267 268 269 270 271

  return 0;
}

static int
272
_tls_rd_wait (struct _mu_stream *stream, int *pflags, struct timeval *tvp)
273
{
274 275 276 277 278 279
  struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
  int rc = EINVAL;
  
  if (*pflags == MU_STREAM_READY_RD)
    rc = mu_stream_wait (sp->transport, pflags, tvp);
  return rc;
280 281 282
}

static int
283
_tls_wr_wait (struct _mu_stream *stream, int *pflags, struct timeval *tvp)
284
{
285 286
  struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;
  int rc = EINVAL;
287
  
288 289 290 291 292 293
  if (*pflags == MU_STREAM_READY_WR)
    rc = mu_stream_wait (sp->transport, pflags, tvp);
  return rc;
}

static int
294
_tls_io_ioctl (struct _mu_stream *stream, int code, int opcode, void *arg)
295 296 297
{
  struct _mu_tls_io_stream *sp = (struct _mu_tls_io_stream *) stream;

298
  switch (code)
299
    {
300
    case MU_IOCTL_TRANSPORT:
301 302
      if (!arg)
	return EINVAL;
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
      else
	{
	  mu_transport_t *ptrans = arg;
	  switch (opcode)
	    {
	    case MU_IOCTL_OP_GET:
	      ptrans[0] = (mu_transport_t) sp->transport;
	      ptrans[1] = NULL;
	      break;

	    case MU_IOCTL_OP_SET:
	      return ENOSYS;

	    default:
	      return EINVAL;
	    }
	}
320 321 322
      break;

    default:
323
      return ENOSYS;
324 325 326 327 328 329 330 331 332 333 334 335
    }
  return 0;
}

static int
_mu_tls_io_stream_create (mu_stream_t *pstream,
			  mu_stream_t transport, int flags,
			  struct _mu_tls_stream *master)
{
  struct _mu_tls_io_stream *sp;

  sp = (struct _mu_tls_io_stream *)
336
    _mu_stream_create (sizeof (*sp), (flags & MU_STREAM_RDWR) | _MU_STR_OPEN);
337 338 339 340
  if (!sp)
    return ENOMEM;

  if (flags & MU_STREAM_READ)
341
    {
342 343
      sp->stream.read = _tls_io_read; 
      sp->stream.wait = _tls_rd_wait;
344
      mu_stream_set_buffer ((mu_stream_t) sp, mu_buffer_full, 0);
345
    }
346
  else
347
    {
348 349
      sp->stream.write = _tls_io_write;
      sp->stream.wait = _tls_wr_wait;
350
      mu_stream_set_buffer ((mu_stream_t) sp, mu_buffer_line, 0);
351
    }
352 353 354 355 356
  sp->stream.flush = _tls_io_flush;
  sp->stream.close = _tls_io_close;
  sp->stream.done = _tls_io_done; 
  sp->stream.ctl = _tls_io_ioctl;

357
  mu_stream_ref (transport);
358 359 360
  sp->transport = transport;
  sp->up = master;
  *pstream = (mu_stream_t) sp;
Sergey Poznyakoff authored
361
  return 0;
362 363
}

364 365

static ssize_t
366
_tls_stream_pull (gnutls_transport_ptr_t fd, void *buf, size_t size)
367
{
368
  mu_stream_t stream = fd;
369 370 371
  int rc;
  size_t rdbytes;
	
372
  while ((rc = mu_stream_read (stream, buf, size, &rdbytes)) == EAGAIN)
373
    ;
374
  
375 376 377 378 379 380
  if (rc)
    return -1;
  return rdbytes;
}

static ssize_t
381
_tls_stream_push (gnutls_transport_ptr_t fd, const void *buf, size_t size)
382
{
383
  mu_stream_t stream = fd;
384 385
  int rc;

386
  rc = mu_stream_write (stream, buf, size, &size);
387
  if (rc)
388
    {
389 390
      mu_error ("_tls_stream_push: %s",
		mu_stream_strerror (stream, rc)); /* FIXME */
391 392
      return -1;
    }
393

394
  mu_stream_flush (stream);
395 396 397 398
  return size;
}


399
static int
400
_tls_server_open (mu_stream_t stream)
401
{
402
  struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
403
  int rc = 0;
404
  mu_transport_t transport[2];
405
  
Sergey Poznyakoff authored
406
  if (!mu_tls_module_config.enable)
407
    return MU_ERR_DISABLED;
408
  if (!stream || sp->state != state_init)
409
    return EINVAL;
410

411
  mu_init_tls_libs (1);
Sergey Poznyakoff authored
412
  
413
  sp->session = initialize_tls_session ();
414
  mu_stream_ioctl (stream, MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, transport);
415
  gnutls_transport_set_ptr2 (sp->session,
416 417
			     (gnutls_transport_ptr_t) transport[0],
			     (gnutls_transport_ptr_t) transport[1]);
418 419
  gnutls_transport_set_pull_function (sp->session, _tls_stream_pull);
  gnutls_transport_set_push_function (sp->session, _tls_stream_push);
420
  
421
  rc = gnutls_handshake (sp->session);
422
  if (rc < 0)
423
    {
424 425
      gnutls_deinit (sp->session);
      sp->tls_err = rc;
426
      return EIO;
427
    }
428
  sp->state = state_open;
429
  return 0;
430 431
}

432
static int
433
prepare_client_session (mu_stream_t stream)
434
{
435
  struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
436
  int rc;
437
  mu_transport_t transport[2];
438

439
  gnutls_init (&sp->session, GNUTLS_CLIENT);
440 441 442 443 444
  gnutls_priority_set_direct (sp->session,
			      mu_tls_module_config.priorities
			        ? mu_tls_module_config.priorities
			        : default_priority_string,
			      NULL);
445
  gnutls_certificate_allocate_credentials (&x509_cred);
446
  if (mu_tls_module_config.ssl_cafile)
447 448
    {
      rc = gnutls_certificate_set_x509_trust_file (x509_cred,
449
					      mu_tls_module_config.ssl_cafile,
450 451 452
						   GNUTLS_X509_FMT_PEM);
      if (rc < 0)
	{
453
	  sp->tls_err = rc;
454 455 456 457
	  return -1;
	}
    }

458
  gnutls_credentials_set (sp->session, GNUTLS_CRD_CERTIFICATE, x509_cred);
459

460
  mu_stream_ioctl (stream, MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, transport);
461
  gnutls_transport_set_ptr2 (sp->session,
462 463
			     (gnutls_transport_ptr_t) transport[0],
			     (gnutls_transport_ptr_t) transport[1]);
464 465
  gnutls_transport_set_pull_function (sp->session, _tls_stream_pull);
  gnutls_transport_set_push_function (sp->session, _tls_stream_push);
466 467 468
      
  return 0;
}
469

470
static int
471
_tls_client_open (mu_stream_t stream)
472
{
473
  struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
474 475
  int rc = 0;
  
476
  switch (sp->state)
477 478
    {
    case state_closed:
479 480
      if (sp->session)
	gnutls_deinit (sp->session);
481 482 483
      /* FALLTHROUGH */
      
    case state_init:
484
      mu_init_tls_libs (0);
485
      prepare_client_session (stream);
486
      rc = gnutls_handshake (sp->session);
487 488
      if (rc < 0)
	{
489 490 491
	  sp->tls_err = rc;
	  gnutls_deinit (sp->session);
	  sp->state = state_init;
492
	  return MU_ERR_FAILURE;
493 494 495 496
	}
      break;

    default:
497
      return MU_ERR_FAILURE;
498 499 500
    }

  /* FIXME: if (ssl_cafile) verify_certificate (s->session); */
501
  sp->state = state_open;
502 503 504
  return 0;
}

505 506 507
static int
_tls_read (struct _mu_stream *str, char *buf, size_t bufsize,
	   size_t *pnread)
508
{
509 510
  struct _mu_tls_stream *sp = (struct _mu_tls_stream *)str;
  return mu_stream_read (sp->transport[0], buf, bufsize, pnread);
511 512
}

513 514 515
static int
_tls_write (struct _mu_stream *str, const char *buf, size_t bufsize,
	    size_t *pnwrite)
516
{
517 518
  struct _mu_tls_stream *sp = (struct _mu_tls_stream *)str;
  return mu_stream_write (sp->transport[1], buf, bufsize, pnwrite);
519 520
}

521
static int
522
_tls_ioctl (struct _mu_stream *stream, int code, int opcode, void *arg)
523
{
524
  struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
525

Sergey Poznyakoff authored
526
  switch (code)
527
    {
528 529
    case MU_IOCTL_TRANSPORT:
      switch (opcode)
530
	{
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
	case MU_IOCTL_OP_GET:
	  if (!arg)
	    return EINVAL;
	  else
	    {
	      mu_transport_t *ptrans, trans[2];

	      ptrans = arg;
	      mu_stream_ioctl (sp->transport[0], MU_IOCTL_TRANSPORT,
			       MU_IOCTL_OP_GET, trans);
	      ptrans[0] = trans[0];
	      mu_stream_ioctl (sp->transport[1], MU_IOCTL_TRANSPORT,
			       MU_IOCTL_OP_GET, trans);
	      ptrans[1] = trans[0];
	    }
	  break;

	case MU_IOCTL_OP_SET:
	  return ENOSYS;

	default:
	  return EINVAL;
553
	}
554
      break;
555

556
    case MU_IOCTL_TRANSPORT_BUFFER:
557 558 559 560 561
      if (!arg)
	return EINVAL;
      else
	{
	  struct mu_buffer_query *qp = arg;
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
	  switch (opcode)
	    {
	    case MU_IOCTL_OP_GET:
	      if (!MU_TRANSPORT_VALID_TYPE (qp->type) ||
		  !sp->transport[qp->type])
		return EINVAL;
	      return mu_stream_get_buffer (sp->transport[qp->type], qp);

	    case MU_IOCTL_OP_SET:
	      if (!MU_TRANSPORT_VALID_TYPE (qp->type) ||
		  !sp->transport[qp->type])
		return EINVAL;
	      return mu_stream_set_buffer (sp->transport[qp->type],
					   qp->buftype, qp->bufsize);

	    default:
	      return EINVAL;
	    }
580
	}
581
      break;
582
      
583
    default:
584
      return ENOSYS;
585 586 587
    }
  return 0;
}
588

589 590 591 592 593 594 595 596 597 598 599 600
static int
_tls_wait (struct _mu_stream *stream, int *pflags, struct timeval *tvp)
{
  struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
  int rc = EINVAL;
  
  if (*pflags == MU_STREAM_READY_RD)
    rc = mu_stream_wait (sp->transport[0], pflags, tvp);
  else if (*pflags == MU_STREAM_READY_WR)
    rc = mu_stream_wait (sp->transport[1], pflags, tvp);
  return rc;
}
601

602 603 604 605 606 607
static int
_tls_flush (struct _mu_stream *stream)
{
  struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
  return mu_stream_flush (sp->transport[1]);
}
608

609 610 611 612 613 614
static int
_tls_close (mu_stream_t stream)
{
  struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
  
  if (sp->session && sp->state == state_open)
615
    {
616 617
      gnutls_bye (sp->session, GNUTLS_SHUT_RDWR);
      sp->state = state_closed;
618
    }
619
  
620 621
  mu_stream_close (sp->transport[0]);
  mu_stream_close (sp->transport[1]);
622 623 624
  return 0;
}

625 626
static void
_tls_done (struct _mu_stream *stream)
627
{
628 629 630 631 632 633 634
  struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
  
  if (sp->session && sp->state == state_closed)
    {
      gnutls_deinit (sp->session);
      sp->state = state_destroyed;
    }
635

636 637
  mu_stream_destroy (&sp->transport[0]);
  mu_stream_destroy (&sp->transport[1]);
638
}
639

640 641 642 643 644 645 646 647 648 649 650
static const char *
_tls_error_string (struct _mu_stream *stream, int rc)
{
  if (rc == EIO)
    {
      struct _mu_tls_stream *sp = (struct _mu_tls_stream *) stream;
      return gnutls_strerror (sp->tls_err);
    }
  return mu_strerror (rc);
}

651 652 653 654 655 656 657
static int
_mu_tls_stream_create (mu_stream_t *pstream,
		       int (*openfn) (mu_stream_t stream),
		       mu_stream_t strin, mu_stream_t strout, int flags)
{
  struct _mu_tls_stream *sp;
  int rc;
658
  mu_stream_t stream;
659 660
  
  sp = (struct _mu_tls_stream *)
661
    _mu_stream_create (sizeof (*sp), MU_STREAM_RDWR);
662
  if (!sp)
663 664
    return ENOMEM;

665 666 667 668 669 670 671 672
  sp->stream.read = _tls_read; 
  sp->stream.write = _tls_write;
  sp->stream.flush = _tls_flush;
  sp->stream.open = openfn; 
  sp->stream.close = _tls_close;
  sp->stream.done = _tls_done; 
  sp->stream.ctl = _tls_ioctl;
  sp->stream.wait = _tls_wait;
673
  sp->stream.error_string = _tls_error_string;
674 675 676

  mu_stream_set_buffer (strin, mu_buffer_none, 0);
  mu_stream_set_buffer (strout, mu_buffer_none, 0);
677
  rc = _mu_tls_io_stream_create (&sp->transport[0], strin, MU_STREAM_READ, sp);
678 679
  if (rc)
    {
680 681 682
      free (sp);
      return rc;
    }
683
     
684 685
  rc = _mu_tls_io_stream_create (&sp->transport[1], strout, MU_STREAM_WRITE,
                                 sp);
686 687 688 689
  if (rc)
    {
      free (sp);
      free (sp->transport[0]);
690 691
      return rc;
    }
692

693 694 695 696 697 698 699 700
  stream = (mu_stream_t) sp;
  mu_stream_set_buffer (stream, mu_buffer_line, 0);
  rc = mu_stream_open (stream);
  if (rc)
    mu_stream_destroy (&stream);
  else
    *pstream = stream;
  return rc;
701 702
}

703
int
704 705 706 707 708 709 710 711 712 713 714
mu_tls_server_stream_create (mu_stream_t *pstream,
			     mu_stream_t strin, mu_stream_t strout, int flags)
{
  return _mu_tls_stream_create (pstream,
				_tls_server_open,
				strin, strout, flags);
}

int
mu_tls_client_stream_create (mu_stream_t *pstream,
			     mu_stream_t strin, mu_stream_t strout, int flags)
715
{
716 717 718
  return _mu_tls_stream_create (pstream,
				_tls_client_open,
				strin, strout, flags);
719 720
}

721

722 723 724
#endif /* WITH_TLS */

/* EOF */
725