test_cert.c revision fb73393f
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
2#include <errno.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <sys/queue.h>
7
8#include <openssl/pem.h>
9#include <openssl/x509.h>
10#include <openssl/ssl.h>
11
12#include "lsquic_types.h"
13#include "lsquic.h"
14#include "../src/liblsquic/lsquic_logger.h"
15#include "../src/liblsquic/lsquic_hash.h"
16
17#include "test_cert.h"
18
19
20static int
21select_alpn (SSL *ssl, const unsigned char **out, unsigned char *outlen,
22                    const unsigned char *in, unsigned int inlen, void *arg)
23{
24    const unsigned char alpn[] = "\x5h3-27\x5h3-28";
25    int r;
26
27    r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen,
28                                                            alpn, sizeof(alpn));
29    if (r == OPENSSL_NPN_NEGOTIATED)
30        return SSL_TLSEXT_ERR_OK;
31    else
32    {
33        LSQ_WARN("no supported protocol can be selected from %.*s",
34                                                    (int) inlen, (char *) in);
35        return SSL_TLSEXT_ERR_ALERT_FATAL;
36    }
37}
38
39
40
41int
42load_cert (struct lsquic_hash *certs, const char *optarg)
43{
44    int rv = -1;
45    char *sni, *cert_file, *key_file;
46    struct server_cert *cert = NULL;
47    EVP_PKEY *pkey = NULL;
48    FILE *f = NULL;
49
50    sni = strdup(optarg);
51    cert_file = strchr(sni, ',');
52    if (!cert_file)
53        goto end;
54    *cert_file = '\0';
55    ++cert_file;
56    key_file = strchr(cert_file, ',');
57    if (!key_file)
58        goto end;
59    *key_file = '\0';
60    ++key_file;
61
62    cert = calloc(1, sizeof(*cert));
63    cert->ce_sni = strdup(sni);
64
65    cert->ce_ssl_ctx = SSL_CTX_new(TLS_method());
66    if (!cert->ce_ssl_ctx)
67    {
68        LSQ_ERROR("SSL_CTX_new failed");
69        goto end;
70    }
71    SSL_CTX_set_min_proto_version(cert->ce_ssl_ctx, TLS1_3_VERSION);
72    SSL_CTX_set_max_proto_version(cert->ce_ssl_ctx, TLS1_3_VERSION);
73    SSL_CTX_set_default_verify_paths(cert->ce_ssl_ctx);
74    SSL_CTX_set_alpn_select_cb(cert->ce_ssl_ctx, select_alpn, NULL);
75    SSL_CTX_set_early_data_enabled(cert->ce_ssl_ctx, 1);    /* XXX */
76    if (1 != SSL_CTX_use_certificate_chain_file(cert->ce_ssl_ctx, cert_file))
77    {
78        LSQ_ERROR("SSL_CTX_use_certificate_chain_file failed: %s", cert_file);
79        goto end;
80    }
81
82    if (strstr(key_file, ".pkcs8"))
83    {
84        f = fopen(key_file, "r");
85        if (!f)
86        {
87            LSQ_ERROR("fopen(%s) failed: %s", cert_file, strerror(errno));
88            goto end;
89        }
90        pkey = d2i_PrivateKey_fp(f, NULL);
91        fclose(f);
92        f = NULL;
93        if (!pkey)
94        {
95            LSQ_ERROR("Reading private key from %s failed", key_file);
96            goto end;
97        }
98        if (!SSL_CTX_use_PrivateKey(cert->ce_ssl_ctx, pkey))
99        {
100            LSQ_ERROR("SSL_CTX_use_PrivateKey failed");
101            goto end;
102        }
103    }
104    else if (1 != SSL_CTX_use_PrivateKey_file(cert->ce_ssl_ctx, key_file,
105                                                            SSL_FILETYPE_PEM))
106    {
107        LSQ_ERROR("SSL_CTX_use_PrivateKey_file failed");
108        goto end;
109    }
110
111    const int was = SSL_CTX_set_session_cache_mode(cert->ce_ssl_ctx, 1);
112    LSQ_DEBUG("set SSL session cache mode to 1 (was: %d)", was);
113
114    if (lsquic_hash_insert(certs, cert->ce_sni, strlen(cert->ce_sni), cert,
115                                                            &cert->ce_hash_el))
116        rv = 0;
117    else
118        LSQ_WARN("cannot insert cert for %s into hash table", cert->ce_sni);
119
120  end:
121    free(sni);
122    if (rv != 0)
123    {   /* Error: free cert and its components */
124        if (cert)
125        {
126            free(cert->ce_sni);
127            free(cert);
128        }
129    }
130    return rv;
131}
132
133struct ssl_ctx_st *
134lookup_cert (void *cert_lu_ctx, const struct sockaddr *sa_UNUSED,
135             const char *sni)
136{
137    struct lsquic_hash_elem *el;
138    struct server_cert *server_cert;
139
140    if (!cert_lu_ctx)
141        return NULL;
142
143    if (sni)
144        el = lsquic_hash_find(cert_lu_ctx, sni, strlen(sni));
145    else
146    {
147        LSQ_INFO("SNI is not set");
148        el = lsquic_hash_first(cert_lu_ctx);
149    }
150
151    if (el)
152    {
153        server_cert = lsquic_hashelem_getdata(el);
154        if (server_cert)
155            return server_cert->ce_ssl_ctx;
156    }
157
158    return NULL;
159}
160
161
162void
163delete_certs (struct lsquic_hash *certs)
164{
165    struct lsquic_hash_elem *el;
166    struct server_cert *cert;
167
168    for (el = lsquic_hash_first(certs); el; el = lsquic_hash_next(certs))
169    {
170        cert = lsquic_hashelem_getdata(el);
171        SSL_CTX_free(cert->ce_ssl_ctx);
172        free(cert->ce_sni);
173        free(cert);
174    }
175    lsquic_hash_destroy(certs);
176}
177