/**
 *	locking_commit - commit a reserved slot in the buffer
 *	@rchan: the channel
 *	@from: commit the length starting here
 *	@len: length committed
 *	@deliver: length committed
 *	@interrupting: not used
 *
 *      Commits len bytes and calls deliver callback if applicable.
 */
inline void
locking_commit(struct rchan *rchan,
               char *from,
               u32 len,
               int deliver,
               int interrupting)
{
    cur_write_pos(rchan) += len;

    if (interrupting) {
        cur_write_pos(rchan) = interrupted_pos(rchan);
        interrupting_size(rchan) += len;
    } else {
        in_progress_event_size(rchan) = 0;
        if (interrupting_size(rchan)) {
            cur_write_pos(rchan) += interrupting_size(rchan);
            interrupting_size(rchan) = 0;
        }
    }

    if (deliver) {
        if (bulk_delivery(rchan)) {
            u32 cur_idx = cur_write_pos(rchan) - rchan->buf;
            u32 cur_bufno = cur_idx / rchan->buf_size;
            from = rchan->buf + cur_bufno * rchan->buf_size;
            len = cur_idx - cur_bufno * rchan->buf_size;
        }
        rchan->callbacks->deliver(rchan->id, from, len);
        expand_check(rchan);
    }
}
Exemple #2
0
PRIVATE struct exp_list *expand_explist()
{
	struct exp_list *root = NULL;
	struct exp_list *work;

	expand_check(SQ_EXPLIST);

	while (expand_peekc() != SQ_ENDEXPLIST) {
		work =
		    mem_alloc_private(curenv->program_pool,
				      sizeof(struct exp_list));
		work->exp = expand_exp();
		work->next = root;
		root = work;
	}

	expand_getc();

	return my_reverse(root);
}
Exemple #3
0
int
tls_server_start(uschar *require_ciphers, uschar *require_mac,
  uschar *require_kx, uschar *require_proto)
{
int rc;
const char *error;
uschar *expciphers = NULL;
uschar *expmac = NULL;
uschar *expkx = NULL;
uschar *expproto = NULL;

/* Check for previous activation */

if (tls_active >= 0)
  {
  tls_error("STARTTLS received after TLS started", NULL, "");
  smtp_printf("554 Already in TLS\r\n");
  return FAIL;
  }

/* Initialize the library. If it fails, it will already have logged the error
and sent an SMTP response. */

DEBUG(D_tls) debug_printf("initializing GnuTLS as a server\n");

rc = tls_init(NULL, tls_certificate, tls_privatekey, tls_verify_certificates,
  tls_crl);
if (rc != OK) return rc;

if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers) ||
    !expand_check(require_mac, US"gnutls_require_mac", &expmac) ||
    !expand_check(require_kx, US"gnutls_require_kx", &expkx) ||
    !expand_check(require_proto, US"gnutls_require_proto", &expproto))
  return FAIL;

/* If this is a host for which certificate verification is mandatory or
optional, set up appropriately. */

tls_certificate_verified = FALSE;
verify_requirement = VERIFY_NONE;

if (verify_check_host(&tls_verify_hosts) == OK)
  verify_requirement = VERIFY_REQUIRED;
else if (verify_check_host(&tls_try_verify_hosts) == OK)
  verify_requirement = VERIFY_OPTIONAL;

/* Prepare for new connection */

tls_session = tls_session_init(GNUTLS_SERVER, expciphers, expmac, expkx,
  expproto);
if (tls_session == NULL)
  return tls_error(US"tls_session_init", NULL,
    gnutls_strerror(GNUTLS_E_MEMORY_ERROR));

/* Set context and tell client to go ahead, except in the case of TLS startup
on connection, where outputting anything now upsets the clients and tends to
make them disconnect. We need to have an explicit fflush() here, to force out
the response. Other smtp_printf() calls do not need it, because in non-TLS
mode, the fflush() happens when smtp_getc() is called. */

if (!tls_on_connect)
  {
  smtp_printf("220 TLS go ahead\r\n");
  fflush(smtp_out);
  }

/* Now negotiate the TLS session. We put our own timer on it, since it seems
that the GnuTLS library doesn't. */

