test_cert.c revision b1a7c3f9
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 char s_alpn[0x100];
21
22int
23add_alpn (const char *alpn)
24{
25    size_t alpn_len, all_len;
26
27    alpn_len = strlen(alpn);
28    if (alpn_len > 255)
29        return -1;
30
31    all_len = strlen(s_alpn);
32    if (all_len + 1 + alpn_len + 1 > sizeof(s_alpn))
33        return -1;
34
35    s_alpn[all_len] = alpn_len;
36    memcpy(&s_alpn[all_len + 1], alpn, alpn_len);
37    s_alpn[all_len + 1 + alpn_len] = '\0';
38    return 0;
39}
40
41
42static int
43select_alpn (SSL *ssl, const unsigned char **out, unsigned char *outlen,
44                    const unsigned char *in, unsigned int inlen, void *arg)
45{
46    int r;
47
48    r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen,
49                                    (unsigned char *) s_alpn, strlen(s_alpn));
50    if (r == OPENSSL_NPN_NEGOTIATED)
51        return SSL_TLSEXT_ERR_OK;
52    else
53    {
54        LSQ_WARN("no supported protocol can be selected from %.*s",
55                                                    (int) inlen, (char *) in);
56        return SSL_TLSEXT_ERR_ALERT_FATAL;
57    }
58}
59
60
61
62int
63load_cert (struct lsquic_hash *certs, const char *optarg)
64{
65    int rv = -1;
66    char *sni, *cert_file, *key_file;
67    struct server_cert *cert = NULL;
68    EVP_PKEY *pkey = NULL;
69    FILE *f = NULL;
70
71    sni = strdup(optarg);
72    cert_file = strchr(sni, ',');
73    if (!cert_file)
74        goto end;
75    *cert_file = '\0';
76    ++cert_file;
77    key_file = strchr(cert_file, ',');
78    if (!key_file)
79        goto end;
80    *key_file = '\0';
81    ++key_file;
82
83    cert = calloc(1, sizeof(*cert));
84    cert->ce_sni = strdup(sni);
85
86    cert->ce_ssl_ctx = SSL_CTX_new(TLS_method());
87    if (!cert->ce_ssl_ctx)
88    {
89        LSQ_ERROR("SSL_CTX_new failed");
90        goto end;
91    }
92    SSL_CTX_set_min_proto_version(cert->ce_ssl_ctx, TLS1_3_VERSION);
93    SSL_CTX_set_max_proto_version(cert->ce_ssl_ctx, TLS1_3_VERSION);
94    SSL_CTX_set_default_verify_paths(cert->ce_ssl_ctx);
95    SSL_CTX_set_alpn_select_cb(cert->ce_ssl_ctx, select_alpn, NULL);
96    SSL_CTX_set_early_data_enabled(cert->ce_ssl_ctx, 1);    /* XXX */
97    if (1 != SSL_CTX_use_certificate_chain_file(cert->ce_ssl_ctx, cert_file))
98    {
99        LSQ_ERROR("SSL_CTX_use_certificate_chain_file failed: %s", cert_file);
100        goto end;
101    }
102
103    if (strstr(key_file, ".pkcs8"))
104    {
105        f = fopen(key_file, "r");
106        if (!f)
107        {
108            LSQ_ERROR("fopen(%s) failed: %s", cert_file, strerror(errno));
109            goto end;
110        }
111        pkey = d2i_PrivateKey_fp(f, NULL);
112        fclose(f);
113        f = NULL;
114        if (!pkey)
115        {
116            LSQ_ERROR("Reading private key from %s failed", key_file);
117            goto end;
118        }
119        if (!SSL_CTX_use_PrivateKey(cert->ce_ssl_ctx, pkey))
120        {
121            LSQ_ERROR("SSL_CTX_use_PrivateKey failed");
122            goto end;
123        }
124    }
125    else if (1 != SSL_CTX_use_PrivateKey_file(cert->ce_ssl_ctx, key_file,
126                                                            SSL_FILETYPE_PEM))
127    {
128        LSQ_ERROR("SSL_CTX_use_PrivateKey_file failed");
129        goto end;
130    }
131
132    const int was = SSL_CTX_set_session_cache_mode(cert->ce_ssl_ctx, 1);
133    LSQ_DEBUG("set SSL session cache mode to 1 (was: %d)", was);
134
135    if (lsquic_hash_insert(certs, cert->ce_sni, strlen(cert->ce_sni), cert,
136                                                            &cert->ce_hash_el))
137        rv = 0;
138    else
139        LSQ_WARN("cannot insert cert for %s into hash table", cert->ce_sni);
140
141  end:
142    free(sni);
143    if (rv != 0)
144    {   /* Error: free cert and its components */
145        if (cert)
146        {
147            free(cert->ce_sni);
148            free(cert);
149        }
150    }
151    return rv;
152}
153
154struct ssl_ctx_st *
155lookup_cert (void *cert_lu_ctx, const struct sockaddr *sa_UNUSED,
156             const char *sni)
157{
158    struct lsquic_hash_elem *el;
159    struct server_cert *server_cert;
160
161    if (!cert_lu_ctx)
162        return NULL;
163
164    if (sni)
165        el = lsquic_hash_find(cert_lu_ctx, sni, strlen(sni));
166    else
167    {
168        LSQ_INFO("SNI is not set");
169        el = lsquic_hash_first(cert_lu_ctx);
170    }
171
172    if (el)
173    {
174        server_cert = lsquic_hashelem_getdata(el);
175        if (server_cert)
176            return server_cert->ce_ssl_ctx;
177    }
178
179    return NULL;
180}
181
182
183void
184delete_certs (struct lsquic_hash *certs)
185{
186    struct lsquic_hash_elem *el;
187    struct server_cert *cert;
188
189    for (el = lsquic_hash_first(certs); el; el = lsquic_hash_next(certs))
190    {
191        cert = lsquic_hashelem_getdata(el);
192        SSL_CTX_free(cert->ce_ssl_ctx);
193        free(cert->ce_sni);
194        free(cert);
195    }
196    lsquic_hash_destroy(certs);
197}
198