Commit Diff


commit - 0b00962d37a60c5fb7ab444ddcb8a25dec8754f8
commit + 8443bff77ac2c8a055d9125dc2f621df124b78ef
blob - 359143f73ec0e8d783e0617afef4a68e81f928da
blob + 23ae48231672f58e8faf2390687b85823cb783b1
--- configure
+++ configure
@@ -44,6 +44,7 @@ HAVE_ERR=
 HAVE_EXPLICIT_BZERO=
 HAVE_GETPROGNAME=
 HAVE_LIBTLS=
+HAVE_OPENSSL=
 HAVE_RECALLOCARRAY=
 HAVE_STRLCAT=
 HAVE_STRLCPY=
@@ -59,9 +60,15 @@ BINDIR=
 INSTALL="install"
 
 # try to auto detect CFLAGS and LDFLAGS
-if which pkg-config 2>/dev/null 1>&2 && pkg-config libtls; then
-	CFLAGS=`pkg-config --cflags libtls`
-	LDFLAGS=`pkg-config --libs  libtls`
+if which pkg-config 2>/dev/null 1>&2; then
+	if pkg-config libtls; then
+		CFLAGS="$(pkg-config --cflags libtls)"
+		LDFLAGS="$(pkg-config --libs libtls)"
+	fi
+	if pkg-config openssl; then
+		CFLAGS="${CFLAGS} $(pkg-config --cflags openssl)"
+		LDFLAGS="${LDFLAGS} $(pkg-config --libs openssl)"
+	fi
 fi
 
 # auto detect lex/flex
@@ -229,6 +236,7 @@ runtest err		ERR		|| true
 runtest explicit_bzero	EXPLICIT_BZERO	|| true
 runtest getprogname	GETPROGNAME	|| true
 runtest libtls		LIBTLS		|| true
+runtest openssl		OPENSSL		|| true
 runtest recallocarray	RECALLOCARRAY	|| true
 runtest strlcat		STRLCAT		|| true
 runtest strlcpy		STRLCPY		|| true
@@ -241,6 +249,12 @@ if [ ${HAVE_LIBTLS} -eq 0 ]; then
 	exit 1
 fi
 
+if [ ${HAVE_OPENSSL} -eq 0 ]; then
+	echo "FATAL: openssl not found" 1>&2
+	echo "FATAL: openssl not found" 1>&3
+	exit 1
+fi
+
 # --------
 # write config.h
 
blob - 7b1238e37afbcfbfe0d8ed4fd64d3f936768f082
blob + 356a2991611f6e72ce1781144d099bea30104ecd
--- gmid.c
+++ 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];
@@ -211,9 +216,122 @@ absolutify_path(const char *path)
 		fatal("asprintf: %s", strerror(errno));
 	free(wd);
 	return r;
+}
+
+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,49 +546,43 @@ 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;
+			configless = 1;
 			break;
 
-		case 'C':
-			hosts[0].cert = optarg;
-			break;
-
 		case 'c':
 			config_path = optarg;
 			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) {
blob - 968559bddf67af3f24a4879b61fd3bf1783d0594
blob + 9774257709cf5f822ae47e8efa38494b2811c09c
--- gmid.h
+++ gmid.h
@@ -168,6 +168,10 @@ int		 starts_with(const char*, const char*);
 int		 ends_with(const char*, const char*);
 ssize_t		 filesize(int);
 char		*absolutify_path(const char*);
+void		 gen_certificate(const char*, const char*, const char*);
+void		 mkdirs(const char*);
+char		*data_dir(void);
+void		 load_local_cert(const char*, const char*);
 void		 yyerror(const char*);
 int		 parse_portno(const char*);
 void		 parse_conf(const char*);
blob - 3c6b6e73a05d1b1b260837946773df372ee609e3
blob + a305da35b43b915582c63802783777bdd3ab94e9
--- server.c
+++ server.c
@@ -35,6 +35,9 @@ vhost_lang(struct vhost *v, const char *path)
 	struct location *loc;
 	const char *lang = NULL;
 
+	if (v == NULL)
+		return lang;
+
 	for (loc = v->locations; loc->match != NULL; ++loc) {
 		if (!fnmatch(loc->match, path, 0)) {
 			if (loc->lang != NULL)