Add HTTPS support

This commit is contained in:
2025-11-21 19:52:28 +01:00
parent 3f76d1827e
commit ccdd573e90
8 changed files with 857 additions and 83 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ chttp.c chttp.h: $(wildcard src/*.c src/*.h) misc/amalg.py Makefile
python misc/amalg.py python misc/amalg.py
example: main.c chttp.c chttp.h example: main.c chttp.c chttp.h
gcc main.c chttp.c -o example -Wfatal-errors -lws2_32 gcc main.c chttp.c -o example -Wfatal-errors -DHTTPS_ENABLED -lcrypto -lssl
clean: clean:
rm chttp.c chttp.h rm chttp.c chttp.h
+375 -23
View File
@@ -1475,38 +1475,206 @@ int mutex_unlock(Mutex *mutex)
int global_secure_context_init(void) int global_secure_context_init(void)
{ {
// TODO #ifdef HTTPS_ENABLED
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
#endif
return 0;
} }
int global_secure_context_free(void) int global_secure_context_free(void)
{ {
// TODO #ifdef HTTPS_ENABLED
EVP_cleanup();
#endif
return 0;
} }
int client_secure_context_init(ClientSecureContext *ctx) int client_secure_context_init(ClientSecureContext *ctx)
{ {
// TODO #ifdef HTTPS_ENABLED
SSL_CTX *p = SSL_CTX_new(TLS_client_method());
if (!p)
return -1;
SSL_CTX_set_min_proto_version(p, TLS1_2_VERSION);
SSL_CTX_set_verify(p, SSL_VERIFY_PEER, NULL);
if (SSL_CTX_set_default_verify_paths(p) != 1) {
SSL_CTX_free(p);
return -1;
}
ctx->p = p;
return 0;
#else
(void) ctx;
return -1;
#endif
} }
int client_secure_context_free(ClientSecureContext *ctx) void client_secure_context_free(ClientSecureContext *ctx)
{ {
// TODO #ifdef HTTPS_ENABLED
SSL_CTX_free(ctx->p);
#endif
} }
int server_secure_context_init(ServerSecureContext *ctx) #ifdef HTTPS_ENABLED
static int servername_callback(SSL *ssl, int *ad, void *arg)
{ {
// TODO ServerSecureContext *ctx = arg;
(void) ad; // TODO: use this?
const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (servername == NULL)
return SSL_TLSEXT_ERR_NOACK;
for (int i = 0; i < ctx->num_certs; i++) {
ServerCertificate *cert = &ctx->certs[i];
if (!strcmp(cert->domain, servername)) {
SSL_set_SSL_CTX(ssl, cert->ctx);
return SSL_TLSEXT_ERR_OK;
}
}
return SSL_TLSEXT_ERR_NOACK;
}
#endif
int server_secure_context_init(ServerSecureContext *ctx,
HTTP_String cert_file, HTTP_String key_file)
{
#ifdef HTTPS_ENABLED
SSL_CTX *p = SSL_CTX_new(TLS_server_method());
if (!p)
return -1;
SSL_CTX_set_min_proto_version(p, TLS1_2_VERSION);
char cert_buffer[1024];
if (cert_file.len >= (int) sizeof(cert_buffer)) {
SSL_CTX_free(p);
return -1;
}
memcpy(cert_buffer, cert_file.ptr, cert_file.len);
cert_buffer[cert_file.len] = '\0';
// Copy private key file path to static buffer
char key_buffer[1024];
if (key_file.len >= (int) sizeof(key_buffer)) {
SSL_CTX_free(p);
return -1;
}
memcpy(key_buffer, key_file.ptr, key_file.len);
key_buffer[key_file.len] = '\0';
// Load certificate and private key
if (SSL_CTX_use_certificate_file(p, cert_buffer, SSL_FILETYPE_PEM) != 1) {
SSL_CTX_free(p);
return -1;
}
if (SSL_CTX_use_PrivateKey_file(p, key_buffer, SSL_FILETYPE_PEM) != 1) {
SSL_CTX_free(p);
return -1;
}
// Verify that the private key matches the certificate
if (SSL_CTX_check_private_key(p) != 1) {
SSL_CTX_free(p);
return -1;
}
SSL_CTX_set_tlsext_servername_callback(p, servername_callback);
SSL_CTX_set_tlsext_servername_arg(p, ctx);
ctx->p = p;
ctx->num_certs = 0;
return 0;
#else
(void) ctx;
(void) cert_file;
(void) key_file;
return -1;
#endif
} }
int server_secure_context_free(ServerSecureContext *ctx) void server_secure_context_free(ServerSecureContext *ctx)
{ {
// TODO #ifdef HTTPS_ENABLED
SSL_CTX_free(ctx->p);
for (int i = 0; i < ctx->num_certs; i++)
SSL_CTX_free(ctx->certs[i].ctx);
#else
(void) ctx;
#endif
} }
int server_secure_context_add_certificate(ServerSecureContext *ctx, int server_secure_context_add_certificate(ServerSecureContext *ctx,
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) HTTP_String domain, HTTP_String cert_file, HTTP_String key_file)
{ {
// TODO #ifdef HTTPS_ENABLED
if (ctx->num_certs == SERVER_CERTIFICATE_LIMIT)
return -1;
SSL_CTX *p = SSL_CTX_new(TLS_server_method());
if (!p)
return -1;
SSL_CTX_set_min_proto_version(p, TLS1_2_VERSION);
char cert_buffer[1024];
if (cert_file.len >= (int) sizeof(cert_buffer)) {
SSL_CTX_free(p);
return -1;
}
memcpy(cert_buffer, cert_file.ptr, cert_file.len);
cert_buffer[cert_file.len] = '\0';
char key_buffer[1024];
if (key_file.len >= (int) sizeof(key_buffer)) {
SSL_CTX_free(p);
return -1;
}
memcpy(key_buffer, key_file.ptr, key_file.len);
key_buffer[key_file.len] = '\0';
if (SSL_CTX_use_certificate_file(p, cert_buffer, SSL_FILETYPE_PEM) != 1) {
SSL_CTX_free(p);
return -1;
}
if (SSL_CTX_use_PrivateKey_file(p, key_buffer, SSL_FILETYPE_PEM) != 1) {
SSL_CTX_free(p);
return -1;
}
if (SSL_CTX_check_private_key(p) != 1) {
SSL_CTX_free(p);
return -1;
}
ServerCertificate *cert = &ctx->certs[ctx->num_certs];
if (domain.len >= (int) sizeof(cert->domain)) {
SSL_CTX_free(p);
return -1;
}
memcpy(cert->domain, domain.ptr, domain.len);
cert->domain[domain.len] = '\0';
cert->ctx = p;
ctx->num_certs++;
return 0;
#else
(void) ctx;
(void) domain;
(void) cert_file;
(void) key_file;
return -1;
#endif
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@@ -1703,8 +1871,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
} }
int socket_manager_listen_tls(SocketManager *sm, int socket_manager_listen_tls(SocketManager *sm,
HTTP_String addr, Port port, HTTP_String cert_file_name, HTTP_String addr, Port port, HTTP_String cert_file,
HTTP_String key_file_name) HTTP_String key_file)
{ {
if (sm->secure_sock != NATIVE_SOCKET_INVALID) if (sm->secure_sock != NATIVE_SOCKET_INVALID)
return -1; return -1;
@@ -1715,7 +1883,8 @@ int socket_manager_listen_tls(SocketManager *sm,
if (sm->secure_sock == NATIVE_SOCKET_INVALID) if (sm->secure_sock == NATIVE_SOCKET_INVALID)
return -1; return -1;
if (server_secure_context_init(&sm->server_secure_context) < 0) { if (server_secure_context_init(&sm->server_secure_context,
cert_file, key_file) < 0) {
CLOSE_NATIVE_SOCKET(sm->secure_sock); CLOSE_NATIVE_SOCKET(sm->secure_sock);
sm->secure_sock = NATIVE_SOCKET_INVALID; sm->secure_sock = NATIVE_SOCKET_INVALID;
return -1; return -1;
@@ -1741,7 +1910,7 @@ int socket_manager_add_certificate(SocketManager *sm,
static bool is_secure(Socket *s) static bool is_secure(Socket *s)
{ {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
return (s->ssl != NULL); return (s->server_secure_context != NULL);
#else #else
return false; return false;
#endif #endif
@@ -1783,6 +1952,24 @@ connect_failed_because_of_peer(void)
return connect_failed_because_of_peer_2(err); return connect_failed_because_of_peer_2(err);
} }
static void free_addr_list(AddressAndPort *addrs, int num_addr)
{
#ifdef HTTPS_ENABLED
for (int i = 0; i < num_addr; i++) {
RegisteredName *name = addrs[i].name;
if (name) {
assert(name->refs > 0);
name->refs--;
if (name->refs == 0)
free(name);
}
}
#else
(void) addrs;
(void) num_addr;
#endif
}
// This function moves the socket state machine // This function moves the socket state machine
// to the next state until an I/O event would // to the next state until an I/O event would
// be required to continue. // be required to continue.
@@ -1811,6 +1998,13 @@ static void socket_update(Socket *s)
if (s->sock != NATIVE_SOCKET_INVALID) { if (s->sock != NATIVE_SOCKET_INVALID) {
// This is not the first attempt // This is not the first attempt
#ifdef HTTPS_ENABLED
if (s->ssl) {
SSL_free(s->ssl);
s->ssl = NULL;
}
#endif
CLOSE_NATIVE_SOCKET(s->sock); CLOSE_NATIVE_SOCKET(s->sock);
s->next_addr++; s->next_addr++;
@@ -1903,18 +2097,76 @@ static void socket_update(Socket *s)
case SOCKET_STATE_CONNECTED: case SOCKET_STATE_CONNECTED:
{ {
if (!is_secure(s)) {
// We managed to connect to the peer. // We managed to connect to the peer.
// We can free the target array if it // We can free the target array if it
// was allocated dynamically. // was allocated dynamically.
if (s->num_addr > 1) if (s->num_addr > 1)
free(s->addrs); free(s->addrs);
if (!is_secure(s)) {
s->events = 0; s->events = 0;
s->state = SOCKET_STATE_ESTABLISHED_READY; s->state = SOCKET_STATE_ESTABLISHED_READY;
} else { } else {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
assert(0); // TODO if (s->ssl == NULL) {
s->ssl = SSL_new(s->server_secure_context->p);
if (s->ssl == NULL) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
if (SSL_set_fd(s->ssl, s->sock) != 1) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
AddressAndPort addr;
if (s->num_addr > 1)
addr = s->addrs[s->next_addr];
else
addr = s->addr;
if (addr.name)
SSL_set_tlsext_host_name(s->ssl, addr.name->data);
}
int ret = SSL_connect(s->ssl);
if (ret == 1) {
// Handshake done
// We managed to connect to the peer.
// We can free the target array if it
// was allocated dynamically.
if (s->num_addr == 1)
free_addr_list(&s->addr, 1);
else {
assert(s->num_addr > 1);
free_addr_list(s->addrs, s->num_addr);
free(s->addrs);
}
s->state = SOCKET_STATE_ESTABLISHED_READY;
s->events = 0;
break;
}
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
s->events = POLLIN;
break;
}
if (err == SSL_ERROR_WANT_WRITE) {
s->events = POLLOUT;
break;
}
s->state = SOCKET_STATE_PENDING;
s->events = 0;
again = true;
#endif #endif
} }
} }
@@ -1927,7 +2179,45 @@ static void socket_update(Socket *s)
s->events = 0; s->events = 0;
} else { } else {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
assert(0); // TODO // Start server-side SSL handshake
if (!s->ssl) {
s->ssl = SSL_new(s->server_secure_context->p);
if (s->ssl == NULL) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
if (SSL_set_fd(s->ssl, s->sock) != 1) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
}
int ret = SSL_accept(s->ssl);
if (ret == 1) {
// Handshake done
s->state = SOCKET_STATE_ESTABLISHED_READY;
s->events = 0;
break;
}
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
s->events = POLLIN;
break;
}
if (err == SSL_ERROR_WANT_WRITE) {
s->events = POLLOUT;
break;
}
// Server socket error - close the connection
s->state = SOCKET_STATE_DIED;
s->events = 0;
#endif #endif
} }
} }
@@ -1945,7 +2235,26 @@ static void socket_update(Socket *s)
s->events = 0; s->events = 0;
} else { } else {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
assert(0); // TODO int ret = SSL_shutdown(s->ssl);
if (ret == 1) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
s->events = POLLIN;
break;
}
if (err == SSL_ERROR_WANT_WRITE) {
s->events = POLLOUT;
break;
}
s->state = SOCKET_STATE_DIED;
s->events = 0;
#endif #endif
} }
} }
@@ -2107,6 +2416,9 @@ static int socket_manager_translate_events_nolock(
s->sock = sock; s->sock = sock;
s->events = 0; s->events = 0;
s->user = NULL; s->user = NULL;
#ifdef HTTPS_ENABLED
s->ssl = NULL;
#endif
socket_update(s); socket_update(s);
if (s->state == SOCKET_STATE_DIED) { if (s->state == SOCKET_STATE_DIED) {
@@ -2192,23 +2504,35 @@ static int resolve_connect_targets(ConnectTarget *targets,
{ {
char portstr[16]; char portstr[16];
int len = snprintf(portstr, sizeof(portstr), "%u", targets[i].port); int len = snprintf(portstr, sizeof(portstr), "%u", targets[i].port);
if (len < 0 || len >= (int) sizeof(portstr)) assert(len > 1 && len < (int) sizeof(portstr));
return -1;
struct addrinfo hints = {0}; struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
#ifdef HTTPS_ENABLED
RegisteredName *name = malloc(sizeof(RegisteredName) + targets[i].name.len + 1);
if (name == NULL) {
free_addr_list(resolved, num_resolved);
return -1;
}
name->refs = 0;
memcpy(name->data, targets[i].name.ptr, targets[i].name.len);
name->data[targets[i].name.len] = '\0';
char *hostname = name->data;
#else
char hostname[1<<9]; // TODO: Is this a good length? char hostname[1<<9]; // TODO: Is this a good length?
if (targets[i].name.len >= (int) sizeof(hostname)) if (targets[i].name.len >= (int) sizeof(hostname))
return -1; return -1;
memcpy(hostname, targets[i].name.ptr, targets[i].name.len); memcpy(hostname, targets[i].name.ptr, targets[i].name.len);
hostname[targets[i].name.len] = '\0'; hostname[targets[i].name.len] = '\0';
#endif
struct addrinfo *res = NULL; struct addrinfo *res = NULL;
int ret = getaddrinfo(hostname, portstr, &hints, &res); int ret = getaddrinfo(hostname, portstr, &hints, &res);
if (ret != 0) if (ret != 0) {
return -1; free_addr_list(resolved, num_resolved);
return -1; // TODO: free all allocated registered names
}
for (struct addrinfo *rp = res; rp; rp = rp->ai_next) { for (struct addrinfo *rp = res; rp; rp = rp->ai_next) {
if (rp->ai_family == AF_INET) { if (rp->ai_family == AF_INET) {
@@ -2217,6 +2541,10 @@ static int resolve_connect_targets(ConnectTarget *targets,
resolved[num_resolved].is_ipv4 = true; resolved[num_resolved].is_ipv4 = true;
resolved[num_resolved].ipv4 = ipv4; resolved[num_resolved].ipv4 = ipv4;
resolved[num_resolved].port = targets[i].port; resolved[num_resolved].port = targets[i].port;
#ifdef HTTPS_ENABLED
resolved[num_resolved].name = name;
name->refs++;
#endif
num_resolved++; num_resolved++;
} }
} else if (rp->ai_family == AF_INET6) { } else if (rp->ai_family == AF_INET6) {
@@ -2225,11 +2553,20 @@ static int resolve_connect_targets(ConnectTarget *targets,
resolved[num_resolved].is_ipv4 = false; resolved[num_resolved].is_ipv4 = false;
resolved[num_resolved].ipv6 = ipv6; resolved[num_resolved].ipv6 = ipv6;
resolved[num_resolved].port = targets[i].port; resolved[num_resolved].port = targets[i].port;
#ifdef HTTPS_ENABLED
resolved[num_resolved].name = name;
name->refs++;
#endif
num_resolved++; num_resolved++;
} }
} }
} }
#ifdef HTTPS_ENABLED
if (name->refs == 0)
free(name);
#endif
freeaddrinfo(res); freeaddrinfo(res);
} }
break; break;
@@ -2238,6 +2575,9 @@ static int resolve_connect_targets(ConnectTarget *targets,
resolved[num_resolved].is_ipv4 = true; resolved[num_resolved].is_ipv4 = true;
resolved[num_resolved].ipv4 = targets[i].ipv4; resolved[num_resolved].ipv4 = targets[i].ipv4;
resolved[num_resolved].port = targets[i].port; resolved[num_resolved].port = targets[i].port;
#ifdef HTTPS_ENABLED
resolved[num_resolved].name = NULL;
#endif
num_resolved++; num_resolved++;
} }
break; break;
@@ -2246,6 +2586,9 @@ static int resolve_connect_targets(ConnectTarget *targets,
resolved[num_resolved].is_ipv4 = false; resolved[num_resolved].is_ipv4 = false;
resolved[num_resolved].ipv6 = targets[i].ipv6; resolved[num_resolved].ipv6 = targets[i].ipv6;
resolved[num_resolved].port = targets[i].port; resolved[num_resolved].port = targets[i].port;
#ifdef HTTPS_ENABLED
resolved[num_resolved].name = NULL;
#endif
num_resolved++; num_resolved++;
} }
break; break;
@@ -2262,6 +2605,11 @@ int socket_connect(SocketManager *sm, int num_targets,
if (sm->num_used == sm->max_used) if (sm->num_used == sm->max_used)
return -1; return -1;
#ifndef HTTPS_ENABLED
if (secure)
return -1;
#endif
AddressAndPort resolved[MAX_CONNECT_TARGETS]; AddressAndPort resolved[MAX_CONNECT_TARGETS];
int num_resolved = resolve_connect_targets( int num_resolved = resolve_connect_targets(
targets, num_targets, resolved, MAX_CONNECT_TARGETS); targets, num_targets, resolved, MAX_CONNECT_TARGETS);
@@ -2292,6 +2640,10 @@ int socket_connect(SocketManager *sm, int num_targets,
s->state = SOCKET_STATE_PENDING; s->state = SOCKET_STATE_PENDING;
s->sock = NATIVE_SOCKET_INVALID; s->sock = NATIVE_SOCKET_INVALID;
s->user = user; s->user = user;
#ifdef HTTPS_ENABLED
s->server_secure_context = &sm->server_secure_context;
s->ssl = NULL;
#endif
sm->num_used++; sm->num_used++;
return 0; return 0;
} }
+46 -11
View File
@@ -32,6 +32,10 @@
#include <sys/socket.h> #include <sys/socket.h>
#endif #endif
#ifdef HTTPS_ENABLED
#include <openssl/ssl.h>
#endif
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// src/basic.h // src/basic.h
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@@ -204,32 +208,43 @@ int mutex_unlock(Mutex *mutex);
// src/secure_context.h // src/secure_context.h
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
#ifndef SERVER_CERTIFICATE_LIMIT
// Maximum number of certificates that can be
// associated to a TLS server. This doesn't include
// the default certificate.
#define SERVER_CERTIFICATE_LIMIT 8
#endif
int global_secure_context_init(void); int global_secure_context_init(void);
int global_secure_context_free(void); int global_secure_context_free(void);
typedef struct { typedef struct {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
// TODO
SSL_CTX *p; SSL_CTX *p;
#endif #endif
} ClientSecureContext; } ClientSecureContext;
int client_secure_context_init(ClientSecureContext *ctx); int client_secure_context_init(ClientSecureContext *ctx);
int client_secure_context_free(ClientSecureContext *ctx); void client_secure_context_free(ClientSecureContext *ctx);
typedef struct { typedef struct {
#ifdef HTTPS_ENABLED
} SecureDomain; char domain[128];
SSL_CTX *ctx;
#endif
} ServerCertificate;
typedef struct { typedef struct {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
// TODO
SSL_CTX *p; SSL_CTX *p;
int num_certs;
ServerCertificate certs[SERVER_CERTIFICATE_LIMIT];
#endif #endif
} ServerSecureContext; } ServerSecureContext;
int server_secure_context_init(ServerSecureContext *ctx); int server_secure_context_init(ServerSecureContext *ctx,
int server_secure_context_free(ServerSecureContext *ctx); HTTP_String cert_file, HTTP_String key_file);
void server_secure_context_free(ServerSecureContext *ctx);
int server_secure_context_add_certificate(ServerSecureContext *ctx, int server_secure_context_add_certificate(ServerSecureContext *ctx,
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file); HTTP_String domain, HTTP_String cert_file, HTTP_String key_file);
@@ -366,6 +381,11 @@ typedef enum {
SOCKET_STATE_DIED, SOCKET_STATE_DIED,
} SocketState; } SocketState;
typedef struct {
int refs;
char data[];
} RegisteredName;
// Internal use only // Internal use only
typedef struct { typedef struct {
union { union {
@@ -374,6 +394,15 @@ typedef struct {
}; };
bool is_ipv4; bool is_ipv4;
Port port; Port port;
#ifdef HTTPS_ENABLED
// When connecting to a peer using TLS, if the address
// was resolved from a registered name, that name is
// used to request the correct certificate once the TCP
// handshake is established, and therefore need to
// store it somewhere until that happens.
RegisteredName *name;
#endif
} AddressAndPort; } AddressAndPort;
// Internal use only // Internal use only
@@ -405,6 +434,12 @@ typedef struct {
AddressAndPort addr; // When num_addr=1 AddressAndPort addr; // When num_addr=1
AddressAndPort *addrs; // Dynamically allocated when num_addr>1 AddressAndPort *addrs; // Dynamically allocated when num_addr>1
}; };
#ifdef HTTPS_ENABLED
ServerSecureContext *server_secure_context;
SSL *ssl;
#endif
} Socket; } Socket;
// Glorified array of sockets. This structure // Glorified array of sockets. This structure
@@ -476,8 +511,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
// and secure connections. // and secure connections.
// Returns 0 on success, -1 on error. // Returns 0 on success, -1 on error.
int socket_manager_listen_tls(SocketManager *sm, int socket_manager_listen_tls(SocketManager *sm,
HTTP_String addr, Port port, HTTP_String cert_file_name, HTTP_String addr, Port port, HTTP_String cert_file,
HTTP_String key_file_name); HTTP_String key_file);
// If the socket manager was configures to accept // If the socket manager was configures to accept
// TLS connections, this adds additional certificates // TLS connections, this adds additional certificates
+4
View File
@@ -23,3 +23,7 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/socket.h> #include <sys/socket.h>
#endif #endif
#ifdef HTTPS_ENABLED
#include <openssl/ssl.h>
#endif
+178 -10
View File
@@ -1,36 +1,204 @@
int global_secure_context_init(void) int global_secure_context_init(void)
{ {
// TODO #ifdef HTTPS_ENABLED
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
#endif
return 0;
} }
int global_secure_context_free(void) int global_secure_context_free(void)
{ {
// TODO #ifdef HTTPS_ENABLED
EVP_cleanup();
#endif
return 0;
} }
int client_secure_context_init(ClientSecureContext *ctx) int client_secure_context_init(ClientSecureContext *ctx)
{ {
// TODO #ifdef HTTPS_ENABLED
SSL_CTX *p = SSL_CTX_new(TLS_client_method());
if (!p)
return -1;
SSL_CTX_set_min_proto_version(p, TLS1_2_VERSION);
SSL_CTX_set_verify(p, SSL_VERIFY_PEER, NULL);
if (SSL_CTX_set_default_verify_paths(p) != 1) {
SSL_CTX_free(p);
return -1;
}
ctx->p = p;
return 0;
#else
(void) ctx;
return -1;
#endif
} }
int client_secure_context_free(ClientSecureContext *ctx) void client_secure_context_free(ClientSecureContext *ctx)
{ {
// TODO #ifdef HTTPS_ENABLED
SSL_CTX_free(ctx->p);
#endif
} }
int server_secure_context_init(ServerSecureContext *ctx) #ifdef HTTPS_ENABLED
static int servername_callback(SSL *ssl, int *ad, void *arg)
{ {
// TODO ServerSecureContext *ctx = arg;
(void) ad; // TODO: use this?
const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (servername == NULL)
return SSL_TLSEXT_ERR_NOACK;
for (int i = 0; i < ctx->num_certs; i++) {
ServerCertificate *cert = &ctx->certs[i];
if (!strcmp(cert->domain, servername)) {
SSL_set_SSL_CTX(ssl, cert->ctx);
return SSL_TLSEXT_ERR_OK;
}
}
return SSL_TLSEXT_ERR_NOACK;
}
#endif
int server_secure_context_init(ServerSecureContext *ctx,
HTTP_String cert_file, HTTP_String key_file)
{
#ifdef HTTPS_ENABLED
SSL_CTX *p = SSL_CTX_new(TLS_server_method());
if (!p)
return -1;
SSL_CTX_set_min_proto_version(p, TLS1_2_VERSION);
char cert_buffer[1024];
if (cert_file.len >= (int) sizeof(cert_buffer)) {
SSL_CTX_free(p);
return -1;
}
memcpy(cert_buffer, cert_file.ptr, cert_file.len);
cert_buffer[cert_file.len] = '\0';
// Copy private key file path to static buffer
char key_buffer[1024];
if (key_file.len >= (int) sizeof(key_buffer)) {
SSL_CTX_free(p);
return -1;
}
memcpy(key_buffer, key_file.ptr, key_file.len);
key_buffer[key_file.len] = '\0';
// Load certificate and private key
if (SSL_CTX_use_certificate_file(p, cert_buffer, SSL_FILETYPE_PEM) != 1) {
SSL_CTX_free(p);
return -1;
}
if (SSL_CTX_use_PrivateKey_file(p, key_buffer, SSL_FILETYPE_PEM) != 1) {
SSL_CTX_free(p);
return -1;
}
// Verify that the private key matches the certificate
if (SSL_CTX_check_private_key(p) != 1) {
SSL_CTX_free(p);
return -1;
}
SSL_CTX_set_tlsext_servername_callback(p, servername_callback);
SSL_CTX_set_tlsext_servername_arg(p, ctx);
ctx->p = p;
ctx->num_certs = 0;
return 0;
#else
(void) ctx;
(void) cert_file;
(void) key_file;
return -1;
#endif
} }
int server_secure_context_free(ServerSecureContext *ctx) void server_secure_context_free(ServerSecureContext *ctx)
{ {
// TODO #ifdef HTTPS_ENABLED
SSL_CTX_free(ctx->p);
for (int i = 0; i < ctx->num_certs; i++)
SSL_CTX_free(ctx->certs[i].ctx);
#else
(void) ctx;
#endif
} }
int server_secure_context_add_certificate(ServerSecureContext *ctx, int server_secure_context_add_certificate(ServerSecureContext *ctx,
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file) HTTP_String domain, HTTP_String cert_file, HTTP_String key_file)
{ {
// TODO #ifdef HTTPS_ENABLED
if (ctx->num_certs == SERVER_CERTIFICATE_LIMIT)
return -1;
SSL_CTX *p = SSL_CTX_new(TLS_server_method());
if (!p)
return -1;
SSL_CTX_set_min_proto_version(p, TLS1_2_VERSION);
char cert_buffer[1024];
if (cert_file.len >= (int) sizeof(cert_buffer)) {
SSL_CTX_free(p);
return -1;
}
memcpy(cert_buffer, cert_file.ptr, cert_file.len);
cert_buffer[cert_file.len] = '\0';
char key_buffer[1024];
if (key_file.len >= (int) sizeof(key_buffer)) {
SSL_CTX_free(p);
return -1;
}
memcpy(key_buffer, key_file.ptr, key_file.len);
key_buffer[key_file.len] = '\0';
if (SSL_CTX_use_certificate_file(p, cert_buffer, SSL_FILETYPE_PEM) != 1) {
SSL_CTX_free(p);
return -1;
}
if (SSL_CTX_use_PrivateKey_file(p, key_buffer, SSL_FILETYPE_PEM) != 1) {
SSL_CTX_free(p);
return -1;
}
if (SSL_CTX_check_private_key(p) != 1) {
SSL_CTX_free(p);
return -1;
}
ServerCertificate *cert = &ctx->certs[ctx->num_certs];
if (domain.len >= (int) sizeof(cert->domain)) {
SSL_CTX_free(p);
return -1;
}
memcpy(cert->domain, domain.ptr, domain.len);
cert->domain[domain.len] = '\0';
cert->ctx = p;
ctx->num_certs++;
return 0;
#else
(void) ctx;
(void) domain;
(void) cert_file;
(void) key_file;
return -1;
#endif
} }
+20 -9
View File
@@ -1,29 +1,40 @@
#ifndef SERVER_CERTIFICATE_LIMIT
// Maximum number of certificates that can be
// associated to a TLS server. This doesn't include
// the default certificate.
#define SERVER_CERTIFICATE_LIMIT 8
#endif
int global_secure_context_init(void); int global_secure_context_init(void);
int global_secure_context_free(void); int global_secure_context_free(void);
typedef struct { typedef struct {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
// TODO
SSL_CTX *p; SSL_CTX *p;
#endif #endif
} ClientSecureContext; } ClientSecureContext;
int client_secure_context_init(ClientSecureContext *ctx); int client_secure_context_init(ClientSecureContext *ctx);
int client_secure_context_free(ClientSecureContext *ctx); void client_secure_context_free(ClientSecureContext *ctx);
typedef struct { typedef struct {
#ifdef HTTPS_ENABLED
} SecureDomain; char domain[128];
SSL_CTX *ctx;
#endif
} ServerCertificate;
typedef struct { typedef struct {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
// TODO
SSL_CTX *p; SSL_CTX *p;
int num_certs;
ServerCertificate certs[SERVER_CERTIFICATE_LIMIT];
#endif #endif
} ServerSecureContext; } ServerSecureContext;
int server_secure_context_init(ServerSecureContext *ctx); int server_secure_context_init(ServerSecureContext *ctx,
int server_secure_context_free(ServerSecureContext *ctx); HTTP_String cert_file, HTTP_String key_file);
void server_secure_context_free(ServerSecureContext *ctx);
int server_secure_context_add_certificate(ServerSecureContext *ctx, int server_secure_context_add_certificate(ServerSecureContext *ctx,
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file); HTTP_String domain, HTTP_String cert_file, HTTP_String key_file);
+197 -13
View File
@@ -189,8 +189,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
} }
int socket_manager_listen_tls(SocketManager *sm, int socket_manager_listen_tls(SocketManager *sm,
HTTP_String addr, Port port, HTTP_String cert_file_name, HTTP_String addr, Port port, HTTP_String cert_file,
HTTP_String key_file_name) HTTP_String key_file)
{ {
if (sm->secure_sock != NATIVE_SOCKET_INVALID) if (sm->secure_sock != NATIVE_SOCKET_INVALID)
return -1; return -1;
@@ -201,7 +201,8 @@ int socket_manager_listen_tls(SocketManager *sm,
if (sm->secure_sock == NATIVE_SOCKET_INVALID) if (sm->secure_sock == NATIVE_SOCKET_INVALID)
return -1; return -1;
if (server_secure_context_init(&sm->server_secure_context) < 0) { if (server_secure_context_init(&sm->server_secure_context,
cert_file, key_file) < 0) {
CLOSE_NATIVE_SOCKET(sm->secure_sock); CLOSE_NATIVE_SOCKET(sm->secure_sock);
sm->secure_sock = NATIVE_SOCKET_INVALID; sm->secure_sock = NATIVE_SOCKET_INVALID;
return -1; return -1;
@@ -227,7 +228,7 @@ int socket_manager_add_certificate(SocketManager *sm,
static bool is_secure(Socket *s) static bool is_secure(Socket *s)
{ {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
return (s->ssl != NULL); return (s->server_secure_context != NULL);
#else #else
return false; return false;
#endif #endif
@@ -269,6 +270,24 @@ connect_failed_because_of_peer(void)
return connect_failed_because_of_peer_2(err); return connect_failed_because_of_peer_2(err);
} }
static void free_addr_list(AddressAndPort *addrs, int num_addr)
{
#ifdef HTTPS_ENABLED
for (int i = 0; i < num_addr; i++) {
RegisteredName *name = addrs[i].name;
if (name) {
assert(name->refs > 0);
name->refs--;
if (name->refs == 0)
free(name);
}
}
#else
(void) addrs;
(void) num_addr;
#endif
}
// This function moves the socket state machine // This function moves the socket state machine
// to the next state until an I/O event would // to the next state until an I/O event would
// be required to continue. // be required to continue.
@@ -297,6 +316,13 @@ static void socket_update(Socket *s)
if (s->sock != NATIVE_SOCKET_INVALID) { if (s->sock != NATIVE_SOCKET_INVALID) {
// This is not the first attempt // This is not the first attempt
#ifdef HTTPS_ENABLED
if (s->ssl) {
SSL_free(s->ssl);
s->ssl = NULL;
}
#endif
CLOSE_NATIVE_SOCKET(s->sock); CLOSE_NATIVE_SOCKET(s->sock);
s->next_addr++; s->next_addr++;
@@ -389,18 +415,76 @@ static void socket_update(Socket *s)
case SOCKET_STATE_CONNECTED: case SOCKET_STATE_CONNECTED:
{ {
if (!is_secure(s)) {
// We managed to connect to the peer. // We managed to connect to the peer.
// We can free the target array if it // We can free the target array if it
// was allocated dynamically. // was allocated dynamically.
if (s->num_addr > 1) if (s->num_addr > 1)
free(s->addrs); free(s->addrs);
if (!is_secure(s)) {
s->events = 0; s->events = 0;
s->state = SOCKET_STATE_ESTABLISHED_READY; s->state = SOCKET_STATE_ESTABLISHED_READY;
} else { } else {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
assert(0); // TODO if (s->ssl == NULL) {
s->ssl = SSL_new(s->server_secure_context->p);
if (s->ssl == NULL) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
if (SSL_set_fd(s->ssl, s->sock) != 1) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
AddressAndPort addr;
if (s->num_addr > 1)
addr = s->addrs[s->next_addr];
else
addr = s->addr;
if (addr.name)
SSL_set_tlsext_host_name(s->ssl, addr.name->data);
}
int ret = SSL_connect(s->ssl);
if (ret == 1) {
// Handshake done
// We managed to connect to the peer.
// We can free the target array if it
// was allocated dynamically.
if (s->num_addr == 1)
free_addr_list(&s->addr, 1);
else {
assert(s->num_addr > 1);
free_addr_list(s->addrs, s->num_addr);
free(s->addrs);
}
s->state = SOCKET_STATE_ESTABLISHED_READY;
s->events = 0;
break;
}
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
s->events = POLLIN;
break;
}
if (err == SSL_ERROR_WANT_WRITE) {
s->events = POLLOUT;
break;
}
s->state = SOCKET_STATE_PENDING;
s->events = 0;
again = true;
#endif #endif
} }
} }
@@ -413,7 +497,45 @@ static void socket_update(Socket *s)
s->events = 0; s->events = 0;
} else { } else {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
assert(0); // TODO // Start server-side SSL handshake
if (!s->ssl) {
s->ssl = SSL_new(s->server_secure_context->p);
if (s->ssl == NULL) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
if (SSL_set_fd(s->ssl, s->sock) != 1) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
}
int ret = SSL_accept(s->ssl);
if (ret == 1) {
// Handshake done
s->state = SOCKET_STATE_ESTABLISHED_READY;
s->events = 0;
break;
}
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
s->events = POLLIN;
break;
}
if (err == SSL_ERROR_WANT_WRITE) {
s->events = POLLOUT;
break;
}
// Server socket error - close the connection
s->state = SOCKET_STATE_DIED;
s->events = 0;
#endif #endif
} }
} }
@@ -431,7 +553,26 @@ static void socket_update(Socket *s)
s->events = 0; s->events = 0;
} else { } else {
#ifdef HTTPS_ENABLED #ifdef HTTPS_ENABLED
assert(0); // TODO int ret = SSL_shutdown(s->ssl);
if (ret == 1) {
s->state = SOCKET_STATE_DIED;
s->events = 0;
break;
}
int err = SSL_get_error(s->ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
s->events = POLLIN;
break;
}
if (err == SSL_ERROR_WANT_WRITE) {
s->events = POLLOUT;
break;
}
s->state = SOCKET_STATE_DIED;
s->events = 0;
#endif #endif
} }
} }
@@ -593,6 +734,9 @@ static int socket_manager_translate_events_nolock(
s->sock = sock; s->sock = sock;
s->events = 0; s->events = 0;
s->user = NULL; s->user = NULL;
#ifdef HTTPS_ENABLED
s->ssl = NULL;
#endif
socket_update(s); socket_update(s);
if (s->state == SOCKET_STATE_DIED) { if (s->state == SOCKET_STATE_DIED) {
@@ -678,23 +822,35 @@ static int resolve_connect_targets(ConnectTarget *targets,
{ {
char portstr[16]; char portstr[16];
int len = snprintf(portstr, sizeof(portstr), "%u", targets[i].port); int len = snprintf(portstr, sizeof(portstr), "%u", targets[i].port);
if (len < 0 || len >= (int) sizeof(portstr)) assert(len > 1 && len < (int) sizeof(portstr));
return -1;
struct addrinfo hints = {0}; struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
#ifdef HTTPS_ENABLED
RegisteredName *name = malloc(sizeof(RegisteredName) + targets[i].name.len + 1);
if (name == NULL) {
free_addr_list(resolved, num_resolved);
return -1;
}
name->refs = 0;
memcpy(name->data, targets[i].name.ptr, targets[i].name.len);
name->data[targets[i].name.len] = '\0';
char *hostname = name->data;
#else
char hostname[1<<9]; // TODO: Is this a good length? char hostname[1<<9]; // TODO: Is this a good length?
if (targets[i].name.len >= (int) sizeof(hostname)) if (targets[i].name.len >= (int) sizeof(hostname))
return -1; return -1;
memcpy(hostname, targets[i].name.ptr, targets[i].name.len); memcpy(hostname, targets[i].name.ptr, targets[i].name.len);
hostname[targets[i].name.len] = '\0'; hostname[targets[i].name.len] = '\0';
#endif
struct addrinfo *res = NULL; struct addrinfo *res = NULL;
int ret = getaddrinfo(hostname, portstr, &hints, &res); int ret = getaddrinfo(hostname, portstr, &hints, &res);
if (ret != 0) if (ret != 0) {
return -1; free_addr_list(resolved, num_resolved);
return -1; // TODO: free all allocated registered names
}
for (struct addrinfo *rp = res; rp; rp = rp->ai_next) { for (struct addrinfo *rp = res; rp; rp = rp->ai_next) {
if (rp->ai_family == AF_INET) { if (rp->ai_family == AF_INET) {
@@ -703,6 +859,10 @@ static int resolve_connect_targets(ConnectTarget *targets,
resolved[num_resolved].is_ipv4 = true; resolved[num_resolved].is_ipv4 = true;
resolved[num_resolved].ipv4 = ipv4; resolved[num_resolved].ipv4 = ipv4;
resolved[num_resolved].port = targets[i].port; resolved[num_resolved].port = targets[i].port;
#ifdef HTTPS_ENABLED
resolved[num_resolved].name = name;
name->refs++;
#endif
num_resolved++; num_resolved++;
} }
} else if (rp->ai_family == AF_INET6) { } else if (rp->ai_family == AF_INET6) {
@@ -711,11 +871,20 @@ static int resolve_connect_targets(ConnectTarget *targets,
resolved[num_resolved].is_ipv4 = false; resolved[num_resolved].is_ipv4 = false;
resolved[num_resolved].ipv6 = ipv6; resolved[num_resolved].ipv6 = ipv6;
resolved[num_resolved].port = targets[i].port; resolved[num_resolved].port = targets[i].port;
#ifdef HTTPS_ENABLED
resolved[num_resolved].name = name;
name->refs++;
#endif
num_resolved++; num_resolved++;
} }
} }
} }
#ifdef HTTPS_ENABLED
if (name->refs == 0)
free(name);
#endif
freeaddrinfo(res); freeaddrinfo(res);
} }
break; break;
@@ -724,6 +893,9 @@ static int resolve_connect_targets(ConnectTarget *targets,
resolved[num_resolved].is_ipv4 = true; resolved[num_resolved].is_ipv4 = true;
resolved[num_resolved].ipv4 = targets[i].ipv4; resolved[num_resolved].ipv4 = targets[i].ipv4;
resolved[num_resolved].port = targets[i].port; resolved[num_resolved].port = targets[i].port;
#ifdef HTTPS_ENABLED
resolved[num_resolved].name = NULL;
#endif
num_resolved++; num_resolved++;
} }
break; break;
@@ -732,6 +904,9 @@ static int resolve_connect_targets(ConnectTarget *targets,
resolved[num_resolved].is_ipv4 = false; resolved[num_resolved].is_ipv4 = false;
resolved[num_resolved].ipv6 = targets[i].ipv6; resolved[num_resolved].ipv6 = targets[i].ipv6;
resolved[num_resolved].port = targets[i].port; resolved[num_resolved].port = targets[i].port;
#ifdef HTTPS_ENABLED
resolved[num_resolved].name = NULL;
#endif
num_resolved++; num_resolved++;
} }
break; break;
@@ -748,6 +923,11 @@ int socket_connect(SocketManager *sm, int num_targets,
if (sm->num_used == sm->max_used) if (sm->num_used == sm->max_used)
return -1; return -1;
#ifndef HTTPS_ENABLED
if (secure)
return -1;
#endif
AddressAndPort resolved[MAX_CONNECT_TARGETS]; AddressAndPort resolved[MAX_CONNECT_TARGETS];
int num_resolved = resolve_connect_targets( int num_resolved = resolve_connect_targets(
targets, num_targets, resolved, MAX_CONNECT_TARGETS); targets, num_targets, resolved, MAX_CONNECT_TARGETS);
@@ -778,6 +958,10 @@ int socket_connect(SocketManager *sm, int num_targets,
s->state = SOCKET_STATE_PENDING; s->state = SOCKET_STATE_PENDING;
s->sock = NATIVE_SOCKET_INVALID; s->sock = NATIVE_SOCKET_INVALID;
s->user = user; s->user = user;
#ifdef HTTPS_ENABLED
s->server_secure_context = &sm->server_secure_context;
s->ssl = NULL;
#endif
sm->num_used++; sm->num_used++;
return 0; return 0;
} }
+22 -2
View File
@@ -128,6 +128,11 @@ typedef enum {
SOCKET_STATE_DIED, SOCKET_STATE_DIED,
} SocketState; } SocketState;
typedef struct {
int refs;
char data[];
} RegisteredName;
// Internal use only // Internal use only
typedef struct { typedef struct {
union { union {
@@ -136,6 +141,15 @@ typedef struct {
}; };
bool is_ipv4; bool is_ipv4;
Port port; Port port;
#ifdef HTTPS_ENABLED
// When connecting to a peer using TLS, if the address
// was resolved from a registered name, that name is
// used to request the correct certificate once the TCP
// handshake is established, and therefore need to
// store it somewhere until that happens.
RegisteredName *name;
#endif
} AddressAndPort; } AddressAndPort;
// Internal use only // Internal use only
@@ -167,6 +181,12 @@ typedef struct {
AddressAndPort addr; // When num_addr=1 AddressAndPort addr; // When num_addr=1
AddressAndPort *addrs; // Dynamically allocated when num_addr>1 AddressAndPort *addrs; // Dynamically allocated when num_addr>1
}; };
#ifdef HTTPS_ENABLED
ServerSecureContext *server_secure_context;
SSL *ssl;
#endif
} Socket; } Socket;
// Glorified array of sockets. This structure // Glorified array of sockets. This structure
@@ -238,8 +258,8 @@ int socket_manager_listen_tcp(SocketManager *sm,
// and secure connections. // and secure connections.
// Returns 0 on success, -1 on error. // Returns 0 on success, -1 on error.
int socket_manager_listen_tls(SocketManager *sm, int socket_manager_listen_tls(SocketManager *sm,
HTTP_String addr, Port port, HTTP_String cert_file_name, HTTP_String addr, Port port, HTTP_String cert_file,
HTTP_String key_file_name); HTTP_String key_file);
// If the socket manager was configures to accept // If the socket manager was configures to accept
// TLS connections, this adds additional certificates // TLS connections, this adds additional certificates