From 0d1137f987d78f83e1222ece29a1438a49658d3c Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 24 Sep 2020 17:51:29 -0400 Subject: [PATCH] Implement TLS store Includes hands-free certificate generation and loading --- configure | 3 +- include/config.h | 2 + include/tls.h | 8 +++ src/main.c | 6 ++ src/tls.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++ src/util.c | 7 +- 6 files changed, 198 insertions(+), 4 deletions(-) create mode 100644 include/tls.h create mode 100644 src/tls.c diff --git a/configure b/configure index 0849445..5a5b7a7 100755 --- a/configure +++ b/configure @@ -11,7 +11,8 @@ gmnisrv() { src/main.c \ src/server.c \ src/tls.c \ - src/url.c + src/url.c \ + src/util.c } all="gmnisrv" diff --git a/include/config.h b/include/config.h index 9455c2b..d42a1bf 100644 --- a/include/config.h +++ b/include/config.h @@ -1,6 +1,7 @@ #ifndef GMNISRV_CONFIG #define GMNISRV_CONFIG #include +#include struct gmnisrv_tls { char *store; @@ -11,6 +12,7 @@ struct gmnisrv_tls { struct gmnisrv_host { char *hostname; char *root; + SSL_CTX *ssl_ctx; struct gmnisrv_host *next; }; diff --git a/include/tls.h b/include/tls.h new file mode 100644 index 0000000..bc088ef --- /dev/null +++ b/include/tls.h @@ -0,0 +1,8 @@ +#ifndef GMNISRV_TLS +#define GMNISRV_TLS + +struct gmnisrv_config; + +int gmnisrv_tls_init(struct gmnisrv_config *conf); + +#endif diff --git a/src/main.c b/src/main.c index 6e64965..78d3f9b 100644 --- a/src/main.c +++ b/src/main.c @@ -2,6 +2,7 @@ #include #include "config.h" #include "server.h" +#include "tls.h" static void usage(const char *argv_0) @@ -40,6 +41,11 @@ main(int argc, char **argv) goto exit_conf; } + r = gmnisrv_tls_init(&conf); + if (r != 0) { + goto exit_conf; + } + struct gmnisrv_server server = {0}; r = server_init(&server, &conf); if (r != 0) { diff --git a/src/tls.c b/src/tls.c new file mode 100644 index 0000000..29bfd24 --- /dev/null +++ b/src/tls.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "log.h" +#include "tls.h" +#include "util.h" + +static int +tls_host_gencert(struct gmnisrv_tls *tlsconf, struct gmnisrv_host *host, + const char *crtpath, const char *keypath) +{ + server_log("generating certificate for %s", host->hostname); + + EVP_PKEY *pkey = EVP_PKEY_new(); + assert(pkey); + + BIGNUM *bn = BN_new(); + assert(bn); + BN_set_word(bn, RSA_F4); + + RSA* rsa = RSA_new(); + assert(rsa); + int r = RSA_generate_key_ex(rsa, 4096, bn, NULL); + assert(r == 1); + BN_free(bn); + + EVP_PKEY_assign_RSA(pkey, rsa); + + X509 * x509 = X509_new(); + assert(x509); + + ASN1_INTEGER_set(X509_get_serialNumber(x509), 1); + X509_gmtime_adj(X509_get_notBefore(x509), 0); + X509_gmtime_adj(X509_get_notAfter(x509), 31536000L); // 1 year + X509_set_pubkey(x509, pkey); + + char *organization = "gmnisrv"; + if (tlsconf->organization != NULL) { + organization = tlsconf->organization; + } + X509_NAME *name = X509_get_subject_name(x509); + X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, + (unsigned char *)organization, -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + (unsigned char *)host->hostname, -1, -1, 0); + X509_set_issuer_name(x509, name); + + r = X509_sign(x509, pkey, EVP_sha256()); + assert(r); + + int pfd = open(keypath, O_CREAT | O_WRONLY, 0600); + if (!pfd) { + server_error("opening private key for writing failed: %s", + strerror(errno)); + return 1; + } + + FILE *pf = fdopen(pfd, "w"); + assert(pf); + + r = PEM_write_PrivateKey(pf, pkey, NULL, NULL, 0, NULL, NULL); + fclose(pf); + if (r != 1) { + server_error("writing private key failed"); + return 1; + } + + FILE *xf = fopen(crtpath, "w"); + if (!xf) { + server_error("opening certificate for writing failed: %s", + strerror(errno)); + return 1; + } + r = PEM_write_X509(xf, x509); + fclose(xf); + if (r != 1) { + server_error("writing private key failed"); + return 1; + } + + r = SSL_CTX_use_certificate(host->ssl_ctx, x509); + assert(r == 1); + r = SSL_CTX_use_PrivateKey(host->ssl_ctx, pkey); + assert(r == 1); + return 0; +} + +static int +tls_host_init(struct gmnisrv_tls *tlsconf, struct gmnisrv_host *host) +{ + char crtpath[PATH_MAX + 1]; + char keypath[PATH_MAX + 1]; + snprintf(crtpath, sizeof(crtpath), + "%s/%s.crt", tlsconf->store, host->hostname); + snprintf(keypath, sizeof(keypath), + "%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", + crtpath, strerror(errno)); + return 1; + } else if (!xf) { + goto generate; + } + FILE *kf = fopen(keypath, "r"); + if (!kf && errno != ENOENT) { + server_error("error opening %s for reading: %s", + keypath, strerror(errno)); + fclose(xf); + return 1; + } else if (!kf) { + fclose(xf); + goto generate; + } + + X509 *x509 = PEM_read_X509(xf, NULL, NULL, NULL); + fclose(xf); + if (!x509) { + server_error("error loading certificate from %s", crtpath); + fclose(kf); + return 1; + } + + EVP_PKEY *pkey = PEM_read_PrivateKey(kf, NULL, NULL, NULL); + fclose(kf); + if (!pkey) { + server_error("error loading private key from %s", keypath); + return 1; + } + + int day, sec; + const ASN1_TIME *notAfter = X509_get0_notAfter(x509); + int r = ASN1_TIME_diff(&day, &sec, NULL, notAfter); + assert(r == 1); + if (day < 0 || sec < 0) { + server_log("%s certificate is expired", host->hostname); + goto generate; + } + + r = SSL_CTX_use_certificate(host->ssl_ctx, x509); + assert(r == 1); + r = SSL_CTX_use_PrivateKey(host->ssl_ctx, pkey); + assert(r == 1); + + server_log("loaded certificate for %s", host->hostname); + return 0; + +generate: + return tls_host_gencert(tlsconf, host, crtpath, keypath); +} + +int +gmnisrv_tls_init(struct gmnisrv_config *conf) +{ + int r; + for (struct gmnisrv_host *host = conf->hosts; host; host = host->next) { + r = tls_host_init(&conf->tls, host); + if (r != 0) { + return r; + } + } + return 0; +} diff --git a/src/util.c b/src/util.c index 93c6e91..8cc5502 100644 --- a/src/util.c +++ b/src/util.c @@ -6,6 +6,7 @@ #include #include #include "util.h" +#include static void posix_dirname(char *path, char *dname) @@ -29,11 +30,11 @@ posix_dirname(char *path, char *dname) int mkdirs(char *path, mode_t mode) { - char dname[PATH_MAX + 1]; - posix_dirname(path, dname); - if (strcmp(dname, "/") == 0) { + if (strcmp(path, "/") == 0 || strcmp(path, ".") == 0) { return 0; } + char dname[PATH_MAX + 1]; + posix_dirname(path, dname); if (mkdirs(dname, mode) != 0) { return -1; }