diff options
-rw-r--r-- | doc/texinfo/programs.texi | 9 | ||||
-rw-r--r-- | imap4d/io.c | 143 | ||||
-rw-r--r-- | imap4d/starttls.c | 2 | ||||
-rw-r--r-- | include/mailutils/tls.h | 5 | ||||
-rw-r--r-- | libmu_auth/Makefile.am | 3 | ||||
-rw-r--r-- | libmu_auth/tlsconf.c | 4 | ||||
-rw-r--r-- | libmu_auth/tlsfdstr.c | 683 |
7 files changed, 800 insertions, 49 deletions
diff --git a/doc/texinfo/programs.texi b/doc/texinfo/programs.texi index 88d8d5a26..c26874a06 100644 --- a/doc/texinfo/programs.texi +++ b/doc/texinfo/programs.texi @@ -1820,6 +1820,8 @@ server @var{ipaddr}[:@var{port}] @{ ssl-ca-file @var{file}; # @r{Set the priorities to use on the ciphers, methods, etc.} ssl-priorities @var{string}; + # @r{Set timeout for I/O operations during TLS handshake (seconds).} + handshake-timeout @var{n}; @} # @r{Set server specific ACLs.} @@ -2625,6 +2627,8 @@ tls @{ ssl-ca-file @var{file}; # @r{Set the priorities to use on the ciphers, methods, etc.} ssl-priorities @var{string}; + # @r{Set timeout for I/O operations during TLS handshake (seconds).} + handshake-timeout @var{n}; @} @end example @subheading Description @@ -2651,6 +2655,11 @@ Set the priorities to use on the ciphers, key exchange methods, MACs and compression methods. @end deffn +@deffn {Configuration} handshake-timeout @var{n} +Set the timeout (in seconds) for I/O operations during TLS handshake. +Default value is 10 seconds. +@end deffn + @node tls-file-checks statement @subsection The @code{tls-file-checks} Statement @kwindex tls-file-checks diff --git a/imap4d/io.c b/imap4d/io.c index d24cd210c..238c1a4b8 100644 --- a/imap4d/io.c +++ b/imap4d/io.c @@ -60,49 +60,34 @@ io_setio (int ifd, int ofd, struct mu_tls_config *tls_conf) if (ofd == -1) imap4d_bye (ERR_NO_OFILE); - if (mu_stdio_stream_create (&istream, ifd, MU_STREAM_READ)) - imap4d_bye (ERR_STREAM_CREATE); - mu_stream_set_buffer (istream, mu_buffer_line, 0); - - if (mu_stdio_stream_create (&ostream, ofd, MU_STREAM_WRITE)) - imap4d_bye (ERR_STREAM_CREATE); - mu_stream_set_buffer (ostream, mu_buffer_line, 0); - - /* Combine the two streams into an I/O one. */ if (tls_conf) { - /* Set timeouts for TLS handshake */ - struct timeval tv; - - tv.tv_sec = 10; - tv.tv_usec = 0; - mu_stream_ioctl (istream, MU_IOCTL_TIMEOUT, MU_IOCTL_OP_SET, &tv); - mu_stream_ioctl (ostream, MU_IOCTL_TIMEOUT, MU_IOCTL_OP_SET, &tv); - - rc = mu_tls_stream_create (&str, istream, ostream, - tls_conf, - MU_TLS_SERVER, - 0); + rc = mu_tlsfd_stream_create (&str, ifd, ofd, tls_conf, MU_TLS_SERVER, 0); if (rc) { mu_error (_("failed to create TLS stream: %s"), mu_strerror (rc)); imap4d_bye (ERR_STREAM_CREATE); } - - /* Reset timeouts */ - tv.tv_sec = 0; - tv.tv_usec = 0; - mu_stream_ioctl (istream, MU_IOCTL_TIMEOUT, MU_IOCTL_OP_SET, &tv); - mu_stream_ioctl (ostream, MU_IOCTL_TIMEOUT, MU_IOCTL_OP_SET, &tv); - log_cipher (str); } - else if (mu_iostream_create (&str, istream, ostream)) - imap4d_bye (ERR_STREAM_CREATE); - - mu_stream_unref (istream); - mu_stream_unref (ostream); + else + { + if (mu_stdio_stream_create (&istream, ifd, MU_STREAM_READ)) + imap4d_bye (ERR_STREAM_CREATE); + mu_stream_set_buffer (istream, mu_buffer_line, 0); + + if (mu_stdio_stream_create (&ostream, ofd, MU_STREAM_WRITE)) + imap4d_bye (ERR_STREAM_CREATE); + mu_stream_set_buffer (ostream, mu_buffer_line, 0); + + /* Combine the two streams into an I/O one. */ + if (mu_iostream_create (&str, istream, ostream)) + imap4d_bye (ERR_STREAM_CREATE); + mu_stream_unref (istream); + mu_stream_unref (ostream); + } + /* Convert all writes to CRLF form. There is no need to convert reads, as the code ignores extra \r anyway. */ @@ -141,23 +126,44 @@ io_setio (int ifd, int ofd, struct mu_tls_config *tls_conf) int imap4d_init_tls_server (struct mu_tls_config *tls_conf) { - mu_stream_t tlsstream, stream[2]; + mu_stream_t tlsstream, stream[2], tstr, istr; + mu_transport_t t[2]; + int ifd, ofd; int rc; rc = mu_stream_ioctl (iostream, MU_IOCTL_SUBSTREAM, MU_IOCTL_OP_GET, stream); if (rc) { - mu_error (_("%s failed: %s"), "MU_IOCTL_GET_STREAM", + mu_error (_("%s failed: %s"), "MU_IOCTL_SUBSTREAM", mu_stream_strerror (iostream, rc)); return 1; } - rc = mu_tls_stream_create (&tlsstream, stream[0], stream[1], - tls_conf, - MU_TLS_SERVER, - 0); - mu_stream_unref (stream[0]); - mu_stream_unref (stream[1]); + rc = mu_stream_ioctl (stream[MU_TRANSPORT_INPUT], MU_IOCTL_TRANSPORT, + MU_IOCTL_OP_GET, t); + if (rc) + { + mu_error (_("%s failed: %s"), "MU_IOCTL_TRANSPORT", + mu_stream_strerror (iostream, rc)); + return 1; + } + ifd = (int) (intptr_t) t[0]; + + rc = mu_stream_ioctl (stream[MU_TRANSPORT_OUTPUT], MU_IOCTL_TRANSPORT, + MU_IOCTL_OP_GET, t); + if (rc) + { + mu_error (_("%s failed: %s"), "MU_IOCTL_TRANSPORT", + mu_stream_strerror (iostream, rc)); + return 1; + } + ofd = (int) (intptr_t) t[0]; + + rc = mu_tlsfd_stream_create (&tlsstream, ifd, ofd, + tls_conf, + MU_TLS_SERVER, + 0); + if (rc) { mu_diag_output (MU_DIAG_ERROR, _("cannot open TLS stream: %s"), @@ -167,17 +173,60 @@ imap4d_init_tls_server (struct mu_tls_config *tls_conf) log_cipher (tlsstream); - stream[0] = stream[1] = tlsstream; + t[0] = (mu_transport_t) -1; + mu_stream_ioctl (stream[MU_TRANSPORT_INPUT], MU_IOCTL_TRANSPORT, + MU_IOCTL_OP_SET, t); + t[0] = (mu_transport_t) -1; + mu_stream_ioctl (stream[MU_TRANSPORT_OUTPUT], MU_IOCTL_TRANSPORT, + MU_IOCTL_OP_SET, t); + + mu_stream_unref (stream[0]); + mu_stream_unref (stream[1]); - rc = mu_stream_ioctl (iostream, MU_IOCTL_SUBSTREAM, MU_IOCTL_OP_SET, stream); + /* + * Find the iostream and replace it with the TLS stream. + * Unless transcript is enabled the iostream variable refers to a + * CRLF filter, and its sub-stream is the iostream object. If transcript + * is enabled, the treanscript stream is added on top and iostream refers + * to it. + * + * The loop below uses the fact that iostream is the only stream in + * mailutils that returns *both* transport streams on MU_IOCTL_TOPSTREAM/ + * MU_IOCTL_OP_GET ioctl. Rest of streams that support MU_IOCTL_TOPSTREAM, + * return the transport stream in stream[0] and NULL in stream[1]. + */ + tstr = NULL; + istr = iostream; + while ((rc = mu_stream_ioctl (istr, + MU_IOCTL_TOPSTREAM, MU_IOCTL_OP_GET, + stream)) == 0 + && stream[1] == NULL) + { + tstr = istr; + istr = stream[0]; + mu_stream_unref (istr); + } + if (rc) { - mu_error (_("%s failed: %s"), "MU_IOCTL_SET_STREAM", - mu_stream_strerror (iostream, rc)); - imap4d_bye (ERR_STREAM_CREATE); + mu_error ("%s", _("INTERNAL ERROR: cannot locate iostream")); + return 1; } - mu_stream_unref (tlsstream); + mu_stream_unref (stream[0]); + mu_stream_unref (stream[1]); + + stream[0] = tlsstream; + stream[1] = NULL; + rc = mu_stream_ioctl (tstr, MU_IOCTL_TOPSTREAM, MU_IOCTL_OP_SET, stream); + if (rc) + { + mu_error (_("INTERNAL ERROR: failed to install TLS stream: %s"), + mu_strerror (rc)); + return 1; + } + mu_stream_unref (tlsstream); + return 0; } diff --git a/imap4d/starttls.c b/imap4d/starttls.c index 7a8a15d21..f4329ec60 100644 --- a/imap4d/starttls.c +++ b/imap4d/starttls.c @@ -18,7 +18,7 @@ static int global_conf_status = -1; int global_tls_mode; -struct mu_tls_config global_tls_conf; +struct mu_tls_config global_tls_conf = { .handshake_timeout = 10 }; /* 6.2.1. STARTTLS Command diff --git a/include/mailutils/tls.h b/include/mailutils/tls.h index 4720c2182..752598d67 100644 --- a/include/mailutils/tls.h +++ b/include/mailutils/tls.h @@ -32,6 +32,7 @@ struct mu_tls_config char *key_file; char *ca_file; char *priorities; + unsigned handshake_timeout; }; enum mu_tls_type @@ -60,6 +61,10 @@ extern int mu_tls_ca_file_checks; void mu_tls_cfg_init (void); +int mu_tlsfd_stream_create (mu_stream_t *pstream, int ifd, int ofd, + struct mu_tls_config const *conf, + enum mu_tls_type type, + int flags); int mu_tls_stream_create (mu_stream_t *pstream, mu_stream_t strin, mu_stream_t strout, struct mu_tls_config const *conf, diff --git a/libmu_auth/Makefile.am b/libmu_auth/Makefile.am index 411d77f9e..ed450e908 100644 --- a/libmu_auth/Makefile.am +++ b/libmu_auth/Makefile.am @@ -35,7 +35,8 @@ libmu_auth_la_SOURCES += tlsconf.c tlsvar.c if MU_COND_GNUTLS libmu_auth_la_SOURCES += \ tls.c\ - tlsiostr.c + tlsiostr.c\ + tlsfdstr.c else libmu_auth_la_SOURCES += notls.c endif diff --git a/libmu_auth/tlsconf.c b/libmu_auth/tlsconf.c index d6352f05d..a332c6ba4 100644 --- a/libmu_auth/tlsconf.c +++ b/libmu_auth/tlsconf.c @@ -118,6 +118,10 @@ static struct mu_cfg_param tls_canned_param[] = { N_("Set the priorities to use on the ciphers, key exchange methods, " "macs and compression methods."), NULL }, + { "handshake-timeout", mu_c_uint, + NULL, mu_offsetof(struct mu_tls_config, handshake_timeout), NULL, + N_("Timeout for handshake I/O operations (seconds)"), + "n" }, { NULL } }; diff --git a/libmu_auth/tlsfdstr.c b/libmu_auth/tlsfdstr.c new file mode 100644 index 000000000..7db56a67e --- /dev/null +++ b/libmu_auth/tlsfdstr.c @@ -0,0 +1,683 @@ +#if HAVE_CONFIG_H +# include <config.h> +#endif +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/select.h> +#include <gnutls/gnutls.h> +#include <mailutils/stream.h> +#include <mailutils/errno.h> +#include <mailutils/property.h> +#include <mailutils/sockaddr.h> +#include <mailutils/sys/tls-stream.h> + +struct _mu_tlsfd_stream +{ + struct _mu_stream stream; + enum _mu_tls_stream_state state; + int session_type; /* Either GNUTLS_CLIENT or GNUTLS_SERVER */ + gnutls_session_t session; + int tls_err; + int fd[2]; + int fd_borrowed; + unsigned io_timeout; + struct mu_tls_config conf; + gnutls_certificate_credentials_t cred; +}; + +static char default_priority_string[] = "NORMAL"; + +static int +prep_session (mu_stream_t stream, unsigned handshake_timeout) +{ + struct _mu_tlsfd_stream *sp = (struct _mu_tlsfd_stream *) stream; + gnutls_certificate_credentials_t cred = NULL; + int rc; + const char *errp; + + if (!sp->cred) + { + rc = gnutls_certificate_allocate_credentials (&cred); + if (rc) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + ("gnutls_certificate_allocate_credentials: %s", + gnutls_strerror (rc))); + sp->tls_err = rc; + return MU_ERR_TLS; + } + + if (sp->conf.ca_file) + { + rc = gnutls_certificate_set_x509_trust_file (cred, sp->conf.ca_file, + GNUTLS_X509_FMT_PEM); + if (rc < 0) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + ("can't use X509 CA file %s: %s", + sp->conf.ca_file, + gnutls_strerror (rc))); + goto cred_err; + } + } + + if (sp->conf.cert_file && sp->conf.key_file) + { + rc = gnutls_certificate_set_x509_key_file (cred, + sp->conf.cert_file, + sp->conf.key_file, + GNUTLS_X509_FMT_PEM); + if (rc != GNUTLS_E_SUCCESS) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + ("can't use X509 cert/key pair (%s,%s): %s", + sp->conf.cert_file, + sp->conf.key_file, + gnutls_strerror (rc))); + goto cred_err; + } + } + sp->cred = cred; + } + + rc = gnutls_init (&sp->session, sp->session_type); + if (rc != GNUTLS_E_SUCCESS) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + ("failed to initialize session: %s", gnutls_strerror (rc))); + goto cred_err; + } + + rc = gnutls_priority_set_direct (sp->session, + sp->conf.priorities + ? sp->conf.priorities + : default_priority_string, + &errp); + if (rc != GNUTLS_E_SUCCESS) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + ("error setting priorities near %s: %s", + errp, gnutls_strerror (rc))); + goto cred_err; + } + + rc = gnutls_credentials_set (sp->session, GNUTLS_CRD_CERTIFICATE, sp->cred); + if (rc) + { + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, + ("gnutls_credentials_set: %s", gnutls_strerror (rc))); + goto sess_err; + } + + if (sp->session_type == GNUTLS_SERVER) + gnutls_certificate_server_set_request (sp->session, GNUTLS_CERT_REQUEST); + + gnutls_transport_set_int2 (sp->session, + sp->fd[MU_TRANSPORT_INPUT], + sp->fd[MU_TRANSPORT_OUTPUT]); + if (handshake_timeout) + gnutls_handshake_set_timeout (sp->session, handshake_timeout*1000); + + return 0; + + sess_err: + gnutls_deinit (sp->session); + cred_err: + if (sp->cred) + { + gnutls_certificate_free_credentials (sp->cred); + sp->cred = NULL; + } + sp->tls_err = rc; + return MU_ERR_TLS; +} + +static void +free_conf (struct mu_tls_config *conf) +{ + free (conf->cert_file); + free (conf->key_file); + free (conf->ca_file); + free (conf->priorities); +} + +static int +copy_conf (struct mu_tls_config *dst, struct mu_tls_config const *src) +{ + int rc; + + dst->cert_file = NULL; + dst->key_file = NULL; + dst->ca_file = NULL; + dst->priorities = NULL; + dst->handshake_timeout = 0; + + if (src) + { + if (src->cert_file) + { + dst->cert_file = strdup (src->cert_file); + if (!dst->cert_file) + goto err; + } + else + dst->cert_file = NULL; + + if (src->key_file) + { + dst->key_file = strdup (src->key_file); + if (!dst->cert_file) + goto err; + } + else + dst->key_file = NULL; + + if (src->ca_file) + { + dst->ca_file = strdup (src->ca_file); + if (!dst->ca_file) + goto err; + } + else + dst->ca_file = NULL; + + if (src->priorities) + { + dst->priorities = strdup (src->priorities); + if (!dst->priorities) + goto err; + } + else + dst->priorities = NULL; + + dst->handshake_timeout = src->handshake_timeout; + } + + return 0; + + err: + rc = errno; + free_conf (dst); + return rc; +} + +static int +_tlsfd_open (mu_stream_t stream) +{ + struct _mu_tlsfd_stream *sp = (struct _mu_tlsfd_stream *) stream; + int rc = 0; + + switch (sp->state) + { + case state_closed: + if (sp->session) + { + gnutls_deinit (sp->session); + sp->session = NULL; + } + /* FALLTHROUGH */ + + case state_init: + rc = prep_session (stream, sp->conf.handshake_timeout); + if (rc) + break; + if ((sp->tls_err = gnutls_handshake (sp->session)) != GNUTLS_E_SUCCESS) + { + if (sp->tls_err == GNUTLS_E_TIMEDOUT) + rc = MU_ERR_TIMEOUT; + else + rc = MU_ERR_TLS; + gnutls_deinit (sp->session); + sp->session = NULL; + sp->state = state_init; + } + else + { + /* FIXME: if (ssl_cafile) verify_certificate (s->session); */ + sp->state = state_open; + rc = 0; + } + break; + + default: + rc = MU_ERR_BADOP; + } + return rc; +} + +static int +_tlsfd_close (mu_stream_t stream) +{ + struct _mu_tlsfd_stream *sp = (struct _mu_tlsfd_stream *) stream; + + if (sp->session && sp->state == state_open) + { + gnutls_bye (sp->session, GNUTLS_SHUT_RDWR); + sp->state = state_closed; + } + + if (!sp->fd_borrowed) + { + close (sp->fd[MU_TRANSPORT_INPUT]); + close (sp->fd[MU_TRANSPORT_OUTPUT]); + } + sp->fd[MU_TRANSPORT_INPUT] = -1; + sp->fd[MU_TRANSPORT_OUTPUT] = -1; + + return 0; +} + +static void +_tlsfd_done (struct _mu_stream *stream) +{ + struct _mu_tlsfd_stream *sp = (struct _mu_tlsfd_stream *) stream; + + if (sp->session) + gnutls_deinit (sp->session); + if (sp->cred) + gnutls_certificate_free_credentials (sp->cred); + + free_conf (&sp->conf); + + if (sp->fd[MU_TRANSPORT_INPUT] >= 0) + close (sp->fd[MU_TRANSPORT_INPUT]); + if (sp->fd[MU_TRANSPORT_OUTPUT] >= 0) + close (sp->fd[MU_TRANSPORT_OUTPUT]); +} + + +static int +_tlsfd_read (struct _mu_stream *stream, char *buf, size_t bufsize, + size_t *pnread) +{ + struct _mu_tlsfd_stream *sp = (struct _mu_tlsfd_stream *) stream; + ssize_t rc; + + if (sp->state != state_open) + return EINVAL; + + do + rc = gnutls_record_recv (sp->session, buf, bufsize); + while (rc == GNUTLS_E_AGAIN || rc == GNUTLS_E_INTERRUPTED); + if (rc >= 0) + { + *pnread = rc; + return 0; + } + sp->tls_err = rc; + return MU_ERR_TLS; +} + +static int +_tlsfd_write (struct _mu_stream *stream, const char *buf, size_t bufsize, + size_t *pnwrite) +{ + struct _mu_tlsfd_stream *sp = (struct _mu_tlsfd_stream *) stream; + ssize_t rc; + + if (sp->state != state_open) + return EINVAL; + + do + rc = gnutls_record_send (sp->session, buf, bufsize); + while (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN); + + if (rc >= 0) + { + *pnwrite = rc; + return 0; + } + sp->tls_err = rc; + return MU_ERR_TLS; +} + +int +_tlsfd_wait (mu_stream_t stream, int *pflags, struct timeval *tvp) +{ + struct _mu_tlsfd_stream *sp = (struct _mu_tlsfd_stream *) stream; + int n = 0; + fd_set rdset, wrset, exset; + int rc; + + if (sp->fd[MU_TRANSPORT_INPUT] == sp->fd[MU_TRANSPORT_OUTPUT]) + return mu_fd_wait (sp->fd[MU_TRANSPORT_INPUT], pflags, tvp); + + FD_ZERO (&rdset); + FD_ZERO (&wrset); + FD_ZERO (&exset); + if (*pflags & MU_STREAM_READY_RD) + { + FD_SET (sp->fd[MU_TRANSPORT_INPUT], &rdset); + n = sp->fd[MU_TRANSPORT_INPUT]; + } + if (*pflags & MU_STREAM_READY_WR) + { + FD_SET (sp->fd[MU_TRANSPORT_OUTPUT], &wrset); + if (sp->fd[MU_TRANSPORT_OUTPUT] > n) + n = sp->fd[MU_TRANSPORT_OUTPUT]; + } + if (*pflags & MU_STREAM_READY_EX) + { + FD_SET (sp->fd[MU_TRANSPORT_INPUT], &exset); + FD_SET (sp->fd[MU_TRANSPORT_OUTPUT], &exset); + n = sp->fd[MU_TRANSPORT_INPUT] > sp->fd[MU_TRANSPORT_OUTPUT] + ? sp->fd[MU_TRANSPORT_INPUT] + : sp->fd[MU_TRANSPORT_OUTPUT]; + } + + do + { + if (tvp) + { + struct timeval tv = *tvp; + rc = select (n + 1, &rdset, &wrset, &exset, &tv); + } + else + rc = select (n+ 1, &rdset, &wrset, &exset, NULL); + } + while (rc == -1 && errno == EINTR); + + if (rc < 0) + return errno; + + *pflags = 0; + if (rc > 0) + { + if (FD_ISSET (sp->fd[MU_TRANSPORT_INPUT], &rdset)) + *pflags |= MU_STREAM_READY_RD; + if (FD_ISSET (sp->fd[MU_TRANSPORT_OUTPUT], &wrset)) + *pflags |= MU_STREAM_READY_WR; + if (FD_ISSET (sp->fd[MU_TRANSPORT_INPUT], &exset) || + FD_ISSET (sp->fd[MU_TRANSPORT_OUTPUT], &exset)) + *pflags |= MU_STREAM_READY_EX; + } + return 0; +} + +static int +get_cipher_info (gnutls_session_t session, mu_property_t *pprop) +{ + mu_property_t prop; + const char *s; + int rc; + + if (!pprop) + return EINVAL; + + rc = mu_property_create_init (&prop, mu_assoc_property_init, NULL); + if (rc) + return rc; + + s = gnutls_protocol_get_name (gnutls_protocol_get_version (session)); + mu_property_set_value (prop, "protocol", s, 1); + + s = gnutls_cipher_get_name (gnutls_cipher_get (session)); + mu_property_set_value (prop, "cipher", s, 1); + + s = gnutls_mac_get_name (gnutls_mac_get (session)); + mu_property_set_value (prop, "mac", s, 1); + + *pprop = prop; + + return 0; +} + +static int +_tlsfd_ioctl (struct _mu_stream *stream, int code, int opcode, void *ptr) +{ + struct _mu_tlsfd_stream *sp = (struct _mu_tlsfd_stream *) stream; + + switch (code) + { + case MU_IOCTL_TRANSPORT: + if (!ptr) + return EINVAL; + else + { + mu_transport_t *ptrans = ptr; + + switch (opcode) + { + case MU_IOCTL_OP_GET: + ptrans[MU_TRANSPORT_INPUT] = + (mu_transport_t) (intptr_t) sp->fd[MU_TRANSPORT_INPUT]; + ptrans[MU_TRANSPORT_OUTPUT] = + (mu_transport_t) (intptr_t) sp->fd[MU_TRANSPORT_OUTPUT]; + break; + + case MU_IOCTL_OP_SET: + sp->fd[MU_TRANSPORT_INPUT] = + (int) (intptr_t) ptrans[MU_TRANSPORT_INPUT]; + sp->fd[MU_TRANSPORT_OUTPUT] = + (int) (intptr_t) ptrans[MU_TRANSPORT_OUTPUT]; + break; + + default: + return EINVAL; + } + } + break; + + case MU_IOCTL_TRANSPORT_BUFFER: + if (!ptr) + return EINVAL; + else + { + struct mu_buffer_query *qp = ptr; + switch (opcode) + { + case MU_IOCTL_OP_GET: + return mu_stream_get_buffer (stream, qp); + + case MU_IOCTL_OP_SET: + return mu_stream_set_buffer (stream, qp->buftype, qp->bufsize); + + default: + return EINVAL; + } + } + break; + + case MU_IOCTL_FD: + if (!ptr) + return EINVAL; + switch (opcode) + { + case MU_IOCTL_FD_GET_BORROW: + *(int*) ptr = sp->fd_borrowed; + break; + + case MU_IOCTL_FD_SET_BORROW: + sp->fd_borrowed = *(int*) ptr; + break; + + default: + return EINVAL; + } + break; + + case MU_IOCTL_TIMEOUT: + if (!ptr) + return EINVAL; + else + { + unsigned n; + struct timeval *tv = ptr; +# define MSEC_PER_SECOND 1000 + + switch (opcode) + { + case MU_IOCTL_OP_GET: + tv->tv_sec = sp->io_timeout / MSEC_PER_SECOND; + tv->tv_usec = (sp->io_timeout % MSEC_PER_SECOND) * 1000; + break; + + case MU_IOCTL_OP_SET: + if (tv->tv_sec > UINT_MAX / MSEC_PER_SECOND) + return ERANGE; + n = tv->tv_sec * MSEC_PER_SECOND; + if (UINT_MAX - n < (unsigned long) tv->tv_usec / 1000) + return ERANGE; + n += tv->tv_usec / 1000; + sp->io_timeout = n; + gnutls_record_set_timeout (sp->session, n); + break; + + default: + return EINVAL; + } + +/* + Timeouts: + + gnutls_transport_set_pull_timeout_function (sp->session, + gnutls_system_recv_timeout); + + gnutls_record_set_timeout (sp->session, MS); + gnutls_handshake_set_timeout (sp->session, MS); + + */ + + } + break; + + case MU_IOCTL_TLSSTREAM: + switch (opcode) + { + case MU_IOCTL_TLS_GET_CIPHER_INFO: + return get_cipher_info (sp->session, ptr); + + default: + return EINVAL; + } + break; + + case MU_IOCTL_TCPSTREAM: + switch (opcode) + { + case MU_IOCTL_TCP_GETSOCKNAME: + if (!ptr) + return EINVAL; + else + { + /* FIXME: Check this */ + struct mu_sockaddr *addr; + int rc = mu_sockaddr_from_socket (&addr, sp->fd[MU_TRANSPORT_INPUT]); + if (rc) + return rc; + *(struct mu_sockaddr **)ptr = addr; + } + break; + + default: + return EINVAL; + } + break; + + default: + return ENOSYS; + } + return 0; +} + +static const char * +_tlsfd_error_string (struct _mu_stream *stream, int rc) +{ + if (rc == MU_ERR_TLS) + { + struct _mu_tlsfd_stream *sp = (struct _mu_tlsfd_stream *) stream; + return gnutls_strerror (sp->tls_err); + } + return mu_strerror (rc); +} + + +int +mu_tlsfd_stream_create (mu_stream_t *pstream, int ifd, int ofd, + struct mu_tls_config const *conf, + enum mu_tls_type type, + int flags) +{ + struct _mu_tlsfd_stream *sp; + int rc; + mu_stream_t stream; + int session_type; + + if (!pstream) + return MU_ERR_OUT_PTR_NULL; + if (!conf) + return EINVAL; + + if (!mu_init_tls_libs ()) + return ENOSYS; + + if (conf) + { + switch (mu_tls_config_check (conf, 1)) + { + case MU_TLS_CONFIG_OK: + case MU_TLS_CONFIG_NULL: + break; + case MU_TLS_CONFIG_UNSAFE: + return EACCES; + case MU_TLS_CONFIG_FAIL: + return ENOENT; + } + } + + switch (type) + { + case MU_TLS_CLIENT: + session_type = GNUTLS_CLIENT; + break; + + case MU_TLS_SERVER: + session_type = GNUTLS_SERVER; + break; + + default: + return EINVAL; + } + + sp = (struct _mu_tlsfd_stream *) + _mu_stream_create (sizeof (*sp), MU_STREAM_RDWR); + if (!sp) + return ENOMEM; + + sp->session_type = session_type; + sp->state = state_init; + sp->session = NULL; + sp->cred = NULL; + + rc = copy_conf (&sp->conf, conf); + if (rc) + { + free (sp); + return rc; + } + + sp->fd[MU_TRANSPORT_INPUT] = ifd; + sp->fd[MU_TRANSPORT_OUTPUT] = ofd; + + sp->stream.read = _tlsfd_read; + sp->stream.write = _tlsfd_write; + // sp->stream.flush = _tlsfd_flush; + sp->stream.open = _tlsfd_open; + sp->stream.close = _tlsfd_close; + sp->stream.done = _tlsfd_done; + sp->stream.ctl = _tlsfd_ioctl; + sp->stream.wait = _tlsfd_wait; + sp->stream.error_string = _tlsfd_error_string; + + 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; +} |