|
Packit Service |
4684c1 |
/*
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
The MIT License (MIT)
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
Copyright (c) 2016 Wrymouth Innovation Ltd
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
Permission is hereby granted, free of charge, to any person obtaining a
|
|
Packit Service |
4684c1 |
copy of this software and associated documentation files (the "Software"),
|
|
Packit Service |
4684c1 |
to deal in the Software without restriction, including without limitation
|
|
Packit Service |
4684c1 |
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
Packit Service |
4684c1 |
and/or sell copies of the Software, and to permit persons to whom the
|
|
Packit Service |
4684c1 |
Software is furnished to do so, subject to the following conditions:
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
The above copyright notice and this permission notice shall be included
|
|
Packit Service |
4684c1 |
in all copies or substantial portions of the Software.
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
Packit Service |
4684c1 |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
Packit Service |
4684c1 |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
Packit Service |
4684c1 |
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
Packit Service |
4684c1 |
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
Packit Service |
4684c1 |
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
Packit Service |
4684c1 |
OTHER DEALINGS IN THE SOFTWARE.
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
#include "config.h"
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
#include <errno.h>
|
|
Packit Service |
4684c1 |
#include <fcntl.h>
|
|
Packit Service |
4684c1 |
#include <string.h>
|
|
Packit Service |
4684c1 |
#include <stdint.h>
|
|
Packit Service |
4684c1 |
#include <stdio.h>
|
|
Packit Service |
4684c1 |
#include <stdlib.h>
|
|
Packit Service |
4684c1 |
#include <sys/select.h>
|
|
Packit Service |
4684c1 |
#include <sys/socket.h>
|
|
Packit Service |
4684c1 |
#include <sys/time.h>
|
|
Packit Service |
4684c1 |
#include <sys/types.h>
|
|
Packit Service |
4684c1 |
#include <unistd.h>
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
#include <gnutls/gnutls.h>
|
|
Packit Service |
4684c1 |
#include <gnutls/crypto.h>
|
|
Packit Service |
4684c1 |
#include <gnutls/x509.h>
|
|
Packit Service |
4684c1 |
#include <gnutls/abstract.h>
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
#include "crypto-gnutls.h"
|
|
Packit Service |
4684c1 |
#include "buffer.h"
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
#define FALSE 0
|
|
Packit Service |
4684c1 |
#define TRUE 1
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
struct tlssession
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
gnutls_certificate_credentials_t creds;
|
|
Packit Service |
4684c1 |
gnutls_session_t session;
|
|
Packit Service |
4684c1 |
char *hostname;
|
|
Packit Service |
4684c1 |
int (*quitfn) (void *opaque);
|
|
Packit Service |
4684c1 |
int (*erroutfn) (void *opaque, const char *format, va_list ap);
|
|
Packit Service |
4684c1 |
int debug;
|
|
Packit Service |
4684c1 |
void *opaque;
|
|
Packit Service |
4684c1 |
};
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
#define BUF_SIZE 65536
|
|
Packit Service |
4684c1 |
#define BUF_HWM ((BUF_SIZE*3)/4)
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
static int
|
|
Packit Service |
4684c1 |
falsequit (void *opaque)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
return FALSE;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
static int
|
|
Packit Service |
4684c1 |
quit (tlssession_t * s)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
return s->quitfn (s->opaque);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
#if defined __clang__ || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
|
|
Packit Service |
4684c1 |
# pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
|
|
Packit Service |
4684c1 |
#endif
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
static int stderrout (void *opaque, const char *format, va_list ap)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
return vfprintf (stderr, format, ap);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
static int
|
|
Packit Service |
4684c1 |
errout (tlssession_t * s, const char *format, ...)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
va_list ap;
|
|
Packit Service |
4684c1 |
int ret;
|
|
Packit Service |
4684c1 |
va_start (ap, format);
|
|
Packit Service |
4684c1 |
ret = s->erroutfn (s->opaque, format, ap);
|
|
Packit Service |
4684c1 |
va_end (ap);
|
|
Packit Service |
4684c1 |
return ret;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
static int
|
|
Packit Service |
4684c1 |
debugout (tlssession_t * s, const char *format, ...)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
va_list ap;
|
|
Packit Service |
4684c1 |
int ret = 0;
|
|
Packit Service |
4684c1 |
va_start (ap, format);
|
|
Packit Service |
4684c1 |
if (s->debug)
|
|
Packit Service |
4684c1 |
ret = s->erroutfn (s->opaque, format, ap);
|
|
Packit Service |
4684c1 |
va_end (ap);
|
|
Packit Service |
4684c1 |
return ret;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
static int
|
|
Packit Service |
4684c1 |
socksetnonblock (int fd, int nb)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
int sf = fcntl (fd, F_GETFL, 0);
|
|
Packit Service |
4684c1 |
if (sf == -1)
|
|
Packit Service |
4684c1 |
return -1;
|
|
Packit Service |
4684c1 |
return fcntl (fd, F_SETFL, nb ? (sf | O_NONBLOCK) : (sf & ~O_NONBLOCK));
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* From (public domain) example file in GNUTLS
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* This function will try to verify the peer's certificate, and
|
|
Packit Service |
4684c1 |
* also check if the hostname matches, and the activation, expiration dates.
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
static int
|
|
Packit Service |
4684c1 |
verify_certificate_callback (gnutls_session_t session)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
unsigned int status;
|
|
Packit Service |
4684c1 |
int ret;
|
|
Packit Service |
4684c1 |
tlssession_t *s;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* read session pointer */
|
|
Packit Service |
4684c1 |
s = (tlssession_t *) gnutls_session_get_ptr (session);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
|
|
Packit Service |
4684c1 |
return GNUTLS_E_CERTIFICATE_ERROR;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* This verification function uses the trusted CAs in the credentials
|
|
Packit Service |
4684c1 |
* structure. So you must have installed one or more CA certificates.
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
if (s->hostname && *s->hostname)
|
|
Packit Service |
4684c1 |
ret = gnutls_certificate_verify_peers3 (session, s->hostname, &status);
|
|
Packit Service |
4684c1 |
else
|
|
Packit Service |
4684c1 |
ret = gnutls_certificate_verify_peers2 (session, &status);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (ret < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
debugout (s, "Could not verfify peer certificate due to an error\n");
|
|
Packit Service |
4684c1 |
return GNUTLS_E_CERTIFICATE_ERROR;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (status)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
gnutls_datum_t txt;
|
|
Packit Service |
4684c1 |
ret = gnutls_certificate_verification_status_print(status, GNUTLS_CRT_X509,
|
|
Packit Service |
4684c1 |
&txt, 0);
|
|
Packit Service |
4684c1 |
if (ret >= 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
debugout (s, "verification error: %s\n", txt.data);
|
|
Packit Service |
4684c1 |
gnutls_free(txt.data);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
return GNUTLS_E_CERTIFICATE_ERROR;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
debugout (s, "Peer passed certificate verification\n");
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* notify gnutls to continue handshake normally */
|
|
Packit Service |
4684c1 |
return 0;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
tlssession_t *
|
|
Packit Service |
4684c1 |
tlssession_new (int isserver,
|
|
Packit Service |
4684c1 |
char *keyfile, char *certfile, char *cacertfile,
|
|
Packit Service |
4684c1 |
char *hostname, int insecure, int debug,
|
|
Packit Service |
4684c1 |
int (*quitfn) (void *opaque),
|
|
Packit Service |
4684c1 |
int (*erroutfn) (void *opaque, const char *format,
|
|
Packit Service |
4684c1 |
va_list ap), void *opaque)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
int ret;
|
|
Packit Service |
4684c1 |
tlssession_t *s = calloc (1, sizeof (tlssession_t));
|
|
Packit Service |
4684c1 |
if (!s)
|
|
Packit Service |
4684c1 |
return NULL;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (quitfn)
|
|
Packit Service |
4684c1 |
s->quitfn = quitfn;
|
|
Packit Service |
4684c1 |
else
|
|
Packit Service |
4684c1 |
s->quitfn = falsequit;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (erroutfn)
|
|
Packit Service |
4684c1 |
s->erroutfn = erroutfn;
|
|
Packit Service |
4684c1 |
else
|
|
Packit Service |
4684c1 |
s->erroutfn = stderrout;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (hostname)
|
|
Packit Service |
4684c1 |
s->hostname = strdup (hostname);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
s->debug = debug;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (gnutls_certificate_allocate_credentials (&s->creds) < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "Certificate allocation memory error\n");
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (cacertfile != NULL)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
ret =
|
|
Packit Service |
4684c1 |
gnutls_certificate_set_x509_trust_file (s->creds, cacertfile,
|
|
Packit Service |
4684c1 |
GNUTLS_X509_FMT_PEM);
|
|
Packit Service |
4684c1 |
if (ret < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "Error setting the x509 trust file: %s\n",
|
|
Packit Service |
4684c1 |
gnutls_strerror (ret));
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (!insecure)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
gnutls_certificate_set_verify_function (s->creds,
|
|
Packit Service |
4684c1 |
verify_certificate_callback);
|
|
Packit Service |
4684c1 |
gnutls_certificate_set_verify_flags (s->creds,
|
|
Packit Service |
4684c1 |
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (keyfile && !certfile)
|
|
Packit Service |
4684c1 |
certfile = keyfile;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (certfile != NULL && keyfile != NULL)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
ret =
|
|
Packit Service |
4684c1 |
gnutls_certificate_set_x509_key_file (s->creds, certfile, keyfile,
|
|
Packit Service |
4684c1 |
GNUTLS_X509_FMT_PEM);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (ret < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s,
|
|
Packit Service |
4684c1 |
"Error loading certificate or key file (%s, %s): %s\n",
|
|
Packit Service |
4684c1 |
certfile, keyfile, gnutls_strerror (ret));
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (isserver)
|
|
Packit Service |
4684c1 |
ret = gnutls_init (&s->session, GNUTLS_SERVER);
|
|
Packit Service |
4684c1 |
else
|
|
Packit Service |
4684c1 |
ret = gnutls_init (&s->session, GNUTLS_CLIENT);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (ret < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "Cannot initialize GNUTLS session: %s\n",
|
|
Packit Service |
4684c1 |
gnutls_strerror (ret));
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
gnutls_session_set_ptr (s->session, (void *) s);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (!isserver && s->hostname && *s->hostname)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
ret = gnutls_server_name_set (s->session, GNUTLS_NAME_DNS, s->hostname,
|
|
Packit Service |
4684c1 |
strlen (s->hostname));
|
|
Packit Service |
4684c1 |
if (ret < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "Cannot set server name: %s\n",
|
|
Packit Service |
4684c1 |
gnutls_strerror (ret));
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
ret = gnutls_set_default_priority (s->session);
|
|
Packit Service |
4684c1 |
if (ret < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "Cannot set default GNUTLS session priority: %s\n",
|
|
Packit Service |
4684c1 |
gnutls_strerror (ret));
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
ret = gnutls_credentials_set (s->session, GNUTLS_CRD_CERTIFICATE, s->creds);
|
|
Packit Service |
4684c1 |
if (ret < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "Cannot set session GNUTL credentials: %s\n",
|
|
Packit Service |
4684c1 |
gnutls_strerror (ret));
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (isserver)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* requests but does not check a client certificate */
|
|
Packit Service |
4684c1 |
gnutls_certificate_server_set_request (s->session, GNUTLS_CERT_REQUEST);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
return s;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
error:
|
|
Packit Service |
4684c1 |
if (s->session)
|
|
Packit Service |
4684c1 |
gnutls_deinit (s->session);
|
|
Packit Service |
4684c1 |
free (s);
|
|
Packit Service |
4684c1 |
return NULL;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
void
|
|
Packit Service |
4684c1 |
tlssession_close (tlssession_t * s)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
if (s->session)
|
|
Packit Service |
4684c1 |
gnutls_deinit (s->session);
|
|
Packit Service |
4684c1 |
free (s->hostname);
|
|
Packit Service |
4684c1 |
free (s);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
int
|
|
Packit Service |
4684c1 |
tlssession_init (void)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
return gnutls_global_init ();
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
int
|
|
Packit Service |
4684c1 |
tlssession_mainloop (int cryptfd, int plainfd, tlssession_t * s)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
fd_set readfds;
|
|
Packit Service |
4684c1 |
fd_set writefds;
|
|
Packit Service |
4684c1 |
int maxfd;
|
|
Packit Service |
4684c1 |
int tls_wr_interrupted = 0;
|
|
Packit Service |
4684c1 |
int plainEOF = FALSE;
|
|
Packit Service |
4684c1 |
int cryptEOF = FALSE;
|
|
Packit Service |
4684c1 |
ssize_t ret;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
buffer_t *plainToCrypt = bufNew (BUF_SIZE, BUF_HWM);
|
|
Packit Service |
4684c1 |
buffer_t *cryptToPlain = bufNew (BUF_SIZE, BUF_HWM);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (socksetnonblock (cryptfd, 0) < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "Could not turn on blocking: %m");
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* set it up to work with our FD */
|
|
Packit Service |
4684c1 |
gnutls_transport_set_ptr (s->session,
|
|
Packit Service |
4684c1 |
(gnutls_transport_ptr_t) (intptr_t) cryptfd);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Now do the handshake */
|
|
Packit Service |
4684c1 |
ret = gnutls_handshake (s->session);
|
|
Packit Service |
4684c1 |
if (ret < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "TLS handshake failed: %s\n", gnutls_strerror (ret));
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (socksetnonblock (cryptfd, 1) < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "Could not turn on non-blocking on crypt FD: %m");
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (socksetnonblock (plainfd, 1) < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "Could not turn on non-blocking on plain FD: %m");
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
maxfd = (plainfd > cryptfd) ? plainfd + 1 : cryptfd + 1;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
while ((!plainEOF || !cryptEOF) && !quit (s))
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
struct timeval timeout;
|
|
Packit Service |
4684c1 |
int result;
|
|
Packit Service |
4684c1 |
int selecterrno;
|
|
Packit Service |
4684c1 |
int wait = TRUE;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
FD_ZERO (&readfds);
|
|
Packit Service |
4684c1 |
FD_ZERO (&writefds);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
size_t buffered = gnutls_record_check_pending (s->session);
|
|
Packit Service |
4684c1 |
if (buffered)
|
|
Packit Service |
4684c1 |
wait = FALSE; /* do not wait for select to return if we have buffered data */
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (plainEOF)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* plain text end has closed, but me may still have
|
|
Packit Service |
4684c1 |
* data yet to write to the crypt end */
|
|
Packit Service |
4684c1 |
if (bufIsEmpty (plainToCrypt) && !tls_wr_interrupted)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
cryptEOF = TRUE;
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
else
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
if (!bufIsEmpty (cryptToPlain))
|
|
Packit Service |
4684c1 |
FD_SET (plainfd, &writefds);
|
|
Packit Service |
4684c1 |
if (!bufIsOverHWM (plainToCrypt))
|
|
Packit Service |
4684c1 |
FD_SET (plainfd, &readfds);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (cryptEOF)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* crypt end has closed, but me way still have data to
|
|
Packit Service |
4684c1 |
* write from the crypt buffer */
|
|
Packit Service |
4684c1 |
if (bufIsEmpty (cryptToPlain) && !buffered)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
plainEOF = TRUE;
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
else
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
if (!bufIsEmpty (plainToCrypt) || tls_wr_interrupted)
|
|
Packit Service |
4684c1 |
FD_SET (cryptfd, &writefds);
|
|
Packit Service |
4684c1 |
if (!bufIsOverHWM (cryptToPlain))
|
|
Packit Service |
4684c1 |
FD_SET (cryptfd, &readfds);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Repeat select whilst EINTR happens */
|
|
Packit Service |
4684c1 |
do
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
timeout.tv_sec = wait ? 1 : 0;
|
|
Packit Service |
4684c1 |
timeout.tv_usec = 0;
|
|
Packit Service |
4684c1 |
result = select (maxfd, &readfds, &writefds, NULL, &timeout);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
selecterrno = errno;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
while ((result == -1) && (selecterrno == EINTR) && !quit (s));
|
|
Packit Service |
4684c1 |
if (quit (s))
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (FD_ISSET (plainfd, &readfds))
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* we can read at least one byte */
|
|
Packit Service |
4684c1 |
void *addr = NULL;
|
|
Packit Service |
4684c1 |
/* get a span of characters to write to the
|
|
Packit Service |
4684c1 |
* buffer. As the empty portion may wrap the end of the
|
|
Packit Service |
4684c1 |
* circular buffer this might not be all we could read.
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
ssize_t len = bufGetWriteSpan (plainToCrypt, &addr);
|
|
Packit Service |
4684c1 |
if (len > 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
do
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
ret = read (plainfd, addr, (size_t) len);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
while ((ret < 0) && (errno == EINTR) && !quit (s));
|
|
Packit Service |
4684c1 |
if (quit (s))
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
if (ret < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "Error on read from plain socket: %m\n");
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
if (ret == 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
plainEOF = TRUE;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
else
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
bufDoneWrite (plainToCrypt, ret); /* mark ret bytes as written to the buffer */
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (FD_ISSET (plainfd, &writefds))
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* we can write at least one byte */
|
|
Packit Service |
4684c1 |
void *addr = NULL;
|
|
Packit Service |
4684c1 |
/* get a span of characters to read from the buffer
|
|
Packit Service |
4684c1 |
* as the full portion may wrap the end of the circular buffer
|
|
Packit Service |
4684c1 |
* this might not be all we have to write.
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
ssize_t len = bufGetReadSpan (cryptToPlain, &addr);
|
|
Packit Service |
4684c1 |
if (len > 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
do
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
ret = write (plainfd, addr, (size_t) len);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
while ((ret < 0) && (errno == EINTR) && !quit (s));
|
|
Packit Service |
4684c1 |
if (quit (s))
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
if (ret < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "Error on write to plain socket: %m\n");
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
bufDoneRead (cryptToPlain, ret); /* mark ret bytes as read from the buffer */
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (FD_ISSET (cryptfd, &readfds) || buffered)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* we can read at least one byte */
|
|
Packit Service |
4684c1 |
void *addr = NULL;
|
|
Packit Service |
4684c1 |
/* get a span of characters to write to the
|
|
Packit Service |
4684c1 |
* buffer. As the empty portion may wrap the end of the
|
|
Packit Service |
4684c1 |
* circular buffer this might not be all we could read.
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
ssize_t len = bufGetWriteSpan (cryptToPlain, &addr);
|
|
Packit Service |
4684c1 |
if (len > 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
do
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
ret = gnutls_record_recv (s->session, addr, (size_t) len);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
while (ret == GNUTLS_E_INTERRUPTED && !quit (s));
|
|
Packit Service |
4684c1 |
/* do not loop on GNUTLS_E_AGAIN - this means we'd block so we'd loop for
|
|
Packit Service |
4684c1 |
* ever
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
if (quit (s))
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
if (ret < 0 && ret != GNUTLS_E_AGAIN)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "Error on read from crypt socket: %s\n",
|
|
Packit Service |
4684c1 |
gnutls_strerror (ret));
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
if (ret == 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
cryptEOF = TRUE;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
else
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
bufDoneWrite (cryptToPlain, ret); /* mark ret bytes as written to the buffer */
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (FD_ISSET (cryptfd, &writefds))
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* we can write at least one byte */
|
|
Packit Service |
4684c1 |
void *addr = NULL;
|
|
Packit Service |
4684c1 |
/* get a span of characters to read from the buffer
|
|
Packit Service |
4684c1 |
* as the full portion may wrap the end of the circular buffer
|
|
Packit Service |
4684c1 |
* this might not be all we have to write.
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
ssize_t len = bufGetReadSpan (plainToCrypt, &addr);
|
|
Packit Service |
4684c1 |
if (len > 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
do
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
if (tls_wr_interrupted)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
ret = gnutls_record_send (s->session, NULL, 0);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
else
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
ret = gnutls_record_send (s->session, addr, len);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
while (ret == GNUTLS_E_INTERRUPTED && !quit (s));
|
|
Packit Service |
4684c1 |
if (quit (s))
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
if (ret == GNUTLS_E_AGAIN)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* we need to call this again with NULL parameters
|
|
Packit Service |
4684c1 |
* as it blocked
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
tls_wr_interrupted = TRUE;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
else if (ret < 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
errout (s, "Error on write to crypto socket: %s\n",
|
|
Packit Service |
4684c1 |
gnutls_strerror (ret));
|
|
Packit Service |
4684c1 |
goto error;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
else
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
bufDoneRead (plainToCrypt, ret); /* mark ret bytes as read from the buffer */
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
ret = 0;
|
|
Packit Service |
4684c1 |
goto freereturn;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
error:
|
|
Packit Service |
4684c1 |
ret = -1;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
freereturn:
|
|
Packit Service |
4684c1 |
gnutls_bye (s->session, GNUTLS_SHUT_RDWR);
|
|
Packit Service |
4684c1 |
shutdown (plainfd, SHUT_RDWR);
|
|
Packit Service |
4684c1 |
bufFree (plainToCrypt);
|
|
Packit Service |
4684c1 |
bufFree (cryptToPlain);
|
|
Packit Service |
4684c1 |
return ret;
|
|
Packit Service |
4684c1 |
}
|