mirror of
https://git.sr.ht/~sircmpwn/gmnisrv
synced 2024-09-15 06:08:06 -04: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 *store;
|
||||||
char *organization;
|
char *organization;
|
||||||
char *email;
|
char *email;
|
||||||
|
SSL_CTX *ssl_ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct gmnisrv_host {
|
struct gmnisrv_host {
|
||||||
char *hostname;
|
char *hostname;
|
||||||
char *root;
|
char *root;
|
||||||
SSL_CTX *ssl_ctx;
|
|
||||||
X509 *x509;
|
X509 *x509;
|
||||||
EVP_PKEY *pkey;
|
EVP_PKEY *pkey;
|
||||||
struct gmnisrv_host *next;
|
struct gmnisrv_host *next;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#ifndef GMNISRV_SERVER
|
#ifndef GMNISRV_SERVER
|
||||||
#define GMNISRV_SERVER
|
#define GMNISRV_SERVER
|
||||||
|
#include <openssl/ssl.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
@ -8,11 +9,14 @@
|
|||||||
struct gmnisrv_client {
|
struct gmnisrv_client {
|
||||||
struct sockaddr addr;
|
struct sockaddr addr;
|
||||||
socklen_t addrlen;
|
socklen_t addrlen;
|
||||||
|
|
||||||
char buf[GEMINI_MAX_URL + 2];
|
|
||||||
size_t bufln;
|
|
||||||
|
|
||||||
int sockfd;
|
int sockfd;
|
||||||
|
|
||||||
|
SSL *ssl;
|
||||||
|
BIO *bio;
|
||||||
|
|
||||||
|
char buf[GEMINI_MAX_URL + 3];
|
||||||
|
|
||||||
|
struct gmnisrv_host *host;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct gmisrv_config;
|
struct gmisrv_config;
|
||||||
|
@ -4,5 +4,7 @@
|
|||||||
struct gmnisrv_config;
|
struct gmnisrv_config;
|
||||||
|
|
||||||
int gmnisrv_tls_init(struct gmnisrv_config *conf);
|
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
|
#endif
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
static void
|
static void
|
||||||
server_logf(FILE *f, const char *fmt, va_list ap)
|
server_logf(FILE *f, const char *fmt, va_list ap)
|
||||||
{
|
{
|
||||||
fprintf(f, "<server>\t");
|
fprintf(f, "[gmnisrv] ");
|
||||||
vfprintf(f, fmt, ap);
|
vfprintf(f, fmt, ap);
|
||||||
fprintf(f, "\n");
|
fprintf(f, "\n");
|
||||||
}
|
}
|
||||||
@ -16,12 +16,12 @@ server_logf(FILE *f, const char *fmt, va_list ap)
|
|||||||
static void
|
static void
|
||||||
client_logf(FILE *f, struct sockaddr *addr, const char *fmt, va_list ap)
|
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,
|
const char *addrs = inet_ntop(addr->sa_family,
|
||||||
addr->sa_data, abuf, sizeof(abuf));
|
addr->sa_data, abuf, sizeof(abuf));
|
||||||
assert(addrs);
|
assert(addrs);
|
||||||
|
|
||||||
fprintf(f, "%s\t", addrs);
|
fprintf(f, "%s ", addrs);
|
||||||
vfprintf(f, fmt, ap);
|
vfprintf(f, fmt, ap);
|
||||||
fprintf(f, "\n");
|
fprintf(f, "\n");
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "log.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
|
|
||||||
@ -38,11 +39,13 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
int r = load_config(&conf, confpath);
|
int r = load_config(&conf, confpath);
|
||||||
if (r != 0) {
|
if (r != 0) {
|
||||||
|
server_error("Config load failed");
|
||||||
goto exit_conf;
|
goto exit_conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = gmnisrv_tls_init(&conf);
|
r = gmnisrv_tls_init(&conf);
|
||||||
if (r != 0) {
|
if (r != 0) {
|
||||||
|
server_error("TLS initialization failed");
|
||||||
goto exit_conf;
|
goto exit_conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
105
src/server.c
105
src/server.c
@ -1,6 +1,7 @@
|
|||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -9,8 +10,9 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "server.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "tls.h"
|
||||||
|
|
||||||
int
|
int
|
||||||
server_init(struct gmnisrv_server *server, struct gmnisrv_config *conf)
|
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++];
|
struct gmnisrv_client *client = &server->clients[server->nclients++];
|
||||||
client->sockfd = sockfd;
|
client->sockfd = sockfd;
|
||||||
client->addrlen = addrlen;
|
client->addrlen = addrlen;
|
||||||
memcpy(&client->addr, &addr, addrlen);
|
memcpy(&client->addr, &addr, sizeof(addr));
|
||||||
pollfd->fd = sockfd;
|
pollfd->fd = sockfd;
|
||||||
pollfd->events = POLLIN;
|
pollfd->events = POLLIN;
|
||||||
}
|
}
|
||||||
@ -153,16 +155,72 @@ disconnect_client(struct gmnisrv_server *server, struct gmnisrv_client *client)
|
|||||||
--server->nclients;
|
--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
|
static void
|
||||||
client_readable(struct gmnisrv_server *server, struct gmnisrv_client *client)
|
client_readable(struct gmnisrv_server *server, struct gmnisrv_client *client)
|
||||||
{
|
{
|
||||||
// TODO
|
if (!client->ssl && client_init_ssl(server, client) != 0) {
|
||||||
ssize_t n = read(client->sockfd, client->buf, sizeof(client->buf));
|
return;
|
||||||
if (n == 0) {
|
}
|
||||||
|
|
||||||
|
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);
|
disconnect_client(server, client);
|
||||||
return;
|
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
|
static void
|
||||||
@ -173,6 +231,37 @@ client_writable(struct gmnisrv_server *server, struct gmnisrv_client *client)
|
|||||||
(void)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;
|
bool *run;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -196,6 +285,10 @@ server_run(struct gmnisrv_server *server)
|
|||||||
r = sigaction(SIGTERM, &act, &oterm);
|
r = sigaction(SIGTERM, &act, &oterm);
|
||||||
assert(r == 0);
|
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_log("gmnisrv started");
|
||||||
|
|
||||||
server->run = true;
|
server->run = true;
|
||||||
|
32
src/tls.c
32
src/tls.c
@ -2,6 +2,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/x509.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);
|
"%s/%s.key", tlsconf->store, host->hostname);
|
||||||
mkdirs(tlsconf->store, 0755);
|
mkdirs(tlsconf->store, 0755);
|
||||||
|
|
||||||
host->ssl_ctx = SSL_CTX_new(TLS_method());
|
|
||||||
assert(host->ssl_ctx);
|
|
||||||
|
|
||||||
FILE *xf = fopen(crtpath, "r");
|
FILE *xf = fopen(crtpath, "r");
|
||||||
if (!xf && errno != ENOENT) {
|
if (!xf && errno != ENOENT) {
|
||||||
server_error("error opening %s for reading: %s",
|
server_error("error opening %s for reading: %s",
|
||||||
@ -160,6 +158,14 @@ generate:
|
|||||||
int
|
int
|
||||||
gmnisrv_tls_init(struct gmnisrv_config *conf)
|
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;
|
int r;
|
||||||
for (struct gmnisrv_host *host = conf->hosts; host; host = host->next) {
|
for (struct gmnisrv_host *host = conf->hosts; host; host = host->next) {
|
||||||
r = tls_host_init(&conf->tls, host);
|
r = tls_host_init(&conf->tls, host);
|
||||||
@ -167,5 +173,25 @@ gmnisrv_tls_init(struct gmnisrv_config *conf)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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