commit 8443bff77ac2c8a055d9125dc2f621df124b78ef from: Omar Polo date: Mon Jan 25 14:08:31 2021 UTC rework the configless mode: change flags and generate certs 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 + #include #include #include @@ -23,6 +25,9 @@ #include #include +#include +#include + #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)