aboutsummaryrefslogtreecommitdiff
path: root/gmid.c
diff options
context:
space:
mode:
authorOmar Polo <op@omarpolo.com>2021-01-25 14:08:31 +0000
committerOmar Polo <op@omarpolo.com>2021-01-25 14:08:31 +0000
commit8443bff77ac2c8a055d9125dc2f621df124b78ef (patch)
tree4f0eefd8ba51f10190a7576500b0bf0dac014804 /gmid.c
parent0b00962d37a60c5fb7ab444ddcb8a25dec8754f8 (diff)
rework the configless mode: change flags and generate certs
Diffstat (limited to 'gmid.c')
-rw-r--r--gmid.c186
1 files changed, 159 insertions, 27 deletions
diff --git a/gmid.c b/gmid.c
index 7b1238e..356a299 100644
--- a/gmid.c
+++ b/gmid.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <sys/stat.h>
+
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
@@ -23,6 +25,9 @@
#include <stdarg.h>
#include <string.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+
#include "gmid.h"
struct vhost hosts[HOSTSLEN];
@@ -214,6 +219,119 @@ absolutify_path(const char *path)
}
void
+gen_certificate(const char *host, const char *certpath, const char *keypath)
+{
+ BIGNUM e;
+ EVP_PKEY *pkey;
+ RSA *rsa;
+ X509 *x509;
+ X509_NAME *name;
+ FILE *f;
+ const char *org = "gmid";
+
+ LOGN(NULL, "generating a new certificate for %s in %s (it could take a while)",
+ host, certpath);
+
+ if ((pkey = EVP_PKEY_new()) == NULL)
+ fatal("couldn't create a new private key");
+
+ if ((rsa = RSA_new()) == NULL)
+ fatal("could'nt generate rsa");
+
+ BN_init(&e);
+ BN_set_word(&e, 17);
+ if (!RSA_generate_key_ex(rsa, 4096, &e, NULL))
+ fatal("couldn't generate a rsa key");
+
+ if (!EVP_PKEY_assign_RSA(pkey, rsa))
+ fatal("couldn't assign the key");
+
+ if ((x509 = X509_new()) == NULL)
+ fatal("couldn't generate the X509 certificate");
+
+ ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
+ X509_gmtime_adj(X509_get_notBefore(x509), 0);
+ X509_gmtime_adj(X509_get_notAfter(x509), 315360000L); /* 10 years */
+
+ if (!X509_set_pubkey(x509, pkey))
+ fatal("couldn't set the public key");
+
+ name = X509_get_subject_name(x509);
+ if (!X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, org, -1, -1, 0))
+ fatal("couldn't add N to cert");
+ if (!X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, host, -1, -1, 0))
+ fatal("couldn't add CN to cert");
+ X509_set_issuer_name(x509, name);
+
+ if (!X509_sign(x509, pkey, EVP_sha256()))
+ fatal("couldn't sign the certificate");
+
+ if ((f = fopen(keypath, "w")) == NULL)
+ fatal("fopen(%s): %s", keypath, strerror(errno));
+ if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL))
+ fatal("couldn't write private key");
+ fclose(f);
+
+ if ((f = fopen(certpath, "w")) == NULL)
+ fatal("fopen(%s): %s", certpath, strerror(errno));
+ if (!PEM_write_X509(f, x509))
+ fatal("couldn't write cert");
+ fclose(f);
+
+ X509_free(x509);
+ RSA_free(rsa);
+}
+
+/* XXX: create recursively */
+void
+mkdirs(const char *path)
+{
+ if (mkdir(path, 0755) == -1 && errno != EEXIST)
+ fatal("can't mkdir %s: %s", path, strerror(errno));
+}
+
+/* $XDG_DATA_HOME/gmid */
+char *
+data_dir(void)
+{
+ const char *home, *xdg;
+ char dir[PATH_MAX];
+ char *t;
+
+ if ((xdg = getenv("XDG_DATA_HOME")) == NULL) {
+ if ((home = getenv("HOME")) == NULL)
+ errx(1, "XDG_DATA_HOME and HOME both empty");
+ if (asprintf(&t, "%s/.local/share/gmid", home) == -1)
+ err(1, "asprintf");
+ mkdirs(t);
+ return t;
+ }
+
+ if (asprintf(&t, "%s/gmid", xdg) == -1)
+ err(1, "asprintf");
+ mkdirs(t);
+ return t;
+}
+
+void
+load_local_cert(const char *hostname, const char *dir)
+{
+ char *cert, *key;
+
+ if (asprintf(&cert, "%s/%s.cert.pem", dir, hostname) == -1)
+ errx(1, "asprintf");
+ if (asprintf(&key, "%s/%s.key.pem", dir, hostname) == -1)
+ errx(1, "asprintf");
+
+ if (access(cert, R_OK) == -1 || access(key, R_OK) == -1)
+ gen_certificate(hostname, cert, key);
+
+ hosts[0].cert = cert;
+ hosts[0].key = key;
+ hosts[0].domain = hostname;
+}
+
+void
yyerror(const char *msg)
{
goterror = 1;
@@ -333,7 +451,7 @@ setup_tls(void)
if (tls_config_set_keypair_file(tlsconf, hosts->cert, hosts->key))
fatal("tls_config_set_keypair_file failed");
- for (h = hosts; h->domain != NULL; ++h) {
+ for (h = &hosts[1]; h->domain != NULL; ++h) {
if (tls_config_add_keypair_file(tlsconf, h->cert, h->key) == -1)
fatal("failed to load the keypair (%s, %s)",
h->cert, h->key);
@@ -375,7 +493,7 @@ init_config(void)
for (i = 0; i < HOSTSLEN; ++i)
hosts[i].dirfd = -1;
- conf.foreground = 1;
+ conf.foreground = 0;
conf.port = 1965;
conf.ipv6 = 0;
conf.protos = TLS_PROTOCOL_TLSv1_2 | TLS_PROTOCOL_TLSv1_3;
@@ -419,8 +537,8 @@ void
usage(const char *me)
{
fprintf(stderr,
- "USAGE: %s [-n] [-c config] | [-6fh] [-C cert] [-d root] [-K key] "
- "[-p port] [-x cgi-bin]\n",
+ "USAGE: %s [-n] [-c config] | [-6h] [-d certs-dir] [-H host]"
+ " [-p port] [-x cgi] [dir]",
me);
}
@@ -428,19 +546,16 @@ int
main(int argc, char **argv)
{
int ch, p[2];
- const char *config_path = NULL;
- int conftest = 0;
+ const char *config_path = NULL, *certs_dir = NULL, *hostname = NULL;
+ int conftest = 0, configless = 0;
init_config();
- while ((ch = getopt(argc, argv, "6C:c:d:fhK:np:x:")) != -1) {
+ while ((ch = getopt(argc, argv, "6c:d:H:hnp:x:")) != -1) {
switch (ch) {
case '6':
conf.ipv6 = 1;
- break;
-
- case 'C':
- hosts[0].cert = optarg;
+ configless = 1;
break;
case 'c':
@@ -448,29 +563,26 @@ main(int argc, char **argv)
break;
case 'd':
- free((char*)hosts[0].dir);
- if ((hosts[0].dir = absolutify_path(optarg)) == NULL)
- fatal("absolutify_path");
+ certs_dir = optarg;
+ configless = 1;
break;
- case 'f':
- conf.foreground = 1;
+ case 'H':
+ hostname = optarg;
+ configless = 1;
break;
case 'h':
usage(*argv);
return 0;
- case 'K':
- hosts[0].key = optarg;
- break;
-
case 'n':
conftest = 1;
break;
case 'p':
conf.port = parse_portno(optarg);
+ configless = 1;
break;
case 'x':
@@ -478,6 +590,7 @@ main(int argc, char **argv)
if (*optarg == '/')
optarg++;
hosts[0].cgi = optarg;
+ configless = 1;
break;
default:
@@ -485,17 +598,36 @@ main(int argc, char **argv)
return 1;
}
}
+ argc -= optind;
+ argv += optind;
if (config_path != NULL) {
- if (hosts[0].cert != NULL || hosts[0].key != NULL ||
- hosts[0].dir != NULL)
- fatal("can't specify options in conf mode");
+ if (argc > 0 || configless)
+ fatal("can't specify options is config mode.");
+
parse_conf(config_path);
} else {
- if (hosts[0].cert == NULL || hosts[0].key == NULL ||
- hosts[0].dir == NULL)
- fatal("missing cert, key or root directory to serve");
- hosts[0].domain = "*";
+ conf.foreground = 1;
+
+ if (hostname == NULL)
+ hostname = "localhost";
+ if (certs_dir == NULL)
+ certs_dir = data_dir();
+ load_local_cert(hostname, certs_dir);
+
+ switch (argc) {
+ case 0:
+ hosts[0].dir = ".";
+ break;
+ case 1:
+ hosts[0].dir = argv[0];
+ break;
+ default:
+ usage(getprogname());
+ return 1;
+ }
+
+ LOGN(NULL, "serving %s on port %d", hosts[0].dir, conf.port);
}
if (conftest) {