summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xconfigure3
-rw-r--r--include/config.h2
-rw-r--r--include/tls.h8
-rw-r--r--src/main.c6
-rw-r--r--src/tls.c176
-rw-r--r--src/util.c7
6 files changed, 198 insertions, 4 deletions
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 <arpa/inet.h>
+#include <openssl/x509.h>
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 <stdio.h>
#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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#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 <string.h>
#include <sys/stat.h>
#include "util.h"
+#include <stdio.h>
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;
}