chiark / gitweb /
run debian/rules patch
[innduct.git] / .pc / u_right_length / nnrpd / tls.c
diff --git a/.pc/u_right_length/nnrpd/tls.c b/.pc/u_right_length/nnrpd/tls.c
new file mode 100644 (file)
index 0000000..f31421a
--- /dev/null
@@ -0,0 +1,713 @@
+/* tls.c --- TLSv1 functions
+   Copyright (C) 2000 Kenichi Okada <okada@opaopa.org>
+
+   Author: Kenichi Okada <okada@opaopa.org>
+   Created: 2000-02-22
+
+   Keywords: TLS, OpenSSL
+
+   Commentary:
+
+   [RFC 2246] "The TLS Protocol Version 1.0"
+        by Christopher Allen <callen@certicom.com> and
+        Tim Dierks <tdierks@certicom.com> (1999/01)
+
+   [RFC 2595] "Using TLS with IMAP, POP3 and ACAP"
+        by Chris Newman <chris.newman@innosoft.com> (1999/06)
+
+*/
+
+#include <sys/types.h>
+#include "config.h"
+#include "nnrpd.h"
+
+#ifdef HAVE_SSL
+
+/* System library. */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#endif
+
+/* outside the ifdef so `make depend` works even ifndef HAVE_SSL */
+#include "tls.h"
+#include "sasl_config.h"
+
+#ifdef HAVE_SSL
+
+/* We must keep some of the info available */
+static const char hexcodes[] = "0123456789ABCDEF";
+
+static bool tls_initialized = false;
+
+static int verify_depth;
+static int verify_error = X509_V_OK;
+static int do_dump = 0;
+static SSL_CTX *CTX = NULL;
+SSL *tls_conn = NULL;
+
+#define CCERT_BUFSIZ 256
+
+int     tls_serverengine = 0;
+int     tls_serveractive = 0;  /* available or not */
+char   *tls_peer_subject = NULL;
+char   *tls_peer_issuer = NULL;
+char   *tls_peer_fingerprint = NULL;
+
+int     tls_clientactive = 0;  /* available or not */
+char   *tls_peer_CN = NULL;
+char   *tls_issuer_CN = NULL;
+
+const char   *tls_protocol = NULL;
+const char   *tls_cipher_name = NULL;
+int    tls_cipher_usebits = 0;
+int    tls_cipher_algbits = 0;
+
+
+int tls_loglevel = 0;
+
+
+/* taken from OpenSSL apps/s_cb.c 
+ * tim - this seems to just be giving logging messages
+ */
+
+static void apps_ssl_info_callback(SSL * s, int where, int ret)
+{
+    const char  *str;
+    int         w;
+
+    if (tls_loglevel==0) return;
+
+    w = where & ~SSL_ST_MASK;
+
+    if (w & SSL_ST_CONNECT)
+       str = "SSL_connect";
+    else if (w & SSL_ST_ACCEPT)
+       str = "SSL_accept";
+    else
+       str = "undefined";
+
+    if (where & SSL_CB_LOOP) {
+       if (tls_serverengine && (tls_loglevel >= 2))
+           Printf("%s:%s", str, SSL_state_string_long(s));
+    } else if (where & SSL_CB_ALERT) {
+       str = (where & SSL_CB_READ) ? "read" : "write";
+       if ((tls_serverengine && (tls_loglevel >= 2)) ||
+           ((ret & 0xff) != SSL3_AD_CLOSE_NOTIFY))
+         Printf("SSL3 alert %s:%s:%s", str,
+                SSL_alert_type_string_long(ret),
+                SSL_alert_desc_string_long(ret));
+    } else if (where & SSL_CB_EXIT) {
+       if (ret == 0)
+           Printf("%s:failed in %s",
+                    str, SSL_state_string_long(s));
+       else if (ret < 0) {
+           Printf("%s:error in %s",
+                    str, SSL_state_string_long(s));
+       }
+    }
+}
+
+
+/*
+ *     Hardcoded DH parameter files, from OpenSSL.
+ *     For information on how these files were generated, see
+ *     "Assigned Number for SKIP Protocols" 
+ *     (http://www.skip-vpn.org/spec/numbers.html.
+ */
+static const char file_dh512[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\
+XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\
+-----END DH PARAMETERS-----\n";
+
+static const char file_dh1024[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\
+jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\
+ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\
+-----END DH PARAMETERS-----\n";
+
+static const char file_dh2048[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\
+89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\
+T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\
+zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\
+Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\
+CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\
+-----END DH PARAMETERS-----\n";
+
+static const char file_dh4096[] =
+"-----BEGIN DH PARAMETERS-----\n\
+MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\
+l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\
+Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\
+Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\
+VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\
+alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\
+sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\
+ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\
+OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\
+AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\
+KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
+-----END DH PARAMETERS-----\n";
+
+/*
+ *     Load hardcoded DH parameters.
+ */
+static DH *
+load_dh_buffer (const char *buffer, size_t len)
+{
+       BIO *bio;
+       DH *dh = NULL;
+
+       bio = BIO_new_mem_buf((char *) buffer, len);
+       if (bio == NULL)
+               return NULL;
+       dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+/*     if (dh == NULL) log error */
+       BIO_free(bio);
+
+       return dh;
+}
+
+/*
+ *     Generate empheral DH key.  Because this can take a long
+ *     time to compute, we use precomputed parameters of the
+ *     common key sizes.
+ *
+ *     These values can be static (once loaded or computed) since
+ *     the OpenSSL library can effectively generate random keys
+ *     from the information provided.
+ *
+ *     EDH keying is slightly less efficient than static RSA keying,
+ *     but it offers Perfect Forward Secrecy (PFS).
+ *
+ *     FIXME: support user-specified files, to eliminate risk of
+ *     "small group" attacks.
+ */
+static DH *tmp_dh_cb(SSL *s UNUSED, int export UNUSED, int keylength)
+{
+       DH *r = NULL;
+       static DH *dh = NULL;
+       static DH *dh512 = NULL;
+       static DH *dh1024 = NULL;
+       static DH *dh2048 = NULL;
+       static DH *dh4096 = NULL;
+
+       switch (keylength)
+       {
+       case 512:
+               if (dh512 == NULL)
+                       dh512 = load_dh_buffer(file_dh512, sizeof file_dh512);
+               r = dh512;
+               break;
+       case 1024:
+               if (dh1024 == NULL)
+                       dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024);
+               r = dh1024;
+               break;
+       case 2048:
+               if (dh2048 == NULL)
+                       dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048);
+               r = dh2048;
+               break;
+       case 4096:
+               if (dh4096 == NULL)
+                       dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096);
+               r = dh4096;
+               break;
+       default:
+               /* we should check current keylength vs. requested keylength */
+               /* also, this is an extremely expensive operation! */
+               dh = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
+               r = dh;
+       }
+
+       return r;
+}
+
+/* taken from OpenSSL apps/s_cb.c */
+
+static int verify_callback(int ok, X509_STORE_CTX * ctx)
+{
+    char    buf[256];
+    X509   *err_cert;
+    int     err;
+    int     depth;
+
+    syslog(L_NOTICE,"Doing a peer verify");
+
+    err_cert = X509_STORE_CTX_get_current_cert(ctx);
+    err = X509_STORE_CTX_get_error(ctx);
+    depth = X509_STORE_CTX_get_error_depth(ctx);
+
+    X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
+    if ((tls_serveractive) && (tls_loglevel >= 1))
+      Printf("Peer cert verify depth=%d %s", depth, buf);
+    if (ok==0)
+    {
+      syslog(L_NOTICE, "verify error:num=%d:%s", err,
+            X509_verify_cert_error_string(err));
+      
+       if (verify_depth >= depth) {
+           ok = 0;
+           verify_error = X509_V_OK;
+       } else {
+           ok = 0;
+           verify_error = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+       }
+    }
+    switch (ctx->error) {
+    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+       X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
+       syslog(L_NOTICE, "issuer= %s", buf);
+       break;
+    case X509_V_ERR_CERT_NOT_YET_VALID:
+    case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+       syslog(L_NOTICE, "cert not yet valid");
+       break;
+    case X509_V_ERR_CERT_HAS_EXPIRED:
+    case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+       syslog(L_NOTICE, "cert has expired");
+       break;
+    }
+    if ((tls_serveractive) && (tls_loglevel >= 1))
+      Printf("verify return:%d", ok);
+
+    return (ok);
+}
+
+
+/*
+ * taken from OpenSSL crypto/bio/b_dump.c, modified to save a lot of strcpy
+ * and strcat by Matti Aarnio.
+ */
+
+#define TRUNCATE
+#define DUMP_WIDTH     16
+
+static int tls_dump(const char *s, int len)
+{
+    int     ret = 0;
+    char    buf[160 + 1];
+    char    *ss;
+    int     i;
+    int     j;
+    int     rows;
+    int     trunc;
+    unsigned char ch;
+
+    trunc = 0;
+
+
+#ifdef TRUNCATE
+    for (; (len > 0) && ((s[len - 1] == ' ') || (s[len - 1] == '\0')); len--)
+       trunc++;
+#endif
+
+    rows = (len / DUMP_WIDTH);
+    if ((rows * DUMP_WIDTH) < len)
+       rows++;
+
+    for (i = 0; i < rows; i++) {
+       buf[0] = '\0';                          /* start with empty string */
+       ss = buf;
+
+       sprintf(ss, "%04x ", i * DUMP_WIDTH);
+       ss += strlen(ss);
+       for (j = 0; j < DUMP_WIDTH; j++) {
+           if (((i * DUMP_WIDTH) + j) >= len) {
+               strcpy(ss, "   ");
+           } else {
+               ch = ((unsigned char) *((const char *)(s) + i * DUMP_WIDTH + j))
+                   & 0xff;
+               sprintf(ss, "%02x%c", ch, j == 7 ? '|' : ' ');
+               ss += 3;
+           }
+       }
+       ss += strlen(ss);
+       *ss+= ' ';
+       for (j = 0; j < DUMP_WIDTH; j++) {
+           if (((i * DUMP_WIDTH) + j) >= len)
+               break;
+           ch = ((unsigned char) *((const char *)(s) + i * DUMP_WIDTH + j))
+               & 0xff;
+           *ss+= (((ch >= ' ') && (ch <= '~')) ? ch : '.');
+           if (j == 7) *ss+= ' ';
+       }
+       *ss = 0;
+       /* 
+        * if this is the last call then update the ddt_dump thing so that
+         * we will move the selection point in the debug window
+         */    
+       if (tls_loglevel>0)
+         Printf("%s", buf);
+       ret += strlen(buf);
+    }
+#ifdef TRUNCATE
+    if (trunc > 0) {
+       snprintf(buf, sizeof(buf), "%04x - <SPACES/NULS>\n", len+ trunc);
+       if (tls_loglevel>0)
+         Printf("%s", buf);
+       ret += strlen(buf);
+    }
+#endif
+    return (ret);
+}
+
+ /*
+  * Set up the cert things on the server side. We do need both the
+  * private key (in key_file) and the cert (in cert_file).
+  * Both files may be identical.
+  *
+  * This function is taken from OpenSSL apps/s_cb.c
+  */
+
+static int set_cert_stuff(SSL_CTX * ctx, char *cert_file, char *key_file)
+{
+    struct stat buf;
+
+    if (cert_file != NULL) {
+       if (SSL_CTX_use_certificate_file(ctx, cert_file,
+                                        SSL_FILETYPE_PEM) <= 0) {
+           syslog(L_ERROR, "unable to get certificate from '%s'", cert_file);
+           return (0);
+       }
+       if (key_file == NULL)
+           key_file = cert_file;
+
+       /* check ownership and permissions of key file */
+       if (lstat(key_file, &buf) == -1) {
+           syslog(L_ERROR, "unable to stat private key '%s'", key_file);
+           return (0);
+       }
+       if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) != 0 ||
+           buf.st_uid != getuid()) {
+           syslog(L_ERROR, "bad ownership or permissions on private key"
+                   " '%s': private key must be mode 600 and owned by "
+                   NEWSUSER, cert_file);
+           return (0);
+       }
+
+       if (SSL_CTX_use_PrivateKey_file(ctx, key_file,
+                                       SSL_FILETYPE_PEM) <= 0) {
+           syslog(L_ERROR, "unable to get private key from '%s'", key_file);
+           return (0);
+       }
+       /* Now we know that a key and cert have been set against
+         * the SSL context */
+       if (!SSL_CTX_check_private_key(ctx)) {
+           syslog(L_ERROR, "Private key does not match the certificate public key");
+           return (0);
+       }
+    }
+    return (1);
+}
+
+
+
+ /*
+  * This is the setup routine for the SSL server. As smtpd might be called
+  * more than once, we only want to do the initialization one time.
+  *
+  * The skeleton of this function is taken from OpenSSL apps/s_server.c.
+
+  * returns -1 on error
+  */
+
+int tls_init_serverengine(int verifydepth,
+                         int askcert,
+                         int requirecert,
+                         char *tls_CAfile,
+                         char *tls_CApath,
+                         char *tls_cert_file,
+                         char *tls_key_file
+                         )
+{
+    int     off = 0;
+    int     verify_flags = SSL_VERIFY_NONE;
+    char   *CApath;
+    char   *CAfile;
+    char   *s_cert_file;
+    char   *s_key_file;
+    struct stat buf;
+
+    if (tls_serverengine)
+      return (0);                              /* already running */
+
+    if (tls_loglevel >= 2)
+      Printf("starting TLS engine");
+
+    SSL_load_error_strings();
+    SSLeay_add_ssl_algorithms();
+
+    CTX = SSL_CTX_new(SSLv23_server_method());
+    if (CTX == NULL) {
+      return (-1);
+    };
+
+    off |= SSL_OP_ALL;         /* Work around all known bugs */
+    SSL_CTX_set_options(CTX, off);
+    SSL_CTX_set_info_callback(CTX, apps_ssl_info_callback);
+    SSL_CTX_sess_set_cache_size(CTX, 128);
+
+    if (strlen(tls_CAfile) == 0)
+      CAfile = NULL;
+    else
+      CAfile = tls_CAfile;
+    if (strlen(tls_CApath) == 0)
+      CApath = NULL;
+    else
+      CApath = tls_CApath;
+
+    if ((!SSL_CTX_load_verify_locations(CTX, CAfile, CApath)) ||
+       (!SSL_CTX_set_default_verify_paths(CTX))) {
+      if (tls_loglevel >= 2)
+       Printf("TLS engine: cannot load CA data\n");
+      return (-1);
+    }
+    
+    if (strlen(tls_cert_file) == 0)
+      s_cert_file = NULL;
+    else
+      s_cert_file = tls_cert_file;
+    if (strlen(tls_key_file) == 0)
+      s_key_file = NULL;
+    else
+      s_key_file = tls_key_file;
+    
+    if (!set_cert_stuff(CTX, s_cert_file, s_key_file)) {
+      if (tls_loglevel >= 2)
+       Printf("TLS engine: cannot load cert/key data\n");
+      return (-1);
+    }
+
+    /* load some randomization data from /dev/urandom, if it exists */
+    /* FIXME: should also check for ".rand" file, update it on exit */
+    if (stat("/dev/urandom", &buf) == 0)
+      RAND_load_file("/dev/urandom", 16 * 1024);
+
+    SSL_CTX_set_tmp_dh_callback(CTX, tmp_dh_cb);
+    SSL_CTX_set_options(CTX, SSL_OP_SINGLE_DH_USE);
+
+    verify_depth = verifydepth;
+    if (askcert!=0)
+       verify_flags |= SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
+    if (requirecert)
+       verify_flags |= SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT
+           | SSL_VERIFY_CLIENT_ONCE;
+    SSL_CTX_set_verify(CTX, verify_flags, verify_callback);
+
+    SSL_CTX_set_client_CA_list(CTX, SSL_load_client_CA_file(CAfile));
+
+    tls_serverengine = 1;
+    return (0);
+}
+
+
+/*
+**  The function called by nnrpd to initialize the TLS support.  Calls
+**  tls_init_server_engine and checks the result.  On any sort of failure,
+**  nnrpd will exit.
+*/
+void
+tls_init(void)
+{
+    int ssl_result;
+
+    if (tls_initialized)
+        return;
+    sasl_config_read();
+    ssl_result = tls_init_serverengine(5,        /* depth to verify */
+                                      0,        /* can client auth? */
+                                      0,        /* required client to auth? */
+                                      (char *)sasl_config_getstring("tls_ca_file", ""),
+                                      (char *)sasl_config_getstring("tls_ca_path", ""),
+                                      (char *)sasl_config_getstring("tls_cert_file", ""),
+                                      (char *)sasl_config_getstring("tls_key_file", ""));
+    if (ssl_result == -1) {
+        Reply("%d Error initializing TLS\r\n", NNTP_STARTTLS_BAD_VAL);
+        syslog(L_ERROR, "error initializing TLS: "
+               "[CA_file: %s] [CA_path: %s] [cert_file: %s] [key_file: %s]",
+               sasl_config_getstring("tls_ca_file", ""),
+               sasl_config_getstring("tls_ca_path", ""),
+               sasl_config_getstring("tls_cert_file", ""),
+               sasl_config_getstring("tls_key_file", ""));
+        ExitWithStats(1, false);
+    }
+    tls_initialized = true;
+}
+
+
+/* taken from OpenSSL apps/s_cb.c */
+
+static long bio_dump_cb(BIO * bio, int cmd, const char *argp, int argi,
+                       long argl UNUSED, long ret)
+{
+    if (!do_dump)
+       return (ret);
+
+    if (cmd == (BIO_CB_READ | BIO_CB_RETURN)) {
+       Printf("read from %08X [%08lX] (%d bytes => %ld (0x%X))", (unsigned int) bio, (long unsigned int) argp,
+                argi, ret, (unsigned int) ret);
+       tls_dump(argp, (int) ret);
+       return (ret);
+    } else if (cmd == (BIO_CB_WRITE | BIO_CB_RETURN)) {
+       Printf("write to %08X [%08lX] (%d bytes => %ld (0x%X))", (unsigned int) bio, (long unsigned int)argp,
+                argi, ret, (unsigned int) ret);
+       tls_dump(argp, (int) ret);
+    }
+    return (ret);
+}
+
+ /*
+  * This is the actual startup routine for the connection. We expect
+  * that the buffers are flushed and the "220 Ready to start TLS" was
+  * send to the client, so that we can immediately can start the TLS
+  * handshake process.
+  *
+  * layerbits and authid are filled in on sucess. authid is only
+  * filled in if the client authenticated
+  * 
+  */
+int tls_start_servertls(int readfd, int writefd)
+{
+    int     sts;
+    int     keepalive;
+    SSL_SESSION *session;
+    SSL_CIPHER *cipher;
+
+    if (!tls_serverengine)
+    {          
+      /* should never happen */
+      syslog(L_ERROR, "tls_engine not running");
+      return (-1);
+    }
+    if (tls_loglevel >= 1)
+       Printf("setting up TLS connection");
+
+    if (tls_conn == NULL)
+    {
+       tls_conn = (SSL *) SSL_new(CTX);
+    }
+    if (tls_conn == NULL)
+    {
+       return (-1);
+    }
+    SSL_clear(tls_conn);
+
+#if    defined(SOL_SOCKET) && defined(SO_KEEPALIVE)
+    /* Set KEEPALIVE to catch broken socket connections. */
+    keepalive = 1;
+    if (setsockopt(readfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)) < 0)
+        syslog(L_ERROR, "fd %d can't setsockopt(KEEPALIVE) %m", readfd);
+    if (setsockopt(writefd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)) < 0)
+        syslog(L_ERROR, "fd %d can't setsockopt(KEEPALIVE) %m", writefd);
+#endif /* SOL_SOCKET && SO_KEEPALIVE */
+    
+    /* set the file descriptors for SSL to use */
+    if (SSL_set_rfd(tls_conn, readfd)==0)
+    {
+      return (-1);
+    }
+
+    if (SSL_set_wfd(tls_conn, writefd)==0)
+    {
+      return (-1);
+    }
+    
+    /*
+     * This is the actual handshake routine. It will do all the negotiations
+     * and will check the client cert etc.
+     */
+    SSL_set_accept_state(tls_conn);
+
+    /*
+     * We do have an SSL_set_fd() and now suddenly a BIO_ routine is called?
+     * Well there is a BIO below the SSL routines that is automatically
+     * created for us, so we can use it for debugging purposes.
+     */
+    if (tls_loglevel >= 3)
+       BIO_set_callback(SSL_get_rbio(tls_conn), bio_dump_cb);
+
+    /* Dump the negotiation for loglevels 3 and 4*/
+    if (tls_loglevel >= 3)
+       do_dump = 1;
+
+      if ((sts = SSL_accept(tls_conn)) <= 0) { /* xxx <= 0 */
+       session = SSL_get_session(tls_conn);
+
+       if (session) {
+         SSL_CTX_remove_session(CTX, session);
+       }
+       if (tls_conn)
+         SSL_free(tls_conn);
+       tls_conn = NULL;
+       return (-1);
+      }
+      /* Only loglevel==4 dumps everything */
+      if (tls_loglevel < 4)
+       do_dump = 0;
+
+    tls_protocol = SSL_get_version(tls_conn);
+    cipher = SSL_get_current_cipher(tls_conn);
+
+    tls_cipher_name = SSL_CIPHER_get_name(cipher);
+    tls_cipher_usebits = SSL_CIPHER_get_bits(cipher,
+                                                &tls_cipher_algbits);
+    tls_serveractive = 1;
+
+    syslog(L_NOTICE, "starttls: %s with cipher %s (%d/%d bits) no authentication", tls_protocol, tls_cipher_name,
+          tls_cipher_usebits, tls_cipher_algbits);
+
+    return (0);
+}
+
+ssize_t
+SSL_writev (ssl, vector, count)
+     SSL *ssl;
+     const struct iovec *vector;
+     int count;
+{
+  static char *buffer = NULL;
+  static size_t allocsize = 0;
+  char *bp;
+  size_t bytes, to_copy;
+  int i;
+  /* Find the total number of bytes to be written.  */
+  bytes = 0;
+  for (i = 0; i < count; ++i)
+    bytes += vector[i].iov_len;
+  /* Allocate a buffer to hold the data.  */
+  if (NULL == buffer) {
+    buffer = (char *) xmalloc(bytes);
+    allocsize = bytes;
+  } else if (bytes > allocsize) {
+    buffer = (char *) xrealloc (buffer, bytes);
+    allocsize = bytes;
+  }
+  /* Copy the data into BUFFER.  */
+  to_copy = bytes;
+  bp = buffer;
+  for (i = 0; i < count; ++i)
+    {
+#define min(a, b)       ((a) > (b) ? (b) : (a))
+      size_t copy = min (vector[i].iov_len, to_copy);
+      memcpy (bp, vector[i].iov_base, copy);
+      bp += copy;
+      to_copy -= copy;
+      if (to_copy == 0)
+        break;
+    }
+  return SSL_write (ssl, buffer, bytes);
+}
+
+
+#endif /* HAVE_SSL */