1/* Copyright (c) 2017 - 2022 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    {
97        const char *const s = getenv("LSQUIC_ENABLE_EARLY_DATA");
98        if (!s || atoi(s))
99            SSL_CTX_set_early_data_enabled(cert->ce_ssl_ctx, 1);    /* XXX */
100    }
101    if (1 != SSL_CTX_use_certificate_chain_file(cert->ce_ssl_ctx, cert_file))
102    {
103        LSQ_ERROR("SSL_CTX_use_certificate_chain_file failed: %s", cert_file);
104        goto end;
105    }
106
107    if (strstr(key_file, ".pkcs8"))
108    {
109        f = fopen(key_file, "r");
110        if (!f)
111        {
112            LSQ_ERROR("fopen(%s) failed: %s", cert_file, strerror(errno));
113            goto end;
114        }
115        pkey = d2i_PrivateKey_fp(f, NULL);
116        fclose(f);
117        f = NULL;
118        if (!pkey)
119        {
120            LSQ_ERROR("Reading private key from %s failed", key_file);
121            goto end;
122        }
123        if (!SSL_CTX_use_PrivateKey(cert->ce_ssl_ctx, pkey))
124        {
125            LSQ_ERROR("SSL_CTX_use_PrivateKey failed");
126            goto end;
127        }
128    }
129    else if (1 != SSL_CTX_use_PrivateKey_file(cert->ce_ssl_ctx, key_file,
130                                                            SSL_FILETYPE_PEM))
131    {
132        LSQ_ERROR("SSL_CTX_use_PrivateKey_file failed");
133        goto end;
134    }
135
136    const int was = SSL_CTX_set_session_cache_mode(cert->ce_ssl_ctx, 1);
137    LSQ_DEBUG("set SSL session cache mode to 1 (was: %d)", was);
138
139    if (lsquic_hash_insert(certs, cert->ce_sni, strlen(cert->ce_sni), cert,
140                                                            &cert->ce_hash_el))
141        rv = 0;
142    else
143        LSQ_WARN("cannot insert cert for %s into hash table", cert->ce_sni);
144
145  end:
146    free(sni);
147    if (rv != 0)
148    {   /* Error: free cert and its components */
149        if (cert)
150        {
151            free(cert->ce_sni);
152            free(cert);
153        }
154    }
155    return rv;
156}
157
158struct ssl_ctx_st *
159lookup_cert (void *cert_lu_ctx, const struct sockaddr *sa_UNUSED,
160             const char *sni)
161{
162    struct lsquic_hash_elem *el;
163    struct server_cert *server_cert;
164
165    if (!cert_lu_ctx)
166        return NULL;
167
168    if (sni)
169        el = lsquic_hash_find(cert_lu_ctx, sni, strlen(sni));
170    else
171    {
172        LSQ_INFO("SNI is not set");
173        el = lsquic_hash_first(cert_lu_ctx);
174    }
175
176    if (el)
177    {
178        server_cert = lsquic_hashelem_getdata(el);
179        if (server_cert)
180            return server_cert->ce_ssl_ctx;
181    }
182
183    return NULL;
184}
185
186
187void
188delete_certs (struct lsquic_hash *certs)
189{
190    struct lsquic_hash_elem *el;
191    struct server_cert *cert;
192
193    for (el = lsquic_hash_first(certs); el; el = lsquic_hash_next(certs))
194    {
195        cert = lsquic_hashelem_getdata(el);
196        SSL_CTX_free(cert->ce_ssl_ctx);
197        free(cert->ce_sni);
198        free(cert);
199    }
200    lsquic_hash_destroy(certs);
201}
202