gnutls_transport_set_ptr2(tls_session, (gnutls_transport_ptr)fileno(smtp_in),
                                       (gnutls_transport_ptr)fileno(smtp_out));

sigalrm_seen = FALSE;
if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
rc = gnutls_handshake(tls_session);
alarm(0);

if (rc < 0)
  {
  tls_error(US"gnutls_handshake", NULL,
    sigalrm_seen ? "timed out" : gnutls_strerror(rc));

  /* It seems that, except in the case of a timeout, we have to close the
  connection right here; otherwise if the other end is running OpenSSL it hangs
  until the server times out. */

  if (!sigalrm_seen)
    {
    (void)fclose(smtp_out);
    (void)fclose(smtp_in);
    }

  return FAIL;
  }

DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n");

if (verify_requirement != VERIFY_NONE &&
     !verify_certificate(tls_session, &error))
  {
  tls_error(US"certificate verification failed", NULL, error);
  return FAIL;
  }

construct_cipher_name(tls_session);

/* TLS has been set up. Adjust the input functions to read via TLS,
and initialize appropriately. */

ssl_xfer_buffer = store_malloc(ssl_xfer_buffer_size);
ssl_xfer_buffer_lwm = ssl_xfer_buffer_hwm = 0;
ssl_xfer_eof = ssl_xfer_error = 0;

receive_getc = tls_getc;
receive_ungetc = tls_ungetc;
receive_feof = tls_feof;
receive_ferror = tls_ferror;
receive_smtp_buffered = tls_smtp_buffered;

tls_active = fileno(smtp_out);

return OK;
}
Exemple #4
0
static int
tls_init(host_item *host, uschar *certificate, uschar *privatekey, uschar *cas,
  uschar *crl)
{
int rc;
uschar *cert_expanded, *key_expanded, *cas_expanded, *crl_expanded;

client_host = host;

rc = gnutls_global_init();
if (rc < 0) return tls_error(US"tls-init", host, gnutls_strerror(rc));

/* Create D-H parameters, or read them from the cache file. This function does
its own SMTP error messaging. */

rc = init_dh(host);
if (rc != OK) return rc;

/* Create the credentials structure */

rc = gnutls_certificate_allocate_credentials(&x509_cred);
if (rc < 0)
  return tls_error(US"certificate_allocate_credentials",
    host, gnutls_strerror(rc));

/* This stuff must be done for each session, because different certificates
may be required for different sessions. */

if (!expand_check(certificate, US"tls_certificate", &cert_expanded))
  return DEFER;

key_expanded = NULL;
if (privatekey != NULL)
  {
  if (!expand_check(privatekey, US"tls_privatekey", &key_expanded))
    return DEFER;
  }

/* If expansion was forced to fail, key_expanded will be NULL. If the result of
the expansion is an empty string, ignore it also, and assume that the private
key is in the same file as the certificate. */

if (key_expanded == NULL || *key_expanded == 0)
  key_expanded = cert_expanded;

/* Set the certificate and private keys */

if (cert_expanded != NULL)
  {
  DEBUG(D_tls) debug_printf("certificate file = %s\nkey file = %s\n",
    cert_expanded, key_expanded);
  rc = gnutls_certificate_set_x509_key_file(x509_cred, CS cert_expanded,
    CS key_expanded, GNUTLS_X509_FMT_PEM);
  if (rc < 0)
    {
    uschar *msg = string_sprintf("cert/key setup: cert=%s key=%s",
      cert_expanded, key_expanded);
    return tls_error(msg, host, gnutls_strerror(rc));
    }
  }

/* A certificate is mandatory in a server, but not in a client */

else
  {
  if (host == NULL)
    return tls_error(US"no TLS server certificate is specified", NULL, NULL);
  DEBUG(D_tls) debug_printf("no TLS client certificate is specified\n");
  }

/* Set the trusted CAs file if one is provided, and then add the CRL if one is
provided. Experiment shows that, if the certificate file is empty, an unhelpful
error message is provided. However, if we just refrain from setting anything up
in that case, certificate verification fails, which seems to be the correct
behaviour. */

if (cas != NULL)
  {
  struct stat statbuf;

  if (!expand_check(cas, US"tls_verify_certificates", &cas_expanded))
    return DEFER;

  if (stat(CS cas_expanded, &statbuf) < 0)
    {
    log_write(0, LOG_MAIN|LOG_PANIC, "could not stat %s "
      "(tls_verify_certificates): %s", cas_expanded, strerror(errno));
    return DEFER;
    }

  DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n",
    cas_expanded, statbuf.st_size);

  /* If the cert file is empty, there's no point in loading the CRL file. */

  if (statbuf.st_size > 0)
    {
    rc = gnutls_certificate_set_x509_trust_file(x509_cred, CS cas_expanded,
      GNUTLS_X509_FMT_PEM);
    if (rc < 0) return tls_error(US"setup_certs", host, gnutls_strerror(rc));

    if (crl != NULL && *crl != 0)
      {
      if (!expand_check(crl, US"tls_crl", &crl_expanded))
        return DEFER;
      DEBUG(D_tls) debug_printf("loading CRL file = %s\n", crl_expanded);
      rc = gnutls_certificate_set_x509_crl_file(x509_cred, CS crl_expanded,
        GNUTLS_X509_FMT_PEM);
      if (rc < 0) return tls_error(US"CRL setup", host, gnutls_strerror(rc));
      }
    }
  }

