session server tls UPDATE wrap TLS
diff --git a/src/session_server_tls.c b/src/session_server_tls.c
index 2b562b1..51edfd3 100644
--- a/src/session_server_tls.c
+++ b/src/session_server_tls.c
@@ -24,802 +24,17 @@
 #include <unistd.h>
 
 #include <curl/curl.h>
-#include <openssl/bio.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/ssl.h>
-#include <openssl/x509.h>
-#include <openssl/x509v3.h>
 
 #include "compat.h"
 #include "config.h"
 #include "log_p.h"
 #include "session.h"
 #include "session_p.h"
+#include "session_wrapper.h"
 
 struct nc_server_tls_opts tls_ch_opts;
 extern struct nc_server_opts server_opts;
 
-static char *
-asn1time_to_str(const ASN1_TIME *t)
-{
-    char *cp;
-    BIO *bio;
-    int n;
-
-    if (!t) {
-        return NULL;
-    }
-
-    bio = BIO_new(BIO_s_mem());
-    if (!bio) {
-        return NULL;
-    }
-
-    ASN1_TIME_print(bio, t);
-    n = BIO_pending(bio);
-    cp = malloc(n + 1);
-    if (!cp) {
-        ERRMEM;
-        BIO_free(bio);
-        return NULL;
-    }
-
-    n = BIO_read(bio, cp, n);
-    if (n < 0) {
-        BIO_free(bio);
-        free(cp);
-        return NULL;
-    }
-
-    cp[n] = '\0';
-    BIO_free(bio);
-    return cp;
-}
-
-static void
-digest_to_str(const unsigned char *digest, unsigned int dig_len, char **str)
-{
-    unsigned int i;
-
-    *str = malloc(dig_len * 3);
-    if (!*str) {
-        ERRMEM;
-        return;
-    }
-    for (i = 0; i < dig_len - 1; ++i) {
-        sprintf((*str) + (i * 3), "%02x:", digest[i]);
-    }
-    sprintf((*str) + (i * 3), "%02x", digest[i]);
-}
-
-/* return NULL - SSL error can be retrieved */
-static X509 *
-base64der_to_cert(const char *in)
-{
-    X509 *out;
-    char *buf;
-    BIO *bio;
-
-    if (in == NULL) {
-        return NULL;
-    }
-
-    if (asprintf(&buf, "%s%s%s", "-----BEGIN CERTIFICATE-----\n", in, "\n-----END CERTIFICATE-----") == -1) {
-        return NULL;
-    }
-    bio = BIO_new_mem_buf(buf, strlen(buf));
-    if (!bio) {
-        free(buf);
-        return NULL;
-    }
-
-    out = PEM_read_bio_X509(bio, NULL, NULL, NULL);
-    if (!out) {
-        free(buf);
-        BIO_free(bio);
-        return NULL;
-    }
-
-    free(buf);
-    BIO_free(bio);
-    return out;
-}
-
-static EVP_PKEY *
-base64der_to_privatekey(const char *in, const char *key_str)
-{
-    EVP_PKEY *out;
-    char *buf;
-    BIO *bio;
-
-    if (in == NULL) {
-        return NULL;
-    }
-
-    if (!key_str) {
-        /* avoid writing (null) for possibly unknown key formats */
-        key_str = "";
-    }
-    if (asprintf(&buf, "%s%s%s%s%s%s%s", "-----BEGIN", key_str, "PRIVATE KEY-----\n", in, "\n-----END",
-            key_str, "PRIVATE KEY-----") == -1) {
-        return NULL;
-    }
-    bio = BIO_new_mem_buf(buf, strlen(buf));
-    if (!bio) {
-        free(buf);
-        return NULL;
-    }
-
-    out = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
-    if (!out) {
-        free(buf);
-        BIO_free(bio);
-        return NULL;
-    }
-
-    free(buf);
-    BIO_free(bio);
-    return out;
-}
-
-static int
-cert_pubkey_match(X509 *cert1, X509 *cert2)
-{
-    ASN1_BIT_STRING *bitstr1, *bitstr2;
-
-    bitstr1 = X509_get0_pubkey_bitstr(cert1);
-    bitstr2 = X509_get0_pubkey_bitstr(cert2);
-
-    if (!bitstr1 || !bitstr2 || (bitstr1->length != bitstr2->length) ||
-            memcmp(bitstr1->data, bitstr2->data, bitstr1->length)) {
-        return 0;
-    }
-
-    return 1;
-}
-
-static int
-nc_tls_ctn_get_username_from_cert(X509 *client_cert, NC_TLS_CTN_MAPTYPE map_type, char **username)
-{
-    STACK_OF(GENERAL_NAME) * san_names;
-    GENERAL_NAME *san_name;
-    ASN1_OCTET_STRING *ip;
-    int i, san_count;
-    char *subject, *common_name;
-
-    *username = NULL;
-
-    if (map_type == NC_TLS_CTN_COMMON_NAME) {
-        subject = X509_NAME_oneline(X509_get_subject_name(client_cert), NULL, 0);
-        common_name = strstr(subject, "CN=");
-        if (!common_name) {
-            WRN(NULL, "Certificate does not include the commonName field.");
-            free(subject);
-            return 1;
-        }
-        common_name += 3;
-        if (strchr(common_name, '/')) {
-            *strchr(common_name, '/') = '\0';
-        }
-        *username = strdup(common_name);
-        NC_CHECK_ERRMEM_RET(!*username, 1);
-        free(subject);
-    } else {
-        /* retrieve subjectAltName's rfc822Name (email), dNSName and iPAddress values */
-        san_names = X509_get_ext_d2i(client_cert, NID_subject_alt_name, NULL, NULL);
-        if (!san_names) {
-            WRN(NULL, "Certificate has no SANs or failed to retrieve them.");
-            return 1;
-        }
-
-        san_count = sk_GENERAL_NAME_num(san_names);
-        for (i = 0; i < san_count; ++i) {
-            san_name = sk_GENERAL_NAME_value(san_names, i);
-
-            /* rfc822Name (email) */
-            if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_RFC822_NAME)) &&
-                    (san_name->type == GEN_EMAIL)) {
-                *username = strdup((char *)ASN1_STRING_get0_data(san_name->d.rfc822Name));
-                NC_CHECK_ERRMEM_RET(!*username, 1);
-                break;
-            }
-
-            /* dNSName */
-            if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_DNS_NAME)) &&
-                    (san_name->type == GEN_DNS)) {
-                *username = strdup((char *)ASN1_STRING_get0_data(san_name->d.dNSName));
-                NC_CHECK_ERRMEM_RET(!*username, 1);
-                break;
-            }
-
-            /* iPAddress */
-            if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_IP_ADDRESS)) &&
-                    (san_name->type == GEN_IPADD)) {
-                ip = san_name->d.iPAddress;
-                if (ip->length == 4) {
-                    if (asprintf(username, "%d.%d.%d.%d", ip->data[0], ip->data[1], ip->data[2], ip->data[3]) == -1) {
-                        ERRMEM;
-                        sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
-                        return -1;
-                    }
-                    break;
-                } else if (ip->length == 16) {
-                    if (asprintf(username, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
-                            ip->data[0], ip->data[1], ip->data[2], ip->data[3], ip->data[4], ip->data[5],
-                            ip->data[6], ip->data[7], ip->data[8], ip->data[9], ip->data[10], ip->data[11],
-                            ip->data[12], ip->data[13], ip->data[14], ip->data[15]) == -1) {
-                        ERRMEM;
-                        sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
-                        return -1;
-                    }
-                    break;
-                } else {
-                    WRN(NULL, "SAN IP address in an unknown format (length is %d).", ip->length);
-                }
-            }
-        }
-        sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
-
-        if (i == san_count) {
-            switch (map_type) {
-            case NC_TLS_CTN_SAN_RFC822_NAME:
-                WRN(NULL, "Certificate does not include the SAN rfc822Name field.");
-                break;
-            case NC_TLS_CTN_SAN_DNS_NAME:
-                WRN(NULL, "Certificate does not include the SAN dNSName field.");
-                break;
-            case NC_TLS_CTN_SAN_IP_ADDRESS:
-                WRN(NULL, "Certificate does not include the SAN iPAddress field.");
-                break;
-            case NC_TLS_CTN_SAN_ANY:
-                WRN(NULL, "Certificate does not include any relevant SAN fields.");
-                break;
-            default:
-                break;
-            }
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-/* return: 0 - OK, 1 - no match, -1 - error */
-static int
-nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 *cert)
-{
-    char *digest_md5 = NULL, *digest_sha1 = NULL, *digest_sha224 = NULL;
-    char *digest_sha256 = NULL, *digest_sha384 = NULL, *digest_sha512 = NULL;
-    unsigned char *buf;
-    unsigned int buf_len = 64;
-    int ret = 0;
-    struct nc_ctn *ctn;
-    NC_TLS_CTN_MAPTYPE map_type;
-    char *username = NULL;
-
-    buf = malloc(buf_len);
-    NC_CHECK_ERRMEM_RET(!buf, -1);
-
-    if (!session || !cert) {
-        free(buf);
-        return -1;
-    }
-
-    for (ctn = ctn_first; ctn; ctn = ctn->next) {
-        /* reset map_type */
-        map_type = NC_TLS_CTN_UNKNOWN;
-
-        /* first make sure the entry is valid */
-        if (!ctn->map_type || ((ctn->map_type == NC_TLS_CTN_SPECIFIED) && !ctn->name)) {
-            VRB(session, "Cert verify CTN: entry with id %u not valid, skipping.", ctn->id);
-            continue;
-        }
-
-        /* if ctn has no fingerprint, it will match any certificate */
-        if (!ctn->fingerprint) {
-            map_type = ctn->map_type;
-
-            /* MD5 */
-        } else if (!strncmp(ctn->fingerprint, "01", 2)) {
-            if (!digest_md5) {
-                if (X509_digest(cert, EVP_md5(), buf, &buf_len) != 1) {
-                    ERR(session, "Calculating MD5 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
-                    ret = -1;
-                    goto cleanup;
-                }
-                digest_to_str(buf, buf_len, &digest_md5);
-            }
-
-            if (!strcasecmp(ctn->fingerprint + 3, digest_md5)) {
-                /* we got ourselves a potential winner! */
-                VRB(session, "Cert verify CTN: entry with a matching fingerprint found.");
-                map_type = ctn->map_type;
-            }
-            free(digest_md5);
-            digest_md5 = NULL;
-
-            /* SHA-1 */
-        } else if (!strncmp(ctn->fingerprint, "02", 2)) {
-            if (!digest_sha1) {
-                if (X509_digest(cert, EVP_sha1(), buf, &buf_len) != 1) {
-                    ERR(session, "Calculating SHA-1 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
-                    ret = -1;
-                    goto cleanup;
-                }
-                digest_to_str(buf, buf_len, &digest_sha1);
-            }
-
-            if (!strcasecmp(ctn->fingerprint + 3, digest_sha1)) {
-                /* we got ourselves a potential winner! */
-                VRB(session, "Cert verify CTN: entry with a matching fingerprint found.");
-                map_type = ctn->map_type;
-            }
-            free(digest_sha1);
-            digest_sha1 = NULL;
-
-            /* SHA-224 */
-        } else if (!strncmp(ctn->fingerprint, "03", 2)) {
-            if (!digest_sha224) {
-                if (X509_digest(cert, EVP_sha224(), buf, &buf_len) != 1) {
-                    ERR(session, "Calculating SHA-224 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
-                    ret = -1;
-                    goto cleanup;
-                }
-                digest_to_str(buf, buf_len, &digest_sha224);
-            }
-
-            if (!strcasecmp(ctn->fingerprint + 3, digest_sha224)) {
-                /* we got ourselves a potential winner! */
-                VRB(session, "Cert verify CTN: entry with a matching fingerprint found.");
-                map_type = ctn->map_type;
-            }
-            free(digest_sha224);
-            digest_sha224 = NULL;
-
-            /* SHA-256 */
-        } else if (!strncmp(ctn->fingerprint, "04", 2)) {
-            if (!digest_sha256) {
-                if (X509_digest(cert, EVP_sha256(), buf, &buf_len) != 1) {
-                    ERR(session, "Calculating SHA-256 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
-                    ret = -1;
-                    goto cleanup;
-                }
-                digest_to_str(buf, buf_len, &digest_sha256);
-            }
-
-            if (!strcasecmp(ctn->fingerprint + 3, digest_sha256)) {
-                /* we got ourselves a potential winner! */
-                VRB(session, "Cert verify CTN: entry with a matching fingerprint found.");
-                map_type = ctn->map_type;
-            }
-            free(digest_sha256);
-            digest_sha256 = NULL;
-
-            /* SHA-384 */
-        } else if (!strncmp(ctn->fingerprint, "05", 2)) {
-            if (!digest_sha384) {
-                if (X509_digest(cert, EVP_sha384(), buf, &buf_len) != 1) {
-                    ERR(session, "Calculating SHA-384 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
-                    ret = -1;
-                    goto cleanup;
-                }
-                digest_to_str(buf, buf_len, &digest_sha384);
-            }
-
-            if (!strcasecmp(ctn->fingerprint + 3, digest_sha384)) {
-                /* we got ourselves a potential winner! */
-                VRB(session, "Cert verify CTN: entry with a matching fingerprint found.");
-                map_type = ctn->map_type;
-            }
-            free(digest_sha384);
-            digest_sha384 = NULL;
-
-            /* SHA-512 */
-        } else if (!strncmp(ctn->fingerprint, "06", 2)) {
-            if (!digest_sha512) {
-                if (X509_digest(cert, EVP_sha512(), buf, &buf_len) != 1) {
-                    ERR(session, "Calculating SHA-512 digest failed (%s).", ERR_reason_error_string(ERR_get_error()));
-                    ret = -1;
-                    goto cleanup;
-                }
-                digest_to_str(buf, buf_len, &digest_sha512);
-            }
-
-            if (!strcasecmp(ctn->fingerprint + 3, digest_sha512)) {
-                /* we got ourselves a potential winner! */
-                VRB(session, "Cert verify CTN: entry with a matching fingerprint found.");
-                map_type = ctn->map_type;
-            }
-            free(digest_sha512);
-            digest_sha512 = NULL;
-
-            /* unknown */
-        } else {
-            WRN(session, "Unknown fingerprint algorithm used (%s), skipping.", ctn->fingerprint);
-            continue;
-        }
-
-        if (map_type != NC_TLS_CTN_UNKNOWN) {
-            /* found a fingerprint match */
-            if (map_type == NC_TLS_CTN_SPECIFIED) {
-                /* specified -> get username from the ctn entry */
-                session->username = strdup(ctn->name);
-                NC_CHECK_ERRMEM_GOTO(!session->username, ret = -1, cleanup);
-            } else {
-                /* try to get the username from the cert with this ctn's map type */
-                ret = nc_tls_ctn_get_username_from_cert(session->opts.server.client_cert, map_type, &username);
-                if (ret == -1) {
-                    /* fatal error */
-                    goto cleanup;
-                } else if (ret) {
-                    /* didn't get username, try next ctn entry */
-                    continue;
-                }
-
-                /* success */
-                session->username = username;
-            }
-
-            /* matching fingerprint found and username obtained, success */
-            ret = 0;
-            goto cleanup;
-        }
-    }
-
-    if (!ctn) {
-        ret = 1;
-    }
-
-cleanup:
-    free(digest_md5);
-    free(digest_sha1);
-    free(digest_sha224);
-    free(digest_sha256);
-    free(digest_sha384);
-    free(digest_sha512);
-    free(buf);
-    return ret;
-}
-
-static int
-nc_server_tls_check_crl(X509_STORE *crl_store, X509_STORE_CTX *x509_ctx, X509 *cert,
-        const X509_NAME *subject, const X509_NAME *issuer)
-{
-    int n, i, ret = 0;
-    X509_STORE_CTX *store_ctx = NULL;
-    X509_OBJECT *obj = NULL;
-    X509_CRL *crl;
-    X509_REVOKED *revoked;
-    EVP_PKEY *pubkey;
-    const ASN1_INTEGER *serial;
-    const ASN1_TIME *last_update = NULL, *next_update = NULL;
-    char *cp;
-
-    store_ctx = X509_STORE_CTX_new();
-    NC_CHECK_ERRMEM_GOTO(!store_ctx, ret = -1, cleanup);
-
-    /* init store context */
-    ret = X509_STORE_CTX_init(store_ctx, crl_store, NULL, NULL);
-    if (!ret) {
-        ERR(NULL, "Initializing x509 store ctx failed (%s).", ERR_reason_error_string(ERR_get_error()));
-        ret = -1;
-        goto cleanup;
-    }
-    ret = 0;
-
-    /* try to find a CRL entry that corresponds to the current certificate in question */
-    obj = X509_STORE_CTX_get_obj_by_subject(store_ctx, X509_LU_CRL, subject);
-    crl = X509_OBJECT_get0_X509_CRL(obj);
-    X509_OBJECT_free(obj);
-    if (crl) {
-        /* found it */
-        cp = X509_NAME_oneline(subject, NULL, 0);
-        VRB(NULL, "Cert verify CRL: issuer: %s.", cp);
-        OPENSSL_free(cp);
-
-        last_update = X509_CRL_get0_lastUpdate(crl);
-        next_update = X509_CRL_get0_nextUpdate(crl);
-        cp = asn1time_to_str(last_update);
-        VRB(NULL, "Cert verify CRL: last update: %s.", cp);
-        free(cp);
-        cp = asn1time_to_str(next_update);
-        VRB(NULL, "Cert verify CRL: next update: %s.", cp);
-        free(cp);
-
-        /* verify the signature on this CRL */
-        pubkey = X509_get0_pubkey(cert);
-        if (X509_CRL_verify(crl, pubkey) <= 0) {
-            ERR(NULL, "Cert verify CRL: invalid signature.");
-            X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
-            ret = -1;
-            goto cleanup;
-        }
-
-        /* check date of CRL to make sure it's not expired */
-        if (!next_update) {
-            ERR(NULL, "Cert verify CRL: invalid nextUpdate field.");
-            X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
-            ret = -1;
-            goto cleanup;
-        }
-
-        if (X509_cmp_current_time(next_update) < 0) {
-            ERR(NULL, "Cert verify CRL: expired - revoking all certificates.");
-            X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
-            ret = -1;
-            goto cleanup;
-        }
-    }
-
-    /* try to retrieve a CRL corresponding to the _issuer_ of
-     * the current certificate in order to check for revocation */
-    obj = X509_STORE_CTX_get_obj_by_subject(store_ctx, X509_LU_CRL, issuer);
-    crl = X509_OBJECT_get0_X509_CRL(obj);
-    if (crl) {
-        n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
-        for (i = 0; i < n; i++) {
-            revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
-            serial = X509_REVOKED_get0_serialNumber(revoked);
-            if (ASN1_INTEGER_cmp(serial, X509_get_serialNumber(cert)) == 0) {
-                cp = X509_NAME_oneline(issuer, NULL, 0);
-                ERR(NULL, "Cert verify CRL: certificate with serial %ld (0x%lX) revoked per CRL from issuer %s.",
-                        serial, serial, cp);
-                OPENSSL_free(cp);
-                X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
-                ret = -1;
-                goto cleanup;
-            }
-        }
-    }
-
-cleanup:
-    X509_STORE_CTX_free(store_ctx);
-    X509_OBJECT_free(obj);
-    return ret;
-}
-
-static int
-nc_server_tls_ts_ref_get_certs(const char *referenced_name, struct nc_certificate **certs, uint16_t *cert_count)
-{
-    uint16_t i;
-    struct nc_truststore *ts = &server_opts.truststore;
-
-    *certs = NULL;
-    *cert_count = 0;
-
-    /* lookup name */
-    for (i = 0; i < ts->cert_bag_count; i++) {
-        if (!strcmp(referenced_name, ts->cert_bags[i].name)) {
-            break;
-        }
-    }
-
-    if (i == ts->cert_bag_count) {
-        ERR(NULL, "Truststore entry \"%s\" not found.", referenced_name);
-        return -1;
-    }
-
-    *certs = ts->cert_bags[i].certs;
-    *cert_count = ts->cert_bags[i].cert_count;
-    return 0;
-}
-
-/* In case a CA chain verification failed an end-entity certificate must match.
- * The meaning of local_or_referenced is that it states, which end-entity certificates to check
- * (1 = current endpoint's, 2 = referenced endpoint's).
- */
-static int
-nc_server_tls_do_preverify(struct nc_session *session, X509_STORE_CTX *x509_ctx, int local_or_referenced)
-{
-    X509_STORE *store;
-    struct nc_cert_grouping *ee_certs;
-    int i, ret;
-    X509 *cert;
-    struct nc_certificate *certs;
-    uint16_t cert_count;
-
-    store = X509_STORE_CTX_get0_store(x509_ctx);
-    if (!store) {
-        ERR(session, "Error getting store from context (%s).", ERR_reason_error_string(ERR_get_error()));
-        return -1;
-    }
-
-    /* get the data from the store */
-    ee_certs = X509_STORE_get_ex_data(store, local_or_referenced);
-    if (!ee_certs) {
-        ERR(session, "Error getting data from store (%s).", ERR_reason_error_string(ERR_get_error()));
-        return -1;
-    }
-
-    if (ee_certs->store == NC_STORE_LOCAL) {
-        /* local definition */
-        certs = ee_certs->certs;
-        cert_count = ee_certs->cert_count;
-    } else {
-        /* truststore reference */
-        if (nc_server_tls_ts_ref_get_certs(ee_certs->ts_ref, &certs, &cert_count)) {
-            ERR(NULL, "Error getting end-entity certificates from the truststore reference \"%s\".", ee_certs->ts_ref);
-            return -1;
-        }
-    }
-
-    for (i = 0; i < cert_count; i++) {
-        cert = base64der_to_cert(certs[i].data);
-        ret = cert_pubkey_match(session->opts.server.client_cert, cert);
-        X509_free(cert);
-        if (ret) {
-            /* we are just overriding the failed standard certificate verification (preverify_ok == 0),
-             * this callback will be called again with the same current certificate and preverify_ok == 1 */
-            VRB(session, "Cert verify: fail (%s), but the end-entity certificate is trusted, continuing.",
-                    X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx)));
-            X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-static int
-nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
-{
-    X509_NAME *subject;
-    X509_NAME *issuer;
-    X509 *cert;
-    char *cp;
-    X509_STORE *store;
-
-    STACK_OF(X509) * cert_stack;
-    struct nc_session *session;
-    struct nc_server_tls_opts *opts;
-    struct nc_endpt *referenced_endpt;
-    int rc, depth;
-
-    store = X509_STORE_CTX_get0_store(x509_ctx);
-    if (!store) {
-        ERR(NULL, "Error getting store from context (%s).", ERR_reason_error_string(ERR_get_error()));
-        return 0;
-    }
-
-    /* get session from the store */
-    session = X509_STORE_get_ex_data(store, 0);
-    if (!session) {
-        ERR(session, "Error getting session from store (%s).", ERR_reason_error_string(ERR_get_error()));
-        return 0;
-    }
-
-    opts = session->data;
-
-    /* get the last certificate, that is the peer (client) certificate */
-    if (!session->opts.server.client_cert) {
-        cert_stack = X509_STORE_CTX_get1_chain(x509_ctx);
-        session->opts.server.client_cert = sk_X509_value(cert_stack, 0);
-        X509_up_ref(session->opts.server.client_cert);
-        sk_X509_pop_free(cert_stack, X509_free);
-    }
-
-    /* standard certificate verification failed, so an end-entity client cert must match to continue */
-    if (!preverify_ok) {
-        /* check current endpoint's end-entity certs */
-        rc = nc_server_tls_do_preverify(session, x509_ctx, 1);
-        if (rc == -1) {
-            return 0;
-        } else if (rc == 1) {
-            return 1;
-        }
-
-        /* no match, continue */
-        if (opts->referenced_endpt_name) {
-            /* check referenced endpoint's end-entity certs */
-            rc = nc_server_tls_do_preverify(session, x509_ctx, 2);
-            if (rc == -1) {
-                return 0;
-            } else if (rc == 1) {
-                return 1;
-            }
-        }
-
-        /* no match, fail */
-        ERR(session, "Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx)));
-        return 0;
-    }
-
-    /* print cert verify info */
-    depth = X509_STORE_CTX_get_error_depth(x509_ctx);
-    VRB(session, "Cert verify: depth %d.", depth);
-
-    cert = X509_STORE_CTX_get_current_cert(x509_ctx);
-    subject = X509_get_subject_name(cert);
-    issuer = X509_get_issuer_name(cert);
-
-    cp = X509_NAME_oneline(subject, NULL, 0);
-    VRB(session, "Cert verify: subject: %s.", cp);
-    OPENSSL_free(cp);
-    cp = X509_NAME_oneline(issuer, NULL, 0);
-    VRB(session, "Cert verify: issuer:  %s.", cp);
-    OPENSSL_free(cp);
-
-    /* check if the current certificate is revoked if CRL is set */
-    if (opts->crl_store) {
-        rc = nc_server_tls_check_crl(opts->crl_store, x509_ctx, cert, subject, issuer);
-        if (rc) {
-            return 0;
-        }
-    }
-
-    /* cert-to-name already successful */
-    if (session->username) {
-        return 1;
-    }
-
-    /* cert-to-name */
-    rc = nc_tls_cert_to_name(session, opts->ctn, cert);
-    if (rc == -1) {
-        /* fatal error */
-        depth = 0;
-        goto fail;
-    } else if ((rc == 1) && !opts->referenced_endpt_name) {
-        /* no match found and no referenced endpoint */
-        goto fail;
-    } else if ((rc == 1) && opts->referenced_endpt_name) {
-        /* no match found, but has a referenced endpoint so try it */
-        if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) {
-            /* fatal error */
-            ERRINT;
-            depth = 0;
-            goto fail;
-        }
-
-        rc = nc_tls_cert_to_name(session, referenced_endpt->opts.tls->ctn, cert);
-        if (rc) {
-            if (rc == -1) {
-                /* fatal error */
-                depth = 0;
-            }
-            /* rc == 1 is a normal CTN fail (no match found) */
-            goto fail;
-        }
-    }
-
-    VRB(session, "Cert verify CTN: new client username recognized as \"%s\".", session->username);
-
-    if (server_opts.user_verify_clb && !server_opts.user_verify_clb(session)) {
-        VRB(session, "Cert verify: user verify callback revoked authorization.");
-        X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
-        return 0;
-    }
-
-    return 1;
-
-fail:
-    if (depth > 0) {
-        VRB(session, "Cert verify CTN: cert fail, cert-to-name will continue on the next cert in chain.");
-        return 1;
-    }
-
-    VRB(session, "Cert-to-name unsuccessful, dropping the new client.");
-    X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
-    return 0;
-}
-
-API const X509 *
-nc_session_get_client_cert(const struct nc_session *session)
-{
-    if (!session || (session->side != NC_SERVER)) {
-        ERRARG(session, "session");
-        return NULL;
-    }
-
-    return session->opts.server.client_cert;
-}
-
-API void
-nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *session))
-{
-    server_opts.user_verify_clb = verify_clb;
-}
-
 static int
 nc_server_tls_ks_ref_get_cert_key(const char *referenced_key_name, const char *referenced_cert_name,
         char **privkey_data, NC_PRIVKEY_FORMAT *privkey_type, char **cert_data)
@@ -859,15 +74,507 @@
 }
 
 static int
-nc_tls_ctx_set_server_cert_key(SSL_CTX *tls_ctx, struct nc_server_tls_opts *opts)
+nc_server_tls_ts_ref_get_certs(const char *referenced_name, struct nc_certificate **certs, uint16_t *cert_count)
+{
+    uint16_t i;
+    struct nc_truststore *ts = &server_opts.truststore;
+
+    *certs = NULL;
+    *cert_count = 0;
+
+    /* lookup name */
+    for (i = 0; i < ts->cert_bag_count; i++) {
+        if (!strcmp(referenced_name, ts->cert_bags[i].name)) {
+            break;
+        }
+    }
+
+    if (i == ts->cert_bag_count) {
+        ERR(NULL, "Truststore entry \"%s\" not found.", referenced_name);
+        return -1;
+    }
+
+    *certs = ts->cert_bags[i].certs;
+    *cert_count = ts->cert_bags[i].cert_count;
+    return 0;
+}
+
+static void *
+nc_base64der_to_cert(const char *in)
+{
+    char *buf;
+    void *cert = NULL;
+
+    NC_CHECK_ARG_RET(NULL, in, NULL);
+
+    if (asprintf(&buf, "%s%s%s", "-----BEGIN CERTIFICATE-----\n", in, "\n-----END CERTIFICATE-----") == -1) {
+        ERRMEM;
+        return NULL;
+    }
+
+    cert = nc_tls_pem_to_cert_wrap(buf);
+    free(buf);
+    return cert;
+}
+
+int
+nc_base64der_to_cert_add_to_store(const char *in, void *cert_store)
+{
+    int ret;
+    char *buf;
+
+    NC_CHECK_ARG_RET(NULL, in, cert_store, 1);
+
+    if (asprintf(&buf, "%s%s%s", "-----BEGIN CERTIFICATE-----\n", in, "\n-----END CERTIFICATE-----") == -1) {
+        ERRMEM;
+        return 1;
+    }
+
+    ret = nc_tls_pem_to_cert_add_to_store_wrap(buf, cert_store);
+    free(buf);
+    return ret;
+}
+
+static void *
+nc_base64der_to_privatekey(const char *in, const char *key_str)
+{
+    char *buf;
+    void *pkey;
+
+    NC_CHECK_ARG_RET(NULL, in, NULL);
+
+    if (asprintf(&buf, "%s%s%s%s%s%s%s", "-----BEGIN ", key_str, " PRIVATE KEY-----\n", in, "\n-----END ",
+            key_str, " PRIVATE KEY-----") == -1) {
+        ERRMEM;
+        return NULL;
+    }
+
+    pkey = nc_tls_pem_to_privkey_wrap(buf);
+    free(buf);
+    return pkey;
+}
+
+char *
+nc_server_tls_digest_to_hex(const unsigned char *digest, unsigned int digest_len)
+{
+    unsigned int i;
+    char *hex;
+
+    hex = malloc(digest_len * 3);
+    NC_CHECK_ERRMEM_RET(!hex, NULL);
+
+    for (i = 0; i < digest_len - 1; ++i) {
+        sprintf(hex + (i * 3), "%02x:", digest[i]);
+    }
+    sprintf(hex + (i * 3), "%02x", digest[i]);
+
+    return hex;
+}
+
+char *
+nc_server_tls_md5(void *cert)
+{
+    int rc;
+    unsigned int buf_len = 16;
+    unsigned char buf[buf_len];
+
+    /* compute MD-5 hash of cert and store it in buf */
+    rc = nc_server_tls_md5_wrap(cert, buf);
+    if (rc) {
+        return NULL;
+    }
+
+    /* convert the hash to hex */
+    return nc_server_tls_digest_to_hex(buf, buf_len);
+}
+
+char *
+nc_server_tls_sha1(void *cert)
+{
+    int rc;
+    unsigned int buf_len = 20;
+    unsigned char buf[buf_len];
+
+    /* compute SHA-1 hash of cert and store it in buf */
+    rc = nc_server_tls_sha1_wrap(cert, buf);
+    if (rc) {
+        return NULL;
+    }
+
+    /* convert the hash to hex */
+    return nc_server_tls_digest_to_hex(buf, buf_len);
+}
+
+char *
+nc_server_tls_sha224(void *cert)
+{
+    int rc;
+    unsigned int buf_len = 28;
+    unsigned char buf[buf_len];
+
+    /* compute SHA-224 hash of cert and store it in buf */
+    rc = nc_server_tls_sha224_wrap(cert, buf);
+    if (rc) {
+        return NULL;
+    }
+
+    /* convert the hash to hex */
+    return nc_server_tls_digest_to_hex(buf, buf_len);
+}
+
+char *
+nc_server_tls_sha256(void *cert)
+{
+    int rc;
+    unsigned int buf_len = 32;
+    unsigned char buf[buf_len];
+
+    /* compute SHA-256 hash of cert and store it in buf */
+    rc = nc_server_tls_sha256_wrap(cert, buf);
+    if (rc) {
+        return NULL;
+    }
+
+    /* convert the hash to hex */
+    return nc_server_tls_digest_to_hex(buf, buf_len);
+}
+
+char *
+nc_server_tls_sha384(void *cert)
+{
+    int rc;
+    unsigned int buf_len = 48;
+    unsigned char buf[buf_len];
+
+    /* compute SHA-384 hash of cert and store it in buf */
+    rc = nc_server_tls_sha384_wrap(cert, buf);
+    if (rc) {
+        return NULL;
+    }
+
+    /* convert the hash to hex */
+    return nc_server_tls_digest_to_hex(buf, buf_len);
+}
+
+char *
+nc_server_tls_sha512(void *cert)
+{
+    int rc;
+    unsigned int buf_len = 64;
+    unsigned char buf[buf_len];
+
+    /* compute SHA-512 hash of cert and store it in buf */
+    rc = nc_server_tls_sha512_wrap(cert, buf);
+    if (rc) {
+        return NULL;
+    }
+
+    /* convert the hash to hex */
+    return nc_server_tls_digest_to_hex(buf, buf_len);
+}
+
+static int
+nc_server_tls_cert_to_name(struct nc_ctn *ctn_first, void *cert, struct nc_ctn_data *data)
+{
+    int ret = 0;
+    char *digest_md5 = NULL, *digest_sha1 = NULL, *digest_sha224 = NULL;
+    char *digest_sha256 = NULL, *digest_sha384 = NULL, *digest_sha512 = NULL;
+    struct nc_ctn *ctn;
+    NC_TLS_CTN_MAPTYPE map_type;
+
+    for (ctn = ctn_first; ctn; ctn = ctn->next) {
+        /* reset map_type */
+        map_type = NC_TLS_CTN_UNKNOWN;
+
+        /* first make sure the entry is valid */
+        if (!ctn->map_type || ((ctn->map_type == NC_TLS_CTN_SPECIFIED) && !ctn->name)) {
+            VRB(NULL, "Cert verify CTN: entry with id %u not valid, skipping.", ctn->id);
+            continue;
+        }
+
+        /* if ctn has no fingerprint, it will match any certificate */
+        if (!ctn->fingerprint) {
+            map_type = ctn->map_type;
+
+            /* MD5 */
+        } else if (!strncmp(ctn->fingerprint, "01", 2)) {
+            if (!digest_md5) {
+                digest_md5 = nc_server_tls_md5(cert);
+                if (!digest_md5) {
+                    ret = -1;
+                    goto cleanup;
+                }
+            }
+
+            if (!strcasecmp(ctn->fingerprint + 3, digest_md5)) {
+                /* we got ourselves a potential winner! */
+                VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
+                map_type = ctn->map_type;
+            }
+
+            /* SHA-1 */
+        } else if (!strncmp(ctn->fingerprint, "02", 2)) {
+            if (!digest_sha1) {
+                digest_sha1 = nc_server_tls_sha1(cert);
+                if (!digest_sha1) {
+                    ret = -1;
+                    goto cleanup;
+                }
+            }
+
+            if (!strcasecmp(ctn->fingerprint + 3, digest_sha1)) {
+                /* we got ourselves a potential winner! */
+                VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
+                map_type = ctn->map_type;
+            }
+
+            /* SHA-224 */
+        } else if (!strncmp(ctn->fingerprint, "03", 2)) {
+            if (!digest_sha224) {
+                digest_sha224 = nc_server_tls_sha224(cert);
+                if (!digest_sha224) {
+                    ret = -1;
+                    goto cleanup;
+                }
+            }
+
+            if (!strcasecmp(ctn->fingerprint + 3, digest_sha224)) {
+                /* we got ourselves a potential winner! */
+                VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
+                map_type = ctn->map_type;
+            }
+
+            /* SHA-256 */
+        } else if (!strncmp(ctn->fingerprint, "04", 2)) {
+            if (!digest_sha256) {
+                digest_sha256 = nc_server_tls_sha256(cert);
+                if (!digest_sha256) {
+                    ret = -1;
+                    goto cleanup;
+                }
+            }
+
+            if (!strcasecmp(ctn->fingerprint + 3, digest_sha256)) {
+                /* we got ourselves a potential winner! */
+                VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
+                map_type = ctn->map_type;
+            }
+
+            /* SHA-384 */
+        } else if (!strncmp(ctn->fingerprint, "05", 2)) {
+            if (!digest_sha384) {
+                digest_sha384 = nc_server_tls_sha384(cert);
+                if (!digest_sha384) {
+                    ret = -1;
+                    goto cleanup;
+                }
+            }
+
+            if (!strcasecmp(ctn->fingerprint + 3, digest_sha384)) {
+                /* we got ourselves a potential winner! */
+                VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
+                map_type = ctn->map_type;
+            }
+
+            /* SHA-512 */
+        } else if (!strncmp(ctn->fingerprint, "06", 2)) {
+            if (!digest_sha512) {
+                digest_sha512 = nc_server_tls_sha512(cert);
+                if (!digest_sha512) {
+                    ret = -1;
+                    goto cleanup;
+                }
+            }
+
+            if (!strcasecmp(ctn->fingerprint + 3, digest_sha512)) {
+                /* we got ourselves a potential winner! */
+                VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
+                map_type = ctn->map_type;
+            }
+
+            /* unknown */
+        } else {
+            WRN(NULL, "Unknown fingerprint algorithm used (%s), skipping.", ctn->fingerprint);
+            continue;
+        }
+
+        if (map_type != NC_TLS_CTN_UNKNOWN) {
+            /* found a fingerprint match */
+            if (!(map_type & data->matched_ctns)) {
+                data->matched_ctns |= map_type;
+                data->matched_ctn_type[data->matched_ctn_count++] = map_type;
+                if (!data->username && map_type == NC_TLS_CTN_SPECIFIED) {
+                    data->username = ctn->name; // TODO make a copy?
+                }
+            }
+        }
+    }
+
+cleanup:
+    free(digest_md5);
+    free(digest_sha1);
+    free(digest_sha224);
+    free(digest_sha256);
+    free(digest_sha384);
+    free(digest_sha512);
+    return ret;
+}
+
+static int
+nc_server_tls_verify_peer_cert(void *peer_cert, struct nc_cert_grouping *ee_certs)
+{
+    int i, ret;
+    void *cert;
+    struct nc_certificate *certs;
+    uint16_t cert_count;
+
+    if (ee_certs->store == NC_STORE_LOCAL) {
+        /* local definition */
+        certs = ee_certs->certs;
+        cert_count = ee_certs->cert_count;
+    } else {
+        /* truststore reference */
+        if (nc_server_tls_ts_ref_get_certs(ee_certs->ts_ref, &certs, &cert_count)) {
+            ERR(NULL, "Error getting end-entity certificates from the truststore reference \"%s\".", ee_certs->ts_ref);
+            return -1;
+        }
+    }
+
+    for (i = 0; i < cert_count; i++) {
+        cert = nc_tls_base64_to_cert_wrap(certs[i].data);
+        if (!cert) {
+            /* TODO skip? */
+            continue;
+        }
+        ret = nc_server_tls_certs_match_wrap(peer_cert, cert);
+        nc_tls_cert_destroy_wrap(cert);
+        if (ret) {
+            /* found a match */
+            VRB(NULL, "Cert verify: fail, but the end-entity certificate is trusted, continuing.");
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+int
+nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_verify_cb_data *cb_data)
+{
+    int ret = 0, i;
+    char *subject = NULL, *issuer = NULL;
+    struct nc_server_tls_opts *opts = cb_data->opts;
+    struct nc_session *session = cb_data->session;
+    struct nc_endpt *referenced_endpt;
+
+    subject = nc_server_tls_get_subject_wrap(cert);
+    issuer = nc_server_tls_get_issuer_wrap(cert);
+    if (!subject || !issuer) {
+        ERR(session, "Failed to get certificate's subject or issuer.");
+        ret = -1;
+        goto cleanup;
+    }
+
+    VRB(session, "Cert verify: depth %d.", depth);
+    VRB(session, "Cert verify: subject: %s.", subject);
+    VRB(session, "Cert verify: issuer: %s.", issuer);
+
+    if (depth == 0) {
+        if (self_signed) {
+            /* peer cert is not trusted, so it must match any configured end-entity cert
+             * on the given endpoint in order for the cert to be authenticated */
+            ret = nc_server_tls_verify_peer_cert(cert, &opts->ee_certs);
+            if (ret) {
+                /* we can still check the referenced endpoint's ee certs */
+                if (opts->referenced_endpt_name) {
+                    if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) {
+                        ERRINT;
+                        ret = -1;
+                        goto cleanup;
+                    }
+
+                    ret = nc_server_tls_verify_peer_cert(cert, &referenced_endpt->opts.tls->ee_certs);
+                }
+                if (ret) {
+                    ERR(session, "Cert verify: fail (Client certificate not trusted and does not match any configured end-entity certificate).");
+                    goto cleanup;
+                }
+            }
+        }
+    }
+
+    ret = nc_server_tls_cert_to_name(opts->ctn, cert, &cb_data->ctn_data);
+    if (ret == -1) {
+        /* fatal error */
+        goto cleanup;
+    }
+
+    /* check the referenced endpoint's ctn entries */
+    if (opts->referenced_endpt_name) {
+        if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) {
+            ERRINT;
+            ret = -1;
+            goto cleanup;
+        }
+
+        ret = nc_server_tls_cert_to_name(referenced_endpt->opts.tls->ctn, cert, &cb_data->ctn_data);
+        if (ret == -1) {
+            /* fatal error */
+            goto cleanup;
+        }
+    }
+
+    /* ctn */
+    if (depth == 0) {
+        for (i = 0; i < cb_data->ctn_data.matched_ctn_count; i++) {
+            if (cb_data->ctn_data.matched_ctn_type[i] == NC_TLS_CTN_SPECIFIED) {
+                session->username = strdup(cb_data->ctn_data.username);
+                NC_CHECK_ERRMEM_RET(!session->username, -1);
+            } else {
+                ret = nc_server_tls_get_username_from_cert_wrap(cert, cb_data->ctn_data.matched_ctn_type[i], &session->username);
+                if (ret == -1) {
+                    goto cleanup;
+                }
+            }
+        }
+    }
+
+    if (server_opts.user_verify_clb && !server_opts.user_verify_clb(session)) {
+        VRB(session, "Cert verify: user verify callback revoked authorization.");
+        ret = -1;
+        goto cleanup;
+    }
+
+cleanup:
+    free(subject);
+    free(issuer);
+    return ret;
+}
+
+API const void *
+nc_session_get_client_cert(const struct nc_session *session)
+{
+    if (!session || (session->side != NC_SERVER)) {
+        ERRARG(session, "session");
+        return NULL;
+    }
+
+    return session->opts.server.client_cert;
+}
+
+API void
+nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *session))
+{
+    server_opts.user_verify_clb = verify_clb;
+}
+
+int
+nc_server_tls_load_server_cert_key(struct nc_server_tls_opts *opts, void **srv_cert, void **srv_pkey)
 {
     char *privkey_data = NULL, *cert_data = NULL;
-    int ret = 0;
     NC_PRIVKEY_FORMAT privkey_type;
-    X509 *cert = NULL;
-    EVP_PKEY *pkey = NULL;
-
-    NC_CHECK_ARG_RET(NULL, tls_ctx, opts, -1);
+    void *cert = NULL;
+    void *pkey = NULL;
 
     /* get data needed for setting the server cert */
     if (opts->store == NC_STORE_LOCAL) {
@@ -877,10 +584,9 @@
         privkey_type = opts->privkey_type;
     } else {
         /* keystore */
-        ret = nc_server_tls_ks_ref_get_cert_key(opts->key_ref, opts->cert_ref, &privkey_data, &privkey_type, &cert_data);
-        if (ret) {
+        if (nc_server_tls_ks_ref_get_cert_key(opts->key_ref, opts->cert_ref, &privkey_data, &privkey_type, &cert_data)) {
             ERR(NULL, "Getting server certificate from the keystore reference \"%s\" failed.", opts->key_ref);
-            return -1;
+            return 1;
         }
     }
     if (!cert_data || !privkey_data) {
@@ -889,143 +595,22 @@
         goto cleanup;
     }
 
-    /* load the cert */
-    cert = base64der_to_cert(cert_data);
+    cert = nc_base64der_to_cert(cert_data);
     if (!cert) {
-        ERR(NULL, "Converting certificate data to certificate format failed.");
-        ret = -1;
-        goto cleanup;
+        return 1;
     }
 
-    /* set server cert */
-    ret = SSL_CTX_use_certificate(tls_ctx, cert);
-    if (ret != 1) {
-        ERR(NULL, "Loading the server certificate failed (%s).", ERR_reason_error_string(ERR_get_error()));
-        ret = -1;
-        goto cleanup;
-    }
-
-    /* load the private key */
-    pkey = base64der_to_privatekey(privkey_data, nc_privkey_format_to_str(privkey_type));
+    pkey = nc_base64der_to_privatekey(privkey_data, nc_privkey_format_to_str(privkey_type));
     if (!pkey) {
-        ERR(NULL, "Converting private key data to private key format failed.");
-        ret = -1;
-        goto cleanup;
+        nc_tls_cert_destroy_wrap(cert);
+        return 1;
     }
 
-    /* set server key */
-    ret = SSL_CTX_use_PrivateKey(tls_ctx, pkey);
-    if (ret != 1) {
-        ERR(NULL, "Loading the server private key failed (%s).", ERR_reason_error_string(ERR_get_error()));
-        ret = -1;
-        goto cleanup;
-    }
-
-    ret = 0;
-
-cleanup:
-    X509_free(cert);
-    EVP_PKEY_free(pkey);
-    return ret;
-}
-
-static int
-tls_store_add_trusted_cert(X509_STORE *cert_store, const char *cert_data)
-{
-    X509 *cert;
-
-    cert = base64der_to_cert(cert_data);
-
-    if (!cert) {
-        ERR(NULL, "Loading a trusted certificate (data \"%s\") failed (%s).", cert_data,
-                ERR_reason_error_string(ERR_get_error()));
-        return -1;
-    }
-
-    /* add the trusted certificate */
-    if (X509_STORE_add_cert(cert_store, cert) != 1) {
-        ERR(NULL, "Adding a trusted certificate failed (%s).", ERR_reason_error_string(ERR_get_error()));
-        X509_free(cert);
-        return -1;
-    }
-    X509_free(cert);
-
+    *srv_cert = cert;
+    *srv_pkey = pkey;
     return 0;
 }
 
-static int
-nc_tls_store_set_trusted_certs(X509_STORE *cert_store, struct nc_cert_grouping *ca_certs)
-{
-    uint16_t i;
-    struct nc_certificate *certs;
-    uint16_t cert_count;
-
-    if (ca_certs->store == NC_STORE_LOCAL) {
-        /* local definition */
-        certs = ca_certs->certs;
-        cert_count = ca_certs->cert_count;
-    } else {
-        /* truststore */
-        if (nc_server_tls_ts_ref_get_certs(ca_certs->ts_ref, &certs, &cert_count)) {
-            ERR(NULL, "Error getting certificate-authority certificates from the truststore reference \"%s\".", ca_certs->ts_ref);
-            return -1;
-        }
-    }
-
-    for (i = 0; i < cert_count; i++) {
-        if (tls_store_add_trusted_cert(cert_store, certs[i].data)) {
-            return -1;
-        }
-    }
-
-    return 0;
-}
-
-static int
-nc_server_tls_crl_path(struct nc_session *session, const char *crl_path, X509_STORE *store)
-{
-    int ret = 0;
-    X509_CRL *crl = NULL;
-    FILE *f;
-
-    f = fopen(crl_path, "r");
-    if (!f) {
-        ERR(session, "Unable to open CRL file \"%s\".", crl_path);
-        return -1;
-    }
-
-    /* try DER first */
-    crl = d2i_X509_CRL_fp(f, NULL);
-    if (crl) {
-        /* success */
-        goto ok;
-    }
-
-    /* DER failed, try PEM */
-    rewind(f);
-    crl = PEM_read_X509_CRL(f, NULL, NULL, NULL);
-    if (!crl) {
-        ERR(session, "Reading CRL from file \"%s\" failed.", crl_path);
-        ret = -1;
-        goto cleanup;
-    }
-
-ok:
-    ret = X509_STORE_add_crl(store, crl);
-    if (!ret) {
-        ERR(session, "Error adding CRL to store (%s).", ERR_reason_error_string(ERR_get_error()));
-        ret = -1;
-        goto cleanup;
-    }
-    /* ok */
-    ret = 0;
-
-cleanup:
-    fclose(f);
-    X509_CRL_free(crl);
-    return ret;
-}
-
 static size_t
 nc_server_tls_curl_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
 {
@@ -1045,51 +630,25 @@
 }
 
 static int
-nc_server_tls_curl_init(struct nc_session *session, CURL **handle, struct nc_curl_data *data)
-{
-    NC_CHECK_ARG_RET(session, handle, data, -1);
-
-    *handle = NULL;
-
-    *handle = curl_easy_init();
-    if (!*handle) {
-        ERR(session, "Initializing CURL failed.");
-        return -1;
-    }
-
-    if (curl_easy_setopt(*handle, CURLOPT_WRITEFUNCTION, nc_server_tls_curl_cb)) {
-        ERR(session, "Setting curl callback failed.");
-        return -1;
-    }
-
-    if (curl_easy_setopt(*handle, CURLOPT_WRITEDATA, data)) {
-        ERR(session, "Setting curl callback data failed.");
-        return -1;
-    }
-
-    return 0;
-}
-
-static int
-nc_server_tls_curl_fetch(struct nc_session *session, CURL *handle, const char *url)
+nc_server_tls_curl_fetch(CURL *handle, const char *url)
 {
     char err_buf[CURL_ERROR_SIZE];
 
     /* set uri */
     if (curl_easy_setopt(handle, CURLOPT_URL, url)) {
-        ERR(session, "Setting URI \"%s\" to download CRL from failed.", url);
+        ERR(NULL, "Setting URI \"%s\" to download CRL from failed.", url);
         return -1;
     }
 
     /* set err buf */
     if (curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, err_buf)) {
-        ERR(session, "Setting CURL error buffer option failed.");
+        ERR(NULL, "Setting CURL error buffer option failed.");
         return -1;
     }
 
     /* download */
     if (curl_easy_perform(handle)) {
-        ERR(session, "Downloading CRL from \"%s\" failed (%s).", url, err_buf);
+        ERR(NULL, "Downloading CRL from \"%s\" failed (%s).", url, err_buf);
         return -1;
     }
 
@@ -1097,75 +656,99 @@
 }
 
 static int
-nc_server_tls_add_crl_to_store(struct nc_session *session, struct nc_curl_data *downloaded, X509_STORE *store)
+nc_server_tls_curl_init(CURL **handle, struct nc_curl_data *data)
+{
+    NC_CHECK_ARG_RET(NULL, handle, data, -1);
+
+    *handle = NULL;
+
+    *handle = curl_easy_init();
+    if (!*handle) {
+        ERR(NULL, "Initializing CURL failed.");
+        return -1;
+    }
+
+    if (curl_easy_setopt(*handle, CURLOPT_WRITEFUNCTION, nc_server_tls_curl_cb)) {
+        ERR(NULL, "Setting curl callback failed.");
+        return -1;
+    }
+
+    if (curl_easy_setopt(*handle, CURLOPT_WRITEDATA, data)) {
+        ERR(NULL, "Setting curl callback data failed.");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int
+nc_server_tls_crl_cert_ext(void *cert_store, void *crl_store)
 {
     int ret = 0;
-    X509_CRL *crl = NULL;
-    BIO *bio = NULL;
+    CURL *handle = NULL;
+    struct nc_curl_data downloaded = {0};
+    char **uris = NULL;
+    int uri_count = 0, i;
 
-    /* try DER first */
-    crl = d2i_X509_CRL(NULL, (const unsigned char **) &downloaded->data, downloaded->size);
-    if (crl) {
-        /* it was DER */
-        goto ok;
-    }
-
-    /* DER failed, try PEM next */
-    bio = BIO_new_mem_buf(downloaded->data, downloaded->size);
-    if (!bio) {
-        ERR(session, "Creating new bio failed (%s).", ERR_reason_error_string(ERR_get_error()));
-        ret = -1;
+    /* init curl */
+    ret = nc_server_tls_curl_init(&handle, &downloaded);
+    if (ret) {
         goto cleanup;
     }
 
-    /* try to parse PEM from the downloaded data */
-    crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL);
-    if (!crl) {
-        ERR(session, "Reading downloaded CRL failed (%s).", ERR_reason_error_string(ERR_get_error()));
-        ret = -1;
+    ret = nc_server_tls_get_crl_distpoint_uris_wrap(cert_store, &uris, &uri_count);
+    if (ret) {
         goto cleanup;
     }
 
-ok:
-    /* we obtained the CRL, now add it to the CRL store */
-    ret = X509_STORE_add_crl(store, crl);
-    if (!ret) {
-        ERR(session, "Error adding CRL to store (%s).", ERR_reason_error_string(ERR_get_error()));
-        ret = -1;
-        goto cleanup;
+    for (i = 0; i < uri_count; i++) {
+        VRB(NULL, "Downloading CRL from \"%s\".", uris[i]);
+        ret = nc_server_tls_curl_fetch(handle, uris[i]);
+        if (ret) {
+            /* failed to download the CRL from this entry, try the next entry */
+            WRN(NULL, "Failed to fetch CRL from \"%s\".", uris[i]);
+            continue;
+        }
+
+        /* convert the downloaded data to CRL and add it to the store */
+        ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, cert_store, crl_store);
+        if (ret) {
+            goto cleanup;
+        }
     }
-    /* ok */
-    ret = 0;
 
 cleanup:
-    X509_CRL_free(crl);
-    BIO_free(bio);
+    for (i = 0; i < uri_count; i++) {
+        free(uris[i]);
+    }
+    free(uris);
+    curl_easy_cleanup(handle);
     return ret;
 }
 
 static int
-nc_server_tls_crl_url(struct nc_session *session, const char *url, X509_STORE *store)
+nc_server_tls_crl_url(const char *url, void *cert_store, void *crl_store)
 {
     int ret = 0;
     CURL *handle = NULL;
     struct nc_curl_data downloaded = {0};
 
     /* init curl */
-    ret = nc_server_tls_curl_init(session, &handle, &downloaded);
+    ret = nc_server_tls_curl_init(&handle, &downloaded);
     if (ret) {
         goto cleanup;
     }
 
-    VRB(session, "Downloading CRL from \"%s\".", url);
+    VRB(NULL, "Downloading CRL from \"%s\".", url);
 
     /* download the CRL */
-    ret = nc_server_tls_curl_fetch(session, handle, url);
+    ret = nc_server_tls_curl_fetch(handle, url);
     if (ret) {
         goto cleanup;
     }
 
     /* convert the downloaded data to CRL and add it to the store */
-    ret = nc_server_tls_add_crl_to_store(session, &downloaded, store);
+    ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, cert_store, crl_store);
     if (ret) {
         goto cleanup;
     }
@@ -1175,154 +758,66 @@
     return ret;
 }
 
-static int
-nc_server_tls_crl_cert_ext(struct nc_session *session, X509_STORE *cert_store, X509_STORE *crl_store)
+int
+nc_server_tls_load_crl(struct nc_server_tls_opts *opts, void *cert_store, void *crl_store)
 {
-    int ret = 0, i, j, k, gtype;
-    CURL *handle = NULL;
-    struct nc_curl_data downloaded = {0};
-
-    STACK_OF(X509_OBJECT) * objs;
-    X509_OBJECT *obj;
-    X509 *cert;
-
-    STACK_OF(DIST_POINT) * dist_points;
-    DIST_POINT *dist_point;
-    GENERAL_NAMES *general_names;
-    GENERAL_NAME *general_name;
-    ASN1_STRING *asn_string_uri;
-    const char *crl_distpoint_uri;
-
-    /* init curl */
-    ret = nc_server_tls_curl_init(session, &handle, &downloaded);
-    if (ret) {
-        goto cleanup;
-    }
-
-    /* treat all entries in the cert_store as X509_OBJECTs */
-    objs = X509_STORE_get0_objects(cert_store);
-    if (!objs) {
-        ERR(session, "Getting certificates from store failed (%s).", ERR_reason_error_string(ERR_get_error()));
-        ret = -1;
-        goto cleanup;
-    }
-
-    /* iterate over all the CAs */
-    for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
-        obj = sk_X509_OBJECT_value(objs, i);
-        cert = X509_OBJECT_get0_X509(obj);
-        if (!cert) {
-            /* the object on this index was not a certificate */
-            continue;
-        }
-
-        /* get all the distribution points for this CA */
-        dist_points = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL);
-
-        /* iterate over all the dist points (there can be multiple for a single cert) */
-        for (j = 0; j < sk_DIST_POINT_num(dist_points); j++) {
-            dist_point = sk_DIST_POINT_value(dist_points, j);
-            if (!dist_point) {
-                continue;
-            }
-            general_names = dist_point->distpoint->name.fullname;
-
-            /* iterate over all the GeneralesNames in the distribution point */
-            for (k = 0; k < sk_GENERAL_NAME_num(general_names); k++) {
-                general_name = sk_GENERAL_NAME_value(general_names, k);
-                asn_string_uri = GENERAL_NAME_get0_value(general_name, &gtype);
-
-                /* check if the general name is a URI and has a valid length */
-                if ((gtype != GEN_URI) || (ASN1_STRING_length(asn_string_uri) <= 6)) {
-                    continue;
-                }
-
-                crl_distpoint_uri = (const char *) ASN1_STRING_get0_data(asn_string_uri);
-
-                VRB(session, "Downloading CRL from \"%s\".", crl_distpoint_uri);
-
-                /* download the CRL */
-                ret = nc_server_tls_curl_fetch(session, handle, crl_distpoint_uri);
-                if (ret) {
-                    /* failed to download the CRL from this entry, try th next */
-                    continue;
-                }
-
-                /* convert the downloaded data to CRL and add it to the store */
-                ret = nc_server_tls_add_crl_to_store(session, &downloaded, crl_store);
-                if (ret) {
-                    goto cleanup;
-                }
-
-                /* the CRL was downloaded, no need to download it again using different protocol */
-                break;
-            }
-        }
-    }
-
-cleanup:
-    curl_easy_cleanup(handle);
-    return ret;
-}
-
-static int
-nc_tls_store_set_crl(struct nc_session *session, struct nc_server_tls_opts *opts, X509_STORE *store)
-{
-    if (!opts->crl_store) {
-        /* first call on this endpoint */
-        opts->crl_store = X509_STORE_new();
-        NC_CHECK_ERRMEM_GOTO(!opts->crl_store, , fail);
-    }
-
     if (opts->crl_path) {
-        if (nc_server_tls_crl_path(session, opts->crl_path, opts->crl_store)) {
-            goto fail;
+        if (nc_server_tls_crl_path_wrap(opts->crl_path, cert_store, crl_store)) {
+            return 1;
         }
     } else if (opts->crl_url) {
-        if (nc_server_tls_crl_url(session, opts->crl_url, opts->crl_store)) {
-            goto fail;
+        if (nc_server_tls_crl_url(opts->crl_url, cert_store, crl_store)) {
+            return 1;
         }
     } else {
-        if (nc_server_tls_crl_cert_ext(session, store, opts->crl_store)) {
-            goto fail;
+        if (nc_server_tls_crl_cert_ext(cert_store, crl_store)) {
+            return 1;
         }
     }
 
     return 0;
+}
 
-fail:
-    return -1;
+int
+nc_server_tls_load_trusted_certs(struct nc_cert_grouping *ca_certs, void *cert_store)
+{
+    struct nc_certificate *certs;
+    uint16_t i, cert_count;
+
+    if (ca_certs->store == NC_STORE_LOCAL) {
+        /* local definition */
+        certs = ca_certs->certs;
+        cert_count = ca_certs->cert_count;
+    } else {
+        /* truststore */
+        if (nc_server_tls_ts_ref_get_certs(ca_certs->ts_ref, &certs, &cert_count)) {
+            ERR(NULL, "Error getting certificate-authority certificates from the truststore reference \"%s\".", ca_certs->ts_ref);
+            return 1;
+        }
+    }
+
+    for (i = 0; i < cert_count; i++) {
+        if (nc_base64der_to_cert_add_to_store(certs[i].data, cert_store)) {
+            return 1;
+        }
+    }
+
+    return 0;
 }
 
 static int
-nc_server_tls_accept_check(int accept_ret, struct nc_session *session)
+nc_server_tls_accept_check(int accept_ret, void *tls_session)
 {
-    int verify;
+    uint32_t verify;
 
     /* check certificate verification result */
-    verify = SSL_get_verify_result(session->ti.tls);
-    switch (verify) {
-    case X509_V_OK:
-        if (accept_ret == 1) {
-            VRB(session, "Client certificate verified.");
-        }
-        break;
-    default:
-        ERR(session, "Client certificate error (%s).", X509_verify_cert_error_string(verify));
+    verify = nc_tls_get_verify_result_wrap(tls_session);
+    if (!verify && accept_ret == 1) {
+        VRB(NULL, "Client certificate verified.");
     }
 
     if (accept_ret != 1) {
-        switch (SSL_get_error(session->ti.tls, accept_ret)) {
-        case SSL_ERROR_SYSCALL:
-            ERR(session, "SSL accept failed (%s).", strerror(errno));
-            break;
-        case SSL_ERROR_SSL:
-            ERR(session, "SSL accept failed (%s).", ERR_reason_error_string(ERR_get_error()));
-            break;
-        default:
-            ERR(session, "SSL accept failed.");
-            break;
-        }
+        nc_server_tls_print_accept_error_wrap(accept_ret, tls_session);
     }
 
     return accept_ret;
@@ -1331,142 +826,140 @@
 int
 nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opts, int sock, int timeout)
 {
-    X509_STORE *cert_store;
-    SSL_CTX *tls_ctx;
-    int ret;
+    int rc, timeouted = 0;
     struct timespec ts_timeout;
-    struct nc_endpt *referenced_endpt = NULL;
+    struct nc_tls_verify_cb_data cb_data = {0};
+    struct nc_endpt *referenced_endpt;
+    void *tls_cfg, *srv_cert, *srv_pkey, *cert_store, *crl_store;
 
-    /* SSL_CTX */
-    tls_ctx = SSL_CTX_new(TLS_server_method());
+    tls_cfg = srv_cert = srv_pkey = cert_store = crl_store = NULL;
 
-    if (!tls_ctx) {
-        ERR(session, "Failed to create TLS context.");
-        goto error;
-    }
-
-    SSL_CTX_set_verify(tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_tlsclb_verify);
-    if (nc_tls_ctx_set_server_cert_key(tls_ctx, opts)) {
-        goto error;
-    }
-
-    /* X509_STORE, managed (freed) with the context */
-    cert_store = X509_STORE_new();
-    if (!cert_store) {
-        ERR(session, "Creating certificate store failed (%s).", ERR_reason_error_string(ERR_get_error()));
-        goto error;
-    }
-
-    /* store the session, retrieve it when needed */
-    ret = X509_STORE_set_ex_data(cert_store, 0, session);
-    if (!ret) {
-        ERR(session, "Setting certificate store data failed (%s).", ERR_reason_error_string(ERR_get_error()));
-        goto error;
-    }
-
-    /* set end-entity certs as cert store data, retrieve them if verification fails later */
-    ret = X509_STORE_set_ex_data(cert_store, 1, &opts->ee_certs);
-    if (!ret) {
-        ERR(session, "Setting certificate store data failed (%s).", ERR_reason_error_string(ERR_get_error()));
-        goto error;
-    }
-
-    /* do the same for referenced endpoint's end entity certs */
+    /* set verify cb data */
+    cb_data.session = session;
+    cb_data.opts = opts;
+    cb_data.ee_certs = &opts->ee_certs;
     if (opts->referenced_endpt_name) {
         if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) {
             ERRINT;
-            goto error;
+            return 1;
         }
-
-        ret = X509_STORE_set_ex_data(cert_store, 2, &referenced_endpt->opts.tls->ee_certs);
-        if (!ret) {
-            ERR(session, "Setting certificate store data failed (%s).", ERR_reason_error_string(ERR_get_error()));
-            goto error;
-        }
+        cb_data.referenced_ee_certs = &referenced_endpt->opts.tls->ee_certs;
     }
 
-    /* set store to the context */
-    SSL_CTX_set_cert_store(tls_ctx, cert_store);
-
-    /* set certificate authority certs */
-    if (nc_tls_store_set_trusted_certs(cert_store, &opts->ca_certs)) {
-        goto error;
+    /* prepare TLS context from which a session will be created */
+    tls_cfg = nc_server_tls_config_new_wrap();
+    if (!tls_cfg) {
+        goto fail;
     }
 
-    /* set referenced endpoint's CA certs if set */
+    /* opaque CA/CRL certificate store */
+    cert_store = nc_tls_cert_store_new_wrap();
+    if (!cert_store) {
+        goto fail;
+    }
+
+    /* load server's key and certificate */
+    if (nc_server_tls_load_server_cert_key(opts, &srv_cert, &srv_pkey)) {
+        ERR(session, "Loading server certificate and/or private key failed.");
+        goto fail;
+    }
+
+    /* load trusted CA certificates */
+    if (nc_server_tls_load_trusted_certs(&opts->ca_certs, cert_store)) {
+        ERR(session, "Loading server CA certs failed.");
+        goto fail;
+    }
+
+    /* load referenced endpoint's trusted CA certs if set */
     if (opts->referenced_endpt_name) {
-        if (nc_tls_store_set_trusted_certs(cert_store, &referenced_endpt->opts.tls->ca_certs)) {
-            goto error;
+        if (nc_server_tls_load_trusted_certs(&referenced_endpt->opts.tls->ca_certs, cert_store)) {
+            ERR(session, "Loading server CA certs from referenced endpoint failed.");
+            goto fail;
         }
     }
 
-    /* set Certificate Revocation List if configured */
     if (opts->crl_path || opts->crl_url || opts->crl_cert_ext) {
-        if (nc_tls_store_set_crl(session, opts, cert_store)) {
-            goto error;
+        /* opaque CRL store */
+        crl_store = nc_tls_crl_store_new_wrap();
+        if (!crl_store) {
+            goto fail;
+        }
+
+        /* load CRLs into one of the stores */
+        if (nc_server_tls_load_crl(opts, cert_store, crl_store)) {
+            ERR(session, "Loading server CRL failed.");
+            goto fail;
         }
     }
 
-    session->ti_type = NC_TI_OPENSSL;
-    session->ti.tls = SSL_new(tls_ctx);
-
-    /* context can be freed already, trusted certs must be freed manually */
-    SSL_CTX_free(tls_ctx);
-    tls_ctx = NULL;
-
-    if (!session->ti.tls) {
-        ERR(session, "Failed to create TLS structure from context.");
-        goto error;
-    }
-
-    /* set TLS versions for the current SSL session */
+    /* set supported TLS versions */
     if (opts->tls_versions) {
-        if (!(opts->tls_versions & NC_TLS_VERSION_10)) {
-            SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1);
-        }
-        if (!(opts->tls_versions & NC_TLS_VERSION_11)) {
-            SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_1);
-        }
-        if (!(opts->tls_versions & NC_TLS_VERSION_12)) {
-            SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_2);
-        }
-        if (!(opts->tls_versions & NC_TLS_VERSION_13)) {
-            SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_3);
+        if (nc_server_tls_set_tls_versions_wrap(tls_cfg, opts->tls_versions)) {
+            ERR(session, "Setting supported server TLS versions failed.");
+            goto fail;
         }
     }
 
-    /* set TLS cipher suites */
-    if (opts->ciphers) {
-        /* set for TLS1.2 and lower */
-        SSL_set_cipher_list(session->ti.tls, opts->ciphers);
-        /* set for TLS1.3 */
-        SSL_set_ciphersuites(session->ti.tls, opts->ciphers);
+    /* init TLS context and store data which may be needed later in it */
+    if (nc_tls_init_ctx_wrap(&session->ti.tls.ctx, sock, srv_cert, srv_pkey, cert_store, crl_store)) {
+        goto fail;
     }
 
-    SSL_set_fd(session->ti.tls, sock);
+    /* memory is managed by context now */
+    srv_cert = srv_pkey = cert_store = crl_store = NULL;
+
+    /* setup config from ctx */
+    if (nc_tls_setup_config_wrap(tls_cfg, NC_SERVER, &session->ti.tls.ctx)) {
+        goto fail;
+    } // TODO free openssl shit
+
+    /* fill session data and create TLS session from config */
+    session->ti_type = NC_TI_OPENSSL; // TODO: prejmenovat
+    if (!(session->ti.tls.session = nc_tls_session_new_wrap(tls_cfg))) {
+        goto fail;
+    }
+    session->ti.tls.config = tls_cfg;
+
+    /* set verify callback and its data */
+    nc_server_tls_set_verify_cb_wrap(session->ti.tls.session, &cb_data);
+
+    /* set session fd */
+    nc_server_tls_set_fd_wrap(session->ti.tls.session, sock, &session->ti.tls.ctx);
+
+    /* TODO: ciphers */
+
     sock = -1;
-    SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
 
+    /* do the handshake */
     if (timeout > -1) {
         nc_timeouttime_get(&ts_timeout, timeout);
     }
-    while (((ret = SSL_accept(session->ti.tls)) == -1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) {
+    while ((rc = nc_server_tls_handshake_step_wrap(session->ti.tls.session)) == 0) {
         usleep(NC_TIMEOUT_STEP);
         if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
-            ERR(session, "SSL accept timeout.");
-            return 0;
+            ERR(session, "TLS accept timeout.");
+            timeouted = 1;
+            goto fail;
         }
     }
-    if (nc_server_tls_accept_check(ret, session) != 1) {
-        return -1;
+
+    /* check if handshake was ok */
+    if (nc_server_tls_accept_check(rc, session->ti.tls.session) != 1) {
+        goto fail;
     }
 
     return 1;
 
-error:
+fail:
     if (sock > -1) {
         close(sock);
     }
-    SSL_CTX_free(tls_ctx);
-    return -1;
+
+    nc_tls_session_new_cleanup_wrap(tls_cfg, srv_cert, srv_pkey, cert_store, crl_store);
+
+    if (timeouted) {
+        return 0;
+    } else {
+        return -1;
+    }
 }