summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2021-08-24 21:04:06 +0300
committerSergey Poznyakoff <gray@gnu.org>2021-08-24 21:24:15 +0300
commit9571268d27b3310f7257b1683f82d5809c855eb4 (patch)
treedfd48975a4b69ca00dbe850cdab8346e7680870a
parent33344f24b34711da73ece6939e79bc6037a8e23c (diff)
downloadmailutils-9571268d27b3310f7257b1683f82d5809c855eb4.tar.gz
mailutils-9571268d27b3310f7257b1683f82d5809c855eb4.tar.bz2
Attempt to simplify TLS streams.
* libmu_auth/tlsfdstr.c: New TLS stream implementation. * include/mailutils/tls.h (mu_tls_config): New field: handshake_timeout. (mu_tlsfd_stream_create): New proto. * libmu_auth/Makefile.am: Compile tlsfdstr.c * libmu_auth/tlsconf.c (tls_canned_param): New configuration statement: tls.handshake_timeout * doc/texinfo/programs.texi: Document the tls.handshake-timeout statement. * imap4d/io.c (io_setio, imap4d_init_tls_server): Use mu_tlsfd_stream_create. * imap4d/starttls.c (global_tls_conf): Initialize handshake_timeout.
-rw-r--r--doc/texinfo/programs.texi9
-rw-r--r--imap4d/io.c143
-rw-r--r--imap4d/starttls.c2
-rw-r--r--include/mailutils/tls.h5
-rw-r--r--libmu_auth/Makefile.am3
-rw-r--r--libmu_auth/tlsconf.c4
-rw-r--r--libmu_auth/tlsfdstr.c683
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;
+}

Return to:

Send suggestions and report system problems to the System administrator.