ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
tls.c
Go to the documentation of this file.
1#include "tls.h"
2
3#include <ascii-chat/common.h>
5#include <ngtcp2/ngtcp2_crypto_ossl.h>
6#include <openssl/err.h>
7#include <openssl/x509v3.h>
8#include <string.h>
9
10static asciichat_error_t tls_error(const char *operation) {
11 char message[256] = {0};
12 unsigned long code = ERR_get_error();
13 if (code)
14 ERR_error_string_n(code, message, sizeof(message));
15 return SET_ERRNO(ERROR_CRYPTO, "%s: %s", operation, code ? message : "OpenSSL rejected the operation");
16}
17
18static int select_alpn(SSL *ssl, const unsigned char **out, unsigned char *out_length, const unsigned char *offered,
19 unsigned int offered_length, void *arg) {
20 (void)ssl;
21 quic_tls_t *tls = arg;
22 unsigned char *selected = NULL;
23 unsigned char selected_length = 0;
24 if (SSL_select_next_proto(&selected, &selected_length, tls->alpn, tls->alpn_length, offered, offered_length) !=
25 OPENSSL_NPN_NEGOTIATED)
26 return SSL_TLSEXT_ERR_ALERT_FATAL;
27 *out = selected;
28 *out_length = selected_length;
29 return SSL_TLSEXT_ERR_OK;
30}
31
32static asciichat_error_t create_context(bool server, const char *alpn, quic_tls_t **out) {
33 if (!out)
34 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing QUIC TLS context or ALPN");
35 *out = NULL;
36 if (!alpn)
37 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing QUIC TLS context or ALPN");
38 size_t length = strnlen(alpn, sizeof(((quic_tls_t *)0)->alpn));
39 if (!length || length > 255)
40 return SET_ERRNO(ERROR_INVALID_PARAM, "QUIC ALPN must contain 1 to 255 bytes");
41
42 quic_tls_t *tls = SAFE_CALLOC(1, sizeof(*tls), quic_tls_t *);
43 if (!tls)
44 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate QUIC TLS context");
45 tls->server = server;
46 tls->alpn[0] = (unsigned char)length;
47 memcpy(tls->alpn + 1, alpn, length);
48 tls->alpn_length = (unsigned int)length + 1;
49 ERR_clear_error();
50 tls->ssl_ctx = SSL_CTX_new(server ? TLS_server_method() : TLS_client_method());
51 if (!tls->ssl_ctx || SSL_CTX_set_min_proto_version(tls->ssl_ctx, TLS1_3_VERSION) != 1 ||
52 SSL_CTX_set_max_proto_version(tls->ssl_ctx, TLS1_3_VERSION) != 1 ||
53 SSL_CTX_set_max_early_data(tls->ssl_ctx, 0) != 1) {
54 asciichat_error_t result = tls_error("Cannot configure QUIC TLS 1.3");
55 quic_tls_destroy(&tls);
56 return result;
57 }
58 SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_NO_COMPRESSION | SSL_OP_NO_RENEGOTIATION);
59 if (server)
60 SSL_CTX_set_alpn_select_cb(tls->ssl_ctx, select_alpn, tls);
61 else
62 SSL_CTX_set_verify(tls->ssl_ctx, SSL_VERIFY_PEER, NULL);
63 *out = tls;
64 return ASCIICHAT_OK;
65}
66
67asciichat_error_t quic_tls_server_create(const char *alpn, const char *certificate_file, const char *private_key_file,
68 quic_tls_t **out) {
69 if (!out)
70 return SET_ERRNO(ERROR_INVALID_PARAM, "QUIC server requires a certificate and private key");
71 *out = NULL;
72 if (!certificate_file || !*certificate_file || !private_key_file || !*private_key_file)
73 return SET_ERRNO(ERROR_INVALID_PARAM, "QUIC server requires a certificate and private key");
74 quic_tls_t *tls = NULL;
75 asciichat_error_t result = create_context(true, alpn, &tls);
76 if (result != ASCIICHAT_OK)
77 return result;
78 ERR_clear_error();
79 if (SSL_CTX_use_certificate_chain_file(tls->ssl_ctx, certificate_file) != 1 ||
80 SSL_CTX_use_PrivateKey_file(tls->ssl_ctx, private_key_file, SSL_FILETYPE_PEM) != 1 ||
81 SSL_CTX_check_private_key(tls->ssl_ctx) != 1) {
82 result = tls_error("Cannot load QUIC server certificate or private key");
83 quic_tls_destroy(&tls);
84 return result;
85 }
86 *out = tls;
87 return ASCIICHAT_OK;
88}
89
90asciichat_error_t quic_tls_client_create(const char *alpn, const char *ca_file, quic_tls_t **out) {
91 if (!out)
92 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC client trust configuration");
93 *out = NULL;
94 if (ca_file && !*ca_file)
95 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC client trust configuration");
96 quic_tls_t *tls = NULL;
97 asciichat_error_t result = create_context(false, alpn, &tls);
98 if (result != ASCIICHAT_OK)
99 return result;
100 ERR_clear_error();
101 int loaded = ca_file ? SSL_CTX_load_verify_locations(tls->ssl_ctx, ca_file, NULL)
102 : SSL_CTX_set_default_verify_paths(tls->ssl_ctx);
103 if (loaded != 1) {
104 result = tls_error("Cannot load QUIC client trust roots");
105 quic_tls_destroy(&tls);
106 return result;
107 }
108 *out = tls;
109 return ASCIICHAT_OK;
110}
111
112asciichat_error_t quic_tls_client_configure(quic_tls_t *tls, SSL *ssl, const char *server_name) {
113 if (!tls || tls->server || !ssl || SSL_get_SSL_CTX(ssl) != tls->ssl_ctx || !server_name || !*server_name)
114 return SET_ERRNO(ERROR_INVALID_PARAM, "QUIC client requires the expected server identity");
115 ERR_clear_error();
116 X509_VERIFY_PARAM *verify = SSL_get0_param(ssl);
117 X509_VERIFY_PARAM_set_hostflags(verify, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
118 if (SSL_set_alpn_protos(ssl, tls->alpn, tls->alpn_length) != 0 ||
119 (is_valid_ip(server_name)
120 ? X509_VERIFY_PARAM_set1_ip_asc(verify, server_name) != 1
121 : (SSL_set_tlsext_host_name(ssl, server_name) != 1 || SSL_set1_host(ssl, server_name) != 1)))
122 return tls_error("Cannot configure QUIC server identity");
123 return ASCIICHAT_OK;
124}
125
126asciichat_error_t quic_tls_session_create(quic_tls_t *tls, const char *server_name, ngtcp2_crypto_conn_ref *connection,
127 SSL **out) {
128 if (!out)
129 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC TLS session arguments");
130 *out = NULL;
131 if (!tls || !tls->ssl_ctx || !connection || !connection->get_conn || (tls->server && server_name))
132 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC TLS session arguments");
133 ERR_clear_error();
134 SSL *ssl = SSL_new(tls->ssl_ctx);
135 if (!ssl)
136 return tls_error("Cannot create QUIC TLS session");
137 if (SSL_set_app_data(ssl, connection) != 1) {
138 asciichat_error_t result = tls_error("Cannot attach QUIC connection to TLS session");
139 SSL_free(ssl);
140 return result;
141 }
143 if (tls->server) {
144 SSL_set_accept_state(ssl);
145 if (ngtcp2_crypto_ossl_configure_server_session(ssl) != 0)
146 result = tls_error("Cannot configure server QUIC TLS callbacks");
147 } else {
148 SSL_set_connect_state(ssl);
149 result = quic_tls_client_configure(tls, ssl, server_name);
150 if (result == ASCIICHAT_OK && ngtcp2_crypto_ossl_configure_client_session(ssl) != 0)
151 result = tls_error("Cannot configure client QUIC TLS callbacks");
152 }
153 if (result != ASCIICHAT_OK) {
155 return result;
156 }
157 *out = ssl;
158 return ASCIICHAT_OK;
159}
160
162 if (!tls || !ssl || SSL_get_SSL_CTX(ssl) != tls->ssl_ctx || !SSL_is_init_finished(ssl))
163 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC TLS handshake is not complete");
164 const unsigned char *selected = NULL;
165 unsigned int length = 0;
166 SSL_get0_alpn_selected(ssl, &selected, &length);
167 bool negotiated = false;
168 for (size_t offset = 0; offset < tls->alpn_length;) {
169 size_t protocol_length = tls->alpn[offset++];
170 if (protocol_length > tls->alpn_length - offset)
171 break;
172 if (selected && protocol_length == (size_t)length && !memcmp(selected, tls->alpn + offset, protocol_length))
173 negotiated = true;
174 offset += protocol_length;
175 }
176 if (!negotiated)
177 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "QUIC TLS ALPN was not negotiated");
178 if (!tls->server) {
179 X509_VERIFY_PARAM *verify = SSL_get0_param(ssl);
180 const char *host = X509_VERIFY_PARAM_get0_host(verify, 0);
181 char *ip = X509_VERIFY_PARAM_get1_ip_asc(verify);
182 X509 *peer = SSL_get0_peer_certificate(ssl);
183 bool identity_matches =
184 peer && (host || ip) &&
185 (!host || X509_check_host(peer, host, 0, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS, NULL) == 1) &&
186 (!ip || X509_check_ip_asc(peer, ip, 0) == 1);
187 OPENSSL_free(ip);
188 if (!identity_matches || !(SSL_get_verify_mode(ssl) & SSL_VERIFY_PEER) || SSL_get_verify_result(ssl) != X509_V_OK)
189 return SET_ERRNO(ERROR_CRYPTO, "QUIC TLS peer certificate or identity was not verified");
190 }
191 return ASCIICHAT_OK;
192}
193
195 if (ssl && *ssl) {
196 SSL_set_app_data(*ssl, NULL);
197 SSL_free(*ssl);
198 *ssl = NULL;
199 }
200}
201
203 if (tls && *tls) {
204 SSL_CTX_free((*tls)->ssl_ctx);
205 SAFE_FREE(*tls);
206 }
207}
#define SET_ERRNO(code,...)
⚙️ Common definitions, error codes, macros, and types shared throughout the application
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ERROR_INVALID_STATE
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:76
@ ERROR_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_CRYPTO
Definition error_codes.h:93
@ ERROR_INVALID_PARAM
int is_valid_ip(const char *ip)
Check if string is a valid IP address (IPv4 or IPv6).
Definition ip.c:453
🌍 IP Address Parsing and Formatting Utilities
static bool size_t gotel_query_t * out
Definition queries.h:8
🔤 String Manipulation and Shell Escaping Utilities
SSL_CTX * ssl_ctx
Definition tls.h:12
unsigned char alpn[256]
Definition tls.h:13
bool server
Definition tls.h:15
unsigned int alpn_length
Definition tls.h:14
asciichat_error_t quic_tls_client_create(const char *alpn, const char *ca_file, quic_tls_t **out)
Definition tls.c:90
asciichat_error_t quic_tls_verify_ready(quic_tls_t *tls, SSL *ssl)
Definition tls.c:161
asciichat_error_t quic_tls_session_create(quic_tls_t *tls, const char *server_name, ngtcp2_crypto_conn_ref *connection, SSL **out)
Definition tls.c:126
void quic_tls_session_destroy(SSL **ssl)
Definition tls.c:194
void quic_tls_destroy(quic_tls_t **tls)
Definition tls.c:202
asciichat_error_t quic_tls_client_configure(quic_tls_t *tls, SSL *ssl, const char *server_name)
Definition tls.c:112
asciichat_error_t quic_tls_server_create(const char *alpn, const char *certificate_file, const char *private_key_file, quic_tls_t **out)
Definition tls.c:67