/* Associate the parameters with the x509 credentials structure. */

gnutls_certificate_set_dh_params(x509_cred, dh_params);

DEBUG(D_tls) debug_printf("initialized certificate stuff\n");
return OK;
}
Exemple #5
0
int
tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam,
  uschar *certificate, uschar *privatekey, uschar *verify_certs,
  uschar *verify_crl, uschar *require_ciphers, uschar *require_mac,
  uschar *require_kx, uschar *require_proto, int timeout)
{
const gnutls_datum *server_certs;
uschar *expciphers = NULL;
uschar *expmac = NULL;
uschar *expkx = NULL;
uschar *expproto = NULL;
const char *error;
unsigned int server_certs_size;
int rc;

DEBUG(D_tls) debug_printf("initializing GnuTLS as a client\n");

verify_requirement = (verify_certs == NULL)? VERIFY_NONE : VERIFY_REQUIRED;
rc = tls_init(host, certificate, privatekey, verify_certs, verify_crl);
if (rc != OK) return rc;

if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers) ||
    !expand_check(require_mac, US"gnutls_require_mac", &expmac) ||
    !expand_check(require_kx, US"gnutls_require_kx", &expkx) ||
    !expand_check(require_proto, US"gnutls_require_proto", &expproto))
  return FAIL;

tls_session = tls_session_init(GNUTLS_CLIENT, expciphers, expmac, expkx,
  expproto);

if (tls_session == NULL)
  return tls_error(US "tls_session_init", host,
    gnutls_strerror(GNUTLS_E_MEMORY_ERROR));

gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)fd);

/* There doesn't seem to be a built-in timeout on connection. */

sigalrm_seen = FALSE;
alarm(timeout);
rc = gnutls_handshake(tls_session);
alarm(0);

if (rc < 0)
  return tls_error(US "gnutls_handshake", host,
    sigalrm_seen ? "timed out" : gnutls_strerror(rc));

server_certs = gnutls_certificate_get_peers(tls_session, &server_certs_size);

if (server_certs != NULL)
  {
  uschar buff[1024];
  gnutls_x509_crt gcert;

  gnutls_x509_crt_init(&gcert);
  tls_peerdn = US"unknown";

  if (gnutls_x509_crt_import(gcert, server_certs, GNUTLS_X509_FMT_DER) == 0)
    {
    size_t bufsize = sizeof(buff);
    if (gnutls_x509_crt_get_dn(gcert, CS buff, &bufsize) >= 0)
      tls_peerdn = string_copy_malloc(buff);
    }
  }

/* Should we also verify the hostname here? */

if (verify_requirement != VERIFY_NONE &&
      !verify_certificate(tls_session, &error))
  return tls_error(US"certificate verification failed", host, error);

construct_cipher_name(tls_session);    /* Sets tls_cipher */
tls_active = fd;
return OK;
}