mirror of
https://git.sr.ht/~sircmpwn/gmnisrv
synced 2024-11-03 06:07:17 -05:00
Implement TLS exchange with clients
This probably leaves a bit to be desired tbh
This commit is contained in:
parent
7af04ea471
commit
fa69887e52
@ -7,12 +7,12 @@ struct gmnisrv_tls {
|
||||
char *store;
|
||||
char *organization;
|
||||
char *email;
|
||||
SSL_CTX *ssl_ctx;
|
||||
};
|
||||
|
||||
struct gmnisrv_host {
|
||||
char *hostname;
|
||||
char *root;
|
||||
SSL_CTX *ssl_ctx;
|
||||
X509 *x509;
|
||||
EVP_PKEY *pkey;
|
||||
struct gmnisrv_host *next;
|
||||
|
@ -1,5 +1,6 @@
|
||||
#ifndef GMNISRV_SERVER
|
||||
#define GMNISRV_SERVER
|
||||
#include <openssl/ssl.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
@ -8,11 +9,14 @@
|
||||
struct gmnisrv_client {
|
||||
struct sockaddr addr;
|
||||
socklen_t addrlen;
|
||||
|
||||
char buf[GEMINI_MAX_URL + 2];
|
||||
size_t bufln;
|
||||
|
||||
int sockfd;
|
||||
|
||||
SSL *ssl;
|
||||
BIO *bio;
|
||||
|
||||
char buf[GEMINI_MAX_URL + 3];
|
||||
|
||||
struct gmnisrv_host *host;
|
||||
};
|
||||
|
||||
struct gmisrv_config;
|
||||
|
@ -4,5 +4,7 @@
|
||||
struct gmnisrv_config;
|
||||
|
||||
int gmnisrv_tls_init(struct gmnisrv_config *conf);
|
||||
SSL *gmnisrv_tls_get_ssl(struct gmnisrv_config *conf, int fd);
|
||||
void gmnisrv_tls_set_host(SSL *ssl, struct gmnisrv_host *host);
|
||||
|
||||
#endif
|
||||
|
@ -8,7 +8,7 @@
|
||||
static void
|
||||
server_logf(FILE *f, const char *fmt, va_list ap)
|
||||
{
|
||||
fprintf(f, "<server>\t");
|
||||
fprintf(f, "[gmnisrv] ");
|
||||
vfprintf(f, fmt, ap);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
@ -16,12 +16,12 @@ server_logf(FILE *f, const char *fmt, va_list ap)
|
||||
static void
|
||||
client_logf(FILE *f, struct sockaddr *addr, const char *fmt, va_list ap)
|
||||
{
|
||||
char abuf[INET6_ADDRSTRLEN];
|
||||
char abuf[INET6_ADDRSTRLEN + 1];
|
||||
const char *addrs = inet_ntop(addr->sa_family,
|
||||
addr->sa_data, abuf, sizeof(abuf));
|
||||
assert(addrs);
|
||||
|
||||
fprintf(f, "%s\t", addrs);
|
||||
fprintf(f, "%s ", addrs);
|
||||
vfprintf(f, fmt, ap);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "server.h"
|
||||
#include "tls.h"
|
||||
|
||||
@ -38,11 +39,13 @@ main(int argc, char **argv)
|
||||
|
||||
int r = load_config(&conf, confpath);
|
||||
if (r != 0) {
|
||||
server_error("Config load failed");
|
||||
goto exit_conf;
|
||||
}
|
||||
|
||||
r = gmnisrv_tls_init(&conf);
|
||||
if (r != 0) {
|
||||
server_error("TLS initialization failed");
|
||||
goto exit_conf;
|
||||
}
|
||||
|
||||
|
105
src/server.c
105
src/server.c
@ -1,6 +1,7 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <openssl/err.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
@ -9,8 +10,9 @@
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include "config.h"
|
||||
#include "server.h"
|
||||
#include "log.h"
|
||||
#include "server.h"
|
||||
#include "tls.h"
|
||||
|
||||
int
|
||||
server_init(struct gmnisrv_server *server, struct gmnisrv_config *conf)
|
||||
@ -135,7 +137,7 @@ accept_client(struct gmnisrv_server *server, int fd)
|
||||
struct gmnisrv_client *client = &server->clients[server->nclients++];
|
||||
client->sockfd = sockfd;
|
||||
client->addrlen = addrlen;
|
||||
memcpy(&client->addr, &addr, addrlen);
|
||||
memcpy(&client->addr, &addr, sizeof(addr));
|
||||
pollfd->fd = sockfd;
|
||||
pollfd->events = POLLIN;
|
||||
}
|
||||
@ -153,16 +155,72 @@ disconnect_client(struct gmnisrv_server *server, struct gmnisrv_client *client)
|
||||
--server->nclients;
|
||||
}
|
||||
|
||||
static int
|
||||
client_init_ssl(struct gmnisrv_server *server, struct gmnisrv_client *client)
|
||||
{
|
||||
// TODO: Re-work this to use a non-blocking bio
|
||||
client->ssl = gmnisrv_tls_get_ssl(server->conf, client->sockfd);
|
||||
if (!client->ssl) {
|
||||
client_error(&client->addr,
|
||||
"unable to initialize SSL, disconnecting");
|
||||
disconnect_client(server, client);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int r = SSL_accept(client->ssl);
|
||||
if (r != 1) {
|
||||
r = SSL_get_error(client->ssl, r);
|
||||
client_error(&client->addr, "SSL accept error %s, disconnecting",
|
||||
ERR_error_string(r, NULL));
|
||||
disconnect_client(server, client);
|
||||
return 1;
|
||||
}
|
||||
|
||||
BIO *sbio = BIO_new(BIO_f_ssl());
|
||||
BIO_set_ssl(sbio, client->ssl, 0);
|
||||
client->bio = BIO_new(BIO_f_buffer());
|
||||
BIO_push(client->bio, sbio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
client_readable(struct gmnisrv_server *server, struct gmnisrv_client *client)
|
||||
{
|
||||
// TODO
|
||||
ssize_t n = read(client->sockfd, client->buf, sizeof(client->buf));
|
||||
if (n == 0) {
|
||||
if (!client->ssl && client_init_ssl(server, client) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int r = BIO_gets(client->bio, client->buf, sizeof(client->buf));
|
||||
if (r <= 0) {
|
||||
r = SSL_get_error(client->ssl, r);
|
||||
if (r == SSL_ERROR_WANT_READ) {
|
||||
return;
|
||||
}
|
||||
client_error(&client->addr, "SSL read error %s, disconnecting",
|
||||
ERR_error_string(r, NULL));
|
||||
disconnect_client(server, client);
|
||||
return;
|
||||
}
|
||||
(void)server;
|
||||
client->buf[r] = '\0';
|
||||
|
||||
if (!client->host) {
|
||||
// TODO: We can do a friendly disconnect at this point
|
||||
client_error(&client->addr,
|
||||
"client did not perform SNI, disconnecting");
|
||||
disconnect_client(server, client);
|
||||
return;
|
||||
}
|
||||
|
||||
char *newline = strstr(client->buf, "\r\n");
|
||||
if (!newline) {
|
||||
// TODO: We can do a friendly disconnect at this point
|
||||
client_error(&client->addr, "protocol error, disconnecting");
|
||||
disconnect_client(server, client);
|
||||
return;
|
||||
}
|
||||
*newline = 0;
|
||||
|
||||
client_log(&client->addr, "%s", client->buf);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -173,6 +231,37 @@ client_writable(struct gmnisrv_server *server, struct gmnisrv_client *client)
|
||||
(void)client;
|
||||
}
|
||||
|
||||
static long
|
||||
sni_callback(SSL *ssl, int *al, void *arg)
|
||||
{
|
||||
(void)al;
|
||||
struct gmnisrv_server *server = (struct gmnisrv_server *)arg;
|
||||
struct gmnisrv_client *client;
|
||||
for (size_t i = 0; i < server->nclients; ++i) {
|
||||
client = &server->clients[i];
|
||||
if (client->ssl == ssl) {
|
||||
break;
|
||||
}
|
||||
client = NULL;
|
||||
}
|
||||
|
||||
if (!client) {
|
||||
return SSL_TLSEXT_ERR_NOACK;
|
||||
}
|
||||
|
||||
const char *hostname = SSL_get_servername(client->ssl,
|
||||
SSL_get_servername_type(client->ssl));
|
||||
struct gmnisrv_host *host = gmnisrv_config_get_host(
|
||||
server->conf, hostname);
|
||||
if (!host) {
|
||||
return SSL_TLSEXT_ERR_NOACK;
|
||||
}
|
||||
|
||||
client->host = host;
|
||||
gmnisrv_tls_set_host(client->ssl, client->host);
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
bool *run;
|
||||
|
||||
static void
|
||||
@ -196,6 +285,10 @@ server_run(struct gmnisrv_server *server)
|
||||
r = sigaction(SIGTERM, &act, &oterm);
|
||||
assert(r == 0);
|
||||
|
||||
SSL_CTX_set_tlsext_servername_arg(server->conf->tls.ssl_ctx, server);
|
||||
SSL_CTX_set_tlsext_servername_callback(
|
||||
server->conf->tls.ssl_ctx, sni_callback);
|
||||
|
||||
server_log("gmnisrv started");
|
||||
|
||||
server->run = true;
|
||||
|
32
src/tls.c
32
src/tls.c
@ -2,6 +2,7 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509.h>
|
||||
@ -102,9 +103,6 @@ tls_host_init(struct gmnisrv_tls *tlsconf, struct gmnisrv_host *host)
|
||||
"%s/%s.key", tlsconf->store, host->hostname);
|
||||
mkdirs(tlsconf->store, 0755);
|
||||
|
||||
host->ssl_ctx = SSL_CTX_new(TLS_method());
|
||||
assert(host->ssl_ctx);
|
||||
|
||||
FILE *xf = fopen(crtpath, "r");
|
||||
if (!xf && errno != ENOENT) {
|
||||
server_error("error opening %s for reading: %s",
|
||||
@ -160,6 +158,14 @@ generate:
|
||||
int
|
||||
gmnisrv_tls_init(struct gmnisrv_config *conf)
|
||||
{
|
||||
SSL_load_error_strings();
|
||||
ERR_load_crypto_strings();
|
||||
|
||||
conf->tls.ssl_ctx = SSL_CTX_new(TLS_method());
|
||||
assert(conf->tls.ssl_ctx);
|
||||
|
||||
SSL_CTX_set_tlsext_servername_callback(conf->tls.ssl_ctx, NULL);
|
||||
|
||||
int r;
|
||||
for (struct gmnisrv_host *host = conf->hosts; host; host = host->next) {
|
||||
r = tls_host_init(&conf->tls, host);
|
||||
@ -167,5 +173,25 @@ gmnisrv_tls_init(struct gmnisrv_config *conf)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SSL *
|
||||
gmnisrv_tls_get_ssl(struct gmnisrv_config *conf, int fd)
|
||||
{
|
||||
SSL *ssl = SSL_new(conf->tls.ssl_ctx);
|
||||
if (!ssl) {
|
||||
return NULL;
|
||||
}
|
||||
int r = SSL_set_fd(ssl, fd);
|
||||
assert(r == 1);
|
||||
return ssl;
|
||||
}
|
||||
|
||||
void
|
||||
gmnisrv_tls_set_host(SSL *ssl, struct gmnisrv_host *host)
|
||||
{
|
||||
SSL_use_certificate(ssl, host->x509);
|
||||
SSL_use_PrivateKey(ssl, host->pkey);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user