Compare commits

...

57 Commits
2.0 ... master

Author SHA1 Message Date
Omar Polo c9ea70a36f regress: add test_ipv6_server 2024-05-29 09:06:45 +00:00
Omar Polo 7c723cf05f regress: add a knob to disable test_ipv6_addr
at least on the CI is failing with "can't connect to ::1:10965:
Address not available" which suggests IPv6 is broken there.
2024-05-29 09:05:06 +00:00
Omar Polo b5dd7091ad typo 2024-05-29 08:58:12 +00:00
Omar Polo 5b549c2805 regress: rename ipv4 test and add another with ipv6 2024-05-29 08:56:10 +00:00
Omar Polo b00f71ba97 iri: add support for raw IPv6 addresses 2024-05-29 08:52:25 +00:00
Omar Polo 6ff8de1f8a gg: unbreak -n 2024-05-29 08:37:39 +00:00
Omar Polo 9f675805d0 regress: run test_ip_addr with host=127.0.0.1 2024-05-29 08:33:36 +00:00
Omar Polo a91b0892bf explain why we disable runtime tests on macos 2024-05-29 08:12:51 +00:00
Omar Polo 610a4666cd regress: use the new gg -q to reduce the blabbering 2024-05-29 08:09:25 +00:00
Omar Polo 2f4926259f gg: add -q to avoid printing "Server says" 2024-05-29 08:08:36 +00:00
Omar Polo cd12ad1132 pretty-print the socket address at configuration parsing time
saves a getnameinfo(NI_NUMERICHOST) at runtime, even if it's pretty
cheap.
2024-05-29 08:03:59 +00:00
Omar Polo b2782022c9 add regress that hit gmid via a raw IPv4 address 2024-05-29 07:54:03 +00:00
Omar Polo 1ef0cd0cdb relax the SNI requirement
There are legitimate cases where SNI can't be used, for example
when connecting via an IPv6 address, so don't rejects those requests.
Instead, fill the requested domain with the address (literal) of
the socket they're connected to and attempt to match on it.

This possibly still incur in a "won't proxy" error if the client
then requests a different hostname.

See the github issue https://github.com/omar-polo/gmid/issues/25
2024-05-29 07:52:13 +00:00
Omar Polo 42e2af25ae github: add workflow to build images for ghcr.io 2024-05-27 15:16:38 +00:00
Omar Polo 89dca7ab54 s/MIN/MINIMUM/g 2024-05-25 17:44:35 +00:00
Omar Polo 359c56ce35 contrib/gmid.service: remove User and Group
May cause weird errors (status=216/GROUP) on some distros, and
running as root is already the default, so remove the two lines.
Reported by and debugged together with leandro del Flug, thanks!
2024-04-27 17:12:09 +00:00
Omar Polo c2dcb5fa6e contrib/gmid.service: start as root by default
Various techniques used by gmid are effective only when the daemon
is started as root.  Strongly suggest to do so by switching the
sample configuration.  This way, provided that a local user is
created as well, the chroot configuration will work out-of-the-box
and the TLS certificates can be readable only by root.
2024-04-27 16:17:37 +00:00
Omar Polo 5d12e6a104 improve the description for -f 2024-04-27 16:10:46 +00:00
Omar Polo 0d8eb9b60c typo: semicolors -> semicolons 2024-04-11 09:42:15 +00:00
Omar Polo 5864f3ce3c set next version 2024-04-04 19:28:14 +00:00
Omar Polo 9536c8ca63 prepare release 2.0.1 2024-04-04 19:16:33 +00:00
Omar Polo 40b71b6861 changelog for 2.0.2 2024-04-04 19:07:04 +00:00
Omar Polo 42235e3fc2 add a test for the config dumping 2024-04-04 13:07:09 +00:00
Omar Polo f53f5e5fe1 fix config dumping (-nn) handling
with the privsep rework the config dumping was unadvertitely broken,
it prints the content of the key itself.
2024-04-04 11:22:06 +00:00
Omar Polo 40ea7b163e use -Werror=implicit-function-declaration for function detection
the previous -Werror triggers too easily: on NixOS for example the
FORTIFY_SOURCE #warning about a missing optimization level breaks all
the checks when using -O0 (which is the default for non-release builds).
2024-04-03 14:03:42 +00:00
Omar Polo be265175c6 fix landlock test
include stddef.h for size_t
2024-04-03 14:01:34 +00:00
Omar Polo 8aba5d8b21 remove dead code 2024-03-12 14:10:36 +00:00
Omar Polo 7c83689428 update mac CI target to sonoma 2024-03-06 10:11:17 +00:00
Omar Polo 248fb833f9 fix `log access path' with chroot
We should open the log file inside the chroot; missed in
4acf495f41.

See https://github.com/omar-polo/gmid/issues/24
2024-03-03 15:43:58 +00:00
Omar Polo 0ed763b03d revert 9f1cce3d0e
we actually should open the log file in the chroot, the bug is in
the code.
2024-03-03 15:42:06 +00:00
Omar Polo 9f1cce3d0e fix log access doc: path is not relative to the chroot
Reported by Colin Henein, thanks!

See https://github.com/omar-polo/gmid/issues/24
2024-03-03 15:27:03 +00:00
Omar Polo 8f543d941e add a note regarding the usage of the bundled libtls
See https://codeberg.org/op/gmid/issues/2
2024-02-02 08:24:28 +00:00
Omar Polo acf244c516 sync changelog 2024-01-30 09:39:07 +00:00
Anna “CyberTailor” 53ad458e22 contrib/vim: fix indent 2024-01-30 09:35:37 +00:00
Omar Polo bb5a25d287 rename the @common_opt macro back to @common
now common is no longer a reserved keyword
2024-01-30 09:31:09 +00:00
Omar Polo f862d389ff turn log styles into strings from yacc point of view
having styles as reserved keywords means that variables / macros can't
be called `common', `condensed', etc...  which is not great and not
obvious either.

Instead, let's keep the log styles as strings and match on them.  This
also allows to have a slightly better error message in case of a typo.

See: https://codeberg.org/op/gmid/issues/1
2024-01-30 09:30:50 +00:00
Omar Polo 574f71f7a3 remove stray space 2024-01-30 09:28:54 +00:00
Omar Polo ebe2e54900 tweak and update freebsd task 2024-01-26 17:02:08 +00:00
Omar Polo ddb089c157 rework the grammar so that ; is accepted after variables and options
See Codeberg issue #1.
2024-01-26 16:54:58 +00:00
Omar Polo 3524375abe add a test that uses @-style macros
See Codeberg issue #1.
2024-01-26 15:34:46 +00:00
Omar Polo fe37d79200 change the default PUBKEY for the verify-release target
doesn't play well with minor releases such as 2.0.1 since for them
I reuse the 2.0 key.
2024-01-24 15:21:19 +00:00
Omar Polo 33a5425235 set next version 2024-01-24 15:13:35 +00:00
Omar Polo cd5e264f9d prepare release 2.0.1 2024-01-24 15:06:08 +00:00
Omar Polo 0baf7066ac changelog for 2.0.1 2024-01-24 15:06:08 +00:00
Omar Polo 83a2644bfb convert remaining code to the imsg getters
Now gmid doesn't touch anymore the internals of the imsg structs.
2024-01-21 19:40:06 +00:00
Omar Polo 4f3b85e6d7 convert the remaining bit of crypto.c to the ibuf_* APIs 2024-01-21 19:30:43 +00:00
Omar Polo 2a822b03ba please macos
for some reason that's not entirely clear to me, __dead doesn't
seem to work on macos, so clang thinks datalen is used un-initialized.

meh
2024-01-21 12:53:01 +00:00
Omar Polo 3f16db6263 update imsg test: gmid now requires the new API too 2024-01-21 12:35:46 +00:00
Omar Polo 561b9f0067 convert crypto.c to the new imsg API 2024-01-21 12:33:33 +00:00
Omar Polo aa2cb5c274 rename ibuf to imsgbuf in crypto
soon we'll be using a struct ibuf and it'll be confusing.
2024-01-21 12:27:42 +00:00
Omar Polo 63e6b0bd0c remove proc_forward_imsg since it's unused 2024-01-21 12:23:28 +00:00
Omar Polo 6dec2ad700 convert most of gmid to the new imsg APIs
Makes parsing and handling of imsgs simpler / clearer.  only crypto.c
is left as-is.
2024-01-21 12:23:28 +00:00
Omar Polo b03e976aa2 convert to use imsg_get_fd()
since proc_forward_imsg() never forwards a file descriptor (it's
never called actually) just use -1 there.
2024-01-21 12:23:16 +00:00
Anna “CyberTailor” 6bce8180d9 configure: fix --mandir handling 2024-01-14 10:30:06 +00:00
Omar Polo 20fa7cded6 ops; pretty big omission among the breaking changes 2024-01-11 16:32:18 +00:00
Omar Polo 2865452c40 typo 2024-01-11 16:26:50 +00:00
Omar Polo 1ee636a45c start the 2.1 release cycle 2024-01-11 16:24:35 +00:00
32 changed files with 455 additions and 284 deletions

View File

@ -1,6 +1,9 @@
# gcc' -Werror=use-after-free gets tripped by vis.c: it sees a use # gcc' -Werror=use-after-free gets tripped by vis.c: it sees a use
# after free where it's not possible and breaks the CI. # after free where it's not possible and breaks the CI.
# seems that inside the CI it's not currently possible to bind to ::1
# so set HAVE_IPV6=no.
linux_amd64_task: linux_amd64_task:
container: container:
image: alpine:latest image: alpine:latest
@ -8,7 +11,7 @@ linux_amd64_task:
- apk add alpine-sdk linux-headers bison libretls-dev libevent-dev - apk add alpine-sdk linux-headers bison libretls-dev libevent-dev
- ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations -Wno-use-after-free' -Werror - ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations -Wno-use-after-free' -Werror
- make - make
- make regress REGRESS_HOST="*" - make regress REGRESS_HOST="*" HAVE_IPV6=no
linux_arm_task: linux_arm_task:
arm_container: arm_container:
@ -17,20 +20,25 @@ linux_arm_task:
- apk add alpine-sdk linux-headers bison libretls-dev libevent-dev - apk add alpine-sdk linux-headers bison libretls-dev libevent-dev
- ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations -Wno-use-after-free' -Werror - ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations -Wno-use-after-free' -Werror
- make - make
- make regress REGRESS_HOST="*" - make regress REGRESS_HOST="*" HAVE_IPV6=no
freebsd_13_task: freebsd_14_task:
freebsd_instance: freebsd_instance:
image_family: freebsd-13-0 image_family: freebsd-14-0
test_script: install_script: pkg install -y libevent libressl pkgconf
- pkg install -y libevent libressl pkgconf script:
- ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations' -Werror - ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations' -Werror
- make - make
- make regress - make regress HAVE_IPV6=no
#
# There are some issues with imsg fd passing on macos at the moment that
# seem to be triggered only in applications that do a heavy use of them,
# like gmid or opensmtpd. Still, keep macos to ensure gmid builds here.
#
mac_task: mac_task:
macos_instance: macos_instance:
image: ghcr.io/cirruslabs/macos-ventura-xcode:latest image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest
test_script: test_script:
- brew install libevent openssl libretls - brew install libevent openssl libretls
- PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig" ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations' -Werror - PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig" ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations' -Werror

33
.github/workflows/alpine-release.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: release docker image
on:
push:
tags:
env:
IMAGE_NAME: "gmid"
jobs:
build:
permissions: write-all
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: build the image
run: docker build -f contrib/Dockerfile -t gmid:alpine .
- name: login to ghcr.io
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: push the image
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME
IMAGE_ID=$(echo $IMAGE_ID | tr A-Z a-z)
# strip git ref prefix from version
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
echo IMAGE_ID=$IMAGE_ID
echo VERSION=$VERSION
docker tag gmid:alpine $IMAGE_ID:$VERSION
docker push $IMAGE_ID:$VERSION

View File

@ -1,3 +1,43 @@
2024-04-03 Omar Polo <op@omarpolo.com>
* configure: improve function checking in the configure
* have/landlock.c: fix landlock test
* gmid.c (main_print_conf): fix config dumping with -nn
2024-03-03 Omar Polo <op@omarpolo.com>
* gmid.c: fix `log access path' with a chroot
2024-01-30 Anna “CyberTailor”
* contrib/vim/indent/gmid.vim: fix indent
2024-01-30 Omar Polo <op@omarpolo.com>
* parse.y: don't make log styles reserved keywords. Unbreaks the
example in the manpage with `common = ...'.
2024-01-26 Omar Polo <op@omarpolo.com>
* parse.y: rework grammar to allow the semicolon after
variables/macros definition and top-level options
2024-01-24 Omar Polo <op@omarpolo.com>
* configure (VERSION): release 2.0.1
2024-01-21 Omar Polo <op@omarpolo.com>
* convert gmid to the new imsg API
2024-01-14 Anna “CyberTailor”
* configure: fix --mandir handling
2024-01-11 Omar Polo <op@omarpolo.com>
* configure (VERSION): release 2.0
2024-01-09 Anna “CyberTailor” 2024-01-09 Anna “CyberTailor”
* contrib/vim/syntax/gmid.vim: update Vim syntax file * contrib/vim/syntax/gmid.vim: update Vim syntax file

View File

@ -131,7 +131,7 @@ y.tab.c: parse.y
lint: lint:
man -Tlint -Wstyle -l gmid.8 gmid.conf.5 gemexp.1 gg.1 titan.1 man -Tlint -Wstyle -l gmid.8 gmid.conf.5 gemexp.1 gg.1 titan.1
PUBKEY = keys/gmid-${VERSION}.pub PUBKEY = keys/gmid-2.0.pub
PRIVKEY = set-PRIVKEY PRIVKEY = set-PRIVKEY
DISTFILES = .cirrus.yml .dockerignore .gitignore ChangeLog LICENSE \ DISTFILES = .cirrus.yml .dockerignore .gitignore ChangeLog LICENSE \
Makefile README.md config.c configure crypto.c dirs.c fcgi.c \ Makefile README.md config.c configure crypto.c dirs.c fcgi.c \

100
config.c
View File

@ -474,19 +474,20 @@ config_crypto_recv_kp(struct conf *conf, struct imsg *imsg)
static struct pki *pki; static struct pki *pki;
uint8_t *d; uint8_t *d;
size_t len; size_t len;
int fd;
/* XXX: check for duplicates */ /* XXX: check for duplicates */
if (imsg->fd == -1) if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("no fd for imsg %d", imsg->hdr.type); fatalx("%s: no fd for imsg %d", __func__, imsg_get_type(imsg));
switch (imsg->hdr.type) { switch (imsg_get_type(imsg)) {
case IMSG_RECONF_CERT: case IMSG_RECONF_CERT:
if (pki != NULL) if (pki != NULL)
fatalx("imsg in wrong order; pki is not NULL"); fatalx("imsg in wrong order; pki is not NULL");
if ((pki = calloc(1, sizeof(*pki))) == NULL) if ((pki = calloc(1, sizeof(*pki))) == NULL)
fatal("calloc"); fatal("calloc");
if (load_file(imsg->fd, &d, &len) == -1) if (load_file(fd, &d, &len) == -1)
fatalx("can't load file"); fatalx("can't load file");
if ((pki->hash = ssl_pubkey_hash(d, len)) == NULL) if ((pki->hash = ssl_pubkey_hash(d, len)) == NULL)
fatalx("failed to compute cert hash"); fatalx("failed to compute cert hash");
@ -496,9 +497,9 @@ config_crypto_recv_kp(struct conf *conf, struct imsg *imsg)
case IMSG_RECONF_KEY: case IMSG_RECONF_KEY:
if (pki == NULL) if (pki == NULL)
fatalx("got key without cert beforehand %d", fatalx("%s: RECONF_KEY: got key without cert",
imsg->hdr.type); __func__);
if (load_file(imsg->fd, &d, &len) == -1) if (load_file(fd, &d, &len) == -1)
fatalx("failed to load private key"); fatalx("failed to load private key");
if ((pki->pkey = ssl_load_pkey(d, len)) == NULL) if ((pki->pkey = ssl_load_pkey(d, len)) == NULL)
fatalx("failed load private key"); fatalx("failed load private key");
@ -529,11 +530,10 @@ config_recv(struct conf *conf, struct imsg *imsg)
struct proxy *proxy; struct proxy *proxy;
struct address *addr; struct address *addr;
uint8_t *d; uint8_t *d;
size_t len, datalen; size_t len;
int fd;
datalen = IMSG_DATA_SIZE(imsg); switch (imsg_get_type(imsg)) {
switch (imsg->hdr.type) {
case IMSG_RECONF_START: case IMSG_RECONF_START:
config_purge(conf); config_purge(conf);
h = NULL; h = NULL;
@ -541,13 +541,14 @@ config_recv(struct conf *conf, struct imsg *imsg)
break; break;
case IMSG_RECONF_LOG_FMT: case IMSG_RECONF_LOG_FMT:
IMSG_SIZE_CHECK(imsg, &conf->log_format); if (imsg_get_data(imsg, &conf->log_format,
memcpy(&conf->log_format, imsg->data, datalen); sizeof(conf->log_format)) == -1)
fatalx("bad length imsg LOG_FMT");
break; break;
case IMSG_RECONF_MIME: case IMSG_RECONF_MIME:
IMSG_SIZE_CHECK(imsg, &m); if (imsg_get_data(imsg, &m, sizeof(m)) == -1)
memcpy(&m, imsg->data, datalen); fatalx("bad length imsg RECONF_MIME");
if (m.mime[sizeof(m.mime) - 1] != '\0' || if (m.mime[sizeof(m.mime) - 1] != '\0' ||
m.ext[sizeof(m.ext) - 1] != '\0') m.ext[sizeof(m.ext) - 1] != '\0')
fatal("received corrupted IMSG_RECONF_MIME"); fatal("received corrupted IMSG_RECONF_MIME");
@ -557,18 +558,19 @@ config_recv(struct conf *conf, struct imsg *imsg)
break; break;
case IMSG_RECONF_PROTOS: case IMSG_RECONF_PROTOS:
IMSG_SIZE_CHECK(imsg, &conf->protos); if (imsg_get_data(imsg, &conf->protos, sizeof(conf->protos))
memcpy(&conf->protos, imsg->data, datalen); == -1)
fatalx("bad length imsg RECONF_PROTOS");
break; break;
case IMSG_RECONF_SOCK: case IMSG_RECONF_SOCK:
addr = xcalloc(1, sizeof(*addr)); addr = xcalloc(1, sizeof(*addr));
IMSG_SIZE_CHECK(imsg, addr); if (imsg_get_data(imsg, addr, sizeof(*addr)) == -1)
memcpy(addr, imsg->data, sizeof(*addr)); fatalx("bad length imsg RECONF_SOCK");
if (imsg->fd == -1) if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("missing socket for IMSG_RECONF_SOCK"); fatalx("missing socket for IMSG_RECONF_SOCK");
addr->conf = conf; addr->conf = conf;
addr->sock = imsg->fd; addr->sock = fd;
event_set(&addr->evsock, addr->sock, EV_READ|EV_PERSIST, event_set(&addr->evsock, addr->sock, EV_READ|EV_PERSIST,
server_accept, addr); server_accept, addr);
if ((addr->ctx = tls_server()) == NULL) if ((addr->ctx = tls_server()) == NULL)
@ -577,16 +579,16 @@ config_recv(struct conf *conf, struct imsg *imsg)
break; break;
case IMSG_RECONF_FCGI: case IMSG_RECONF_FCGI:
IMSG_SIZE_CHECK(imsg, fcgi);
fcgi = xcalloc(1, sizeof(*fcgi)); fcgi = xcalloc(1, sizeof(*fcgi));
memcpy(fcgi, imsg->data, datalen); if (imsg_get_data(imsg, fcgi, sizeof(*fcgi)) == -1)
fatalx("bad length imsg RECONF_FCGI");
log_debug("received fcgi %s", fcgi->path); log_debug("received fcgi %s", fcgi->path);
TAILQ_INSERT_TAIL(&conf->fcgi, fcgi, fcgi); TAILQ_INSERT_TAIL(&conf->fcgi, fcgi, fcgi);
break; break;
case IMSG_RECONF_HOST: case IMSG_RECONF_HOST:
IMSG_SIZE_CHECK(imsg, &vht); if (imsg_get_data(imsg, &vht, sizeof(vht)) == -1)
memcpy(&vht, imsg->data, datalen); fatalx("bad length imsg RECONF_HOST");
vh = new_vhost(); vh = new_vhost();
strlcpy(vh->domain, vht.domain, sizeof(vh->domain)); strlcpy(vh->domain, vht.domain, sizeof(vh->domain));
h = vh; h = vh;
@ -605,9 +607,9 @@ config_recv(struct conf *conf, struct imsg *imsg)
fatalx("recv'd cert without host"); fatalx("recv'd cert without host");
if (h->cert != NULL) if (h->cert != NULL)
fatalx("cert already received"); fatalx("cert already received");
if (imsg->fd == -1) if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("no fd for IMSG_RECONF_CERT"); fatalx("no fd for IMSG_RECONF_CERT");
if (load_file(imsg->fd, &h->cert, &h->certlen) == -1) if (load_file(fd, &h->cert, &h->certlen) == -1)
fatalx("failed to load cert for %s", fatalx("failed to load cert for %s",
h->domain); h->domain);
break; break;
@ -620,9 +622,9 @@ config_recv(struct conf *conf, struct imsg *imsg)
fatalx("recv'd key without host"); fatalx("recv'd key without host");
if (h->key != NULL) if (h->key != NULL)
fatalx("key already received"); fatalx("key already received");
if (imsg->fd == -1) if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("no fd for IMSG_RECONF_KEY"); fatalx("no fd for IMSG_RECONF_KEY");
if (load_file(imsg->fd, &h->key, &h->keylen) == -1) if (load_file(fd, &h->key, &h->keylen) == -1)
fatalx("failed to load key for %s", fatalx("failed to load key for %s",
h->domain); h->domain);
break; break;
@ -633,9 +635,9 @@ config_recv(struct conf *conf, struct imsg *imsg)
fatalx("recv'd ocsp without host"); fatalx("recv'd ocsp without host");
if (h->ocsp != NULL) if (h->ocsp != NULL)
fatalx("ocsp already received"); fatalx("ocsp already received");
if (imsg->fd == -1) if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("no fd for IMSG_RECONF_OCSP"); fatalx("no fd for IMSG_RECONF_OCSP");
if (load_file(imsg->fd, &h->ocsp, &h->ocsplen) == -1) if (load_file(fd, &h->ocsp, &h->ocsplen) == -1)
fatalx("failed to load ocsp for %s", fatalx("failed to load ocsp for %s",
h->domain); h->domain);
break; break;
@ -644,22 +646,22 @@ config_recv(struct conf *conf, struct imsg *imsg)
log_debug("receiving host addr"); log_debug("receiving host addr");
if (h == NULL) if (h == NULL)
fatalx("recv'd host address withouth host"); fatalx("recv'd host address withouth host");
IMSG_SIZE_CHECK(imsg, addr);
addr = xcalloc(1, sizeof(*addr)); addr = xcalloc(1, sizeof(*addr));
memcpy(addr, imsg->data, datalen); if (imsg_get_data(imsg, addr, sizeof(*addr)) == -1)
fatalx("bad length imsg RECONF_HOST_ADDR");
TAILQ_INSERT_TAIL(&h->addrs, addr, addrs); TAILQ_INSERT_TAIL(&h->addrs, addr, addrs);
break; break;
case IMSG_RECONF_LOC: case IMSG_RECONF_LOC:
if (h == NULL) if (h == NULL)
fatalx("recv'd location without host"); fatalx("recv'd location without host");
IMSG_SIZE_CHECK(imsg, loc);
loc = xcalloc(1, sizeof(*loc)); loc = xcalloc(1, sizeof(*loc));
memcpy(loc, imsg->data, datalen); if (imsg_get_data(imsg, loc, sizeof(*loc)) == -1)
fatalx("bad length imsg RECONF_LOC");
TAILQ_INIT(&loc->params); TAILQ_INIT(&loc->params);
if (imsg->fd != -1) { if ((fd = imsg_get_fd(imsg)) != -1) {
if (load_file(imsg->fd, &d, &len) == -1) if (load_file(fd, &d, &len) == -1)
fatal("load_file"); fatal("load_file");
loc->reqca = load_ca(d, len); loc->reqca = load_ca(d, len);
if (loc->reqca == NULL) if (loc->reqca == NULL)
@ -674,18 +676,18 @@ config_recv(struct conf *conf, struct imsg *imsg)
case IMSG_RECONF_ENV: case IMSG_RECONF_ENV:
if (l == NULL) if (l == NULL)
fatalx("recv'd env without location"); fatalx("recv'd env without location");
IMSG_SIZE_CHECK(imsg, env);
env = xcalloc(1, sizeof(*env)); env = xcalloc(1, sizeof(*env));
memcpy(env, imsg->data, datalen); if (imsg_get_data(imsg, env, sizeof(*env)) == -1)
fatalx("bad length imsg RECONF_ENV");
TAILQ_INSERT_TAIL(&l->params, env, envs); TAILQ_INSERT_TAIL(&l->params, env, envs);
break; break;
case IMSG_RECONF_ALIAS: case IMSG_RECONF_ALIAS:
if (h == NULL) if (h == NULL)
fatalx("recv'd alias without host"); fatalx("recv'd alias without host");
IMSG_SIZE_CHECK(imsg, alias);
alias = xcalloc(1, sizeof(*alias)); alias = xcalloc(1, sizeof(*alias));
memcpy(alias, imsg->data, datalen); if (imsg_get_data(imsg, alias, sizeof(*alias)) == -1)
fatalx("bad length imsg RECONF_ALIAS");
TAILQ_INSERT_TAIL(&h->aliases, alias, aliases); TAILQ_INSERT_TAIL(&h->aliases, alias, aliases);
break; break;
@ -693,12 +695,12 @@ config_recv(struct conf *conf, struct imsg *imsg)
log_debug("receiving proxy"); log_debug("receiving proxy");
if (h == NULL) if (h == NULL)
fatalx("recv'd proxy without host"); fatalx("recv'd proxy without host");
IMSG_SIZE_CHECK(imsg, proxy);
proxy = xcalloc(1, sizeof(*proxy)); proxy = xcalloc(1, sizeof(*proxy));
memcpy(proxy, imsg->data, datalen); if (imsg_get_data(imsg, proxy, sizeof(*proxy)) == -1)
fatalx("bad length imsg RECONF_PROXY");
if (imsg->fd != -1) { if ((fd = imsg_get_fd(imsg)) != -1) {
if (load_file(imsg->fd, &d, &len) == -1) if (load_file(fd, &d, &len) == -1)
fatal("load_file"); fatal("load_file");
proxy->reqca = load_ca(d, len); proxy->reqca = load_ca(d, len);
if (proxy->reqca == NULL) if (proxy->reqca == NULL)
@ -716,9 +718,9 @@ config_recv(struct conf *conf, struct imsg *imsg)
fatalx("recv'd proxy cert without proxy"); fatalx("recv'd proxy cert without proxy");
if (p->cert != NULL) if (p->cert != NULL)
fatalx("proxy cert already received"); fatalx("proxy cert already received");
if (imsg->fd == -1) if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("no fd for IMSG_RECONF_PROXY_CERT"); fatalx("no fd for IMSG_RECONF_PROXY_CERT");
if (load_file(imsg->fd, &p->cert, &p->certlen) == -1) if (load_file(fd, &p->cert, &p->certlen) == -1)
fatalx("failed to load cert for proxy %s of %s", fatalx("failed to load cert for proxy %s of %s",
p->host, h->domain); p->host, h->domain);
break; break;
@ -729,9 +731,9 @@ config_recv(struct conf *conf, struct imsg *imsg)
fatalx("recv'd proxy key without proxy"); fatalx("recv'd proxy key without proxy");
if (p->key != NULL) if (p->key != NULL)
fatalx("proxy key already received"); fatalx("proxy key already received");
if (imsg->fd == -1) if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("no fd for IMSG_RECONF_PROXY_KEY"); fatalx("no fd for IMSG_RECONF_PROXY_KEY");
if (load_file(imsg->fd, &p->key, &p->keylen) == -1) if (load_file(fd, &p->key, &p->keylen) == -1)
fatalx("failed to load key for proxy %s of %s", fatalx("failed to load key for proxy %s of %s",
p->host, h->domain); p->host, h->domain);
break; break;

9
configure vendored
View File

@ -19,7 +19,7 @@
set -e set -e
RELEASE=no RELEASE=no
VERSION=2.0 VERSION=2.0.2-current
usage() usage()
{ {
@ -59,6 +59,10 @@ CDIAGFLAGS="${CDIAGFLAGS} -Wsign-compare -Wno-unused-parameter" # -Wshadow
CDIAGFLAGS="${CDIAGFLAGS} -Wno-missing-field-initializers" CDIAGFLAGS="${CDIAGFLAGS} -Wno-missing-field-initializers"
CDIAGFLAGS="${CDIAGFLAGS} -Wno-pointer-sign" CDIAGFLAGS="${CDIAGFLAGS} -Wno-pointer-sign"
# On all OSes except OpenBSD use the bundled one. It may crash at
# runtime otherwise since we depend on the libtls internals for the
# privsep crypto engine.
# See <https://codeberg.org/op/gmid/issues/2>.
LIBTLS=bundled # or system LIBTLS=bundled # or system
if [ "$(uname || true)" = OpenBSD ]; then if [ "$(uname || true)" = OpenBSD ]; then
LIBTLS=system LIBTLS=system
@ -120,6 +124,7 @@ while [ $# -gt 0 ]; do
DISABLE_SANDBOX) DISABLE_SANDBOX="$val" ;; DISABLE_SANDBOX) DISABLE_SANDBOX="$val" ;;
INSTALL) INSTALL="$val" ;; INSTALL) INSTALL="$val" ;;
LDFLAGS) LDFLAGS="$val" ;; LDFLAGS) LDFLAGS="$val" ;;
MANDIR) MANDIR="$val" ;;
PKG_CONFIG) PKG_CONFIG="$val" ;; PKG_CONFIG) PKG_CONFIG="$val" ;;
PREFIX) PREFIX="$val" ;; PREFIX) PREFIX="$val" ;;
SYSCONFDIR) SYSCONFDIR="$val" ;; SYSCONFDIR) SYSCONFDIR="$val" ;;
@ -141,7 +146,7 @@ NEED_OPENBSD_SOURCE=0
NEED_LIBBSD_OPENBSD_VIS=0 NEED_LIBBSD_OPENBSD_VIS=0
COMPATS= COMPATS=
COMP="${CC} ${CFLAGS} -Wno-unused -Werror" COMP="${CC} ${CFLAGS} -Werror=implicit-function-declaration"
# singletest name var extra-cflags extra-libs msg # singletest name var extra-cflags extra-libs msg
singletest() { singletest() {

View File

@ -6,8 +6,6 @@ Wants=network-online.target
[Service] [Service]
Type=simple Type=simple
User=gmid
Group=nobody
ExecStart=/usr/local/bin/gmid -f -c /etc/gmid.conf ExecStart=/usr/local/bin/gmid -f -c /etc/gmid.conf
ExecStop=/bin/kill -TERM $MAINPID ExecStop=/bin/kill -TERM $MAINPID
ExecReload=/bin/kill -HUP $MAINPID ExecReload=/bin/kill -HUP $MAINPID

View File

@ -9,3 +9,5 @@ setlocal indentexpr=
setlocal cindent setlocal cindent
" Just make sure that the comments are not reset as defs would be. " Just make sure that the comments are not reset as defs would be.
setlocal cinkeys-=0# setlocal cinkeys-=0#
" And indentation works correctly without semicolons.
setlocal cinoptions=+0

120
crypto.c
View File

@ -80,7 +80,7 @@ crypto_init(struct privsep *ps, struct privsep_proc *p, void *arg)
static int static int
crypto_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) crypto_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
{ {
switch (imsg->hdr.type) { switch (imsg_get_type(imsg)) {
case IMSG_RECONF_START: case IMSG_RECONF_START:
case IMSG_RECONF_CERT: case IMSG_RECONF_CERT:
case IMSG_RECONF_KEY: case IMSG_RECONF_KEY:
@ -117,25 +117,25 @@ crypto_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
EVP_PKEY *pkey; EVP_PKEY *pkey;
struct imsg_crypto_req req; struct imsg_crypto_req req;
struct imsg_crypto_res res; struct imsg_crypto_res res;
struct ibuf ibuf;
struct iovec iov[2]; struct iovec iov[2];
const void *from; const void *from;
unsigned char *data, *to; unsigned char *to;
size_t datalen; int n, ret, type;
int n, ret;
unsigned int len; unsigned int len;
pid_t pid;
data = imsg->data; if (imsg_get_ibuf(imsg, &ibuf) == -1)
datalen = IMSG_DATA_SIZE(imsg); fatalx("%s: couldn't get an ibuf", __func__);
switch (imsg->hdr.type) { pid = imsg_get_pid(imsg);
switch (type = imsg_get_type(imsg)) {
case IMSG_CRYPTO_RSA_PRIVENC: case IMSG_CRYPTO_RSA_PRIVENC:
case IMSG_CRYPTO_RSA_PRIVDEC: case IMSG_CRYPTO_RSA_PRIVDEC:
if (datalen < sizeof(req)) if (ibuf_get(&ibuf, &req, sizeof(req)) == -1 ||
fatalx("size mismatch for imsg %d", imsg->hdr.type); ibuf_size(&ibuf) != req.flen)
memcpy(&req, data, sizeof(req)); fatalx("size mismatch for imsg %d", type);
if (datalen != sizeof(req) + req.flen) from = ibuf_data(&ibuf);
fatalx("size mismatch for imsg %d", imsg->hdr.type);
from = data + sizeof(req);
if ((pkey = get_pkey(req.hash)) == NULL || if ((pkey = get_pkey(req.hash)) == NULL ||
(rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) (rsa = EVP_PKEY_get1_RSA(pkey)) == NULL)
@ -144,7 +144,7 @@ crypto_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
if ((to = calloc(1, req.tlen)) == NULL) if ((to = calloc(1, req.tlen)) == NULL)
fatal("calloc"); fatal("calloc");
if (imsg->hdr.type == IMSG_CRYPTO_RSA_PRIVENC) if (type == IMSG_CRYPTO_RSA_PRIVENC)
ret = RSA_private_encrypt(req.flen, from, ret = RSA_private_encrypt(req.flen, from,
to, rsa, req.padding); to, rsa, req.padding);
else else
@ -168,12 +168,12 @@ crypto_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
n++; n++;
} }
log_debug("replying to server #%d", imsg->hdr.pid); log_debug("replying to server #%d", pid);
if (proc_composev_imsg(ps, PROC_SERVER, imsg->hdr.pid - 1, if (proc_composev_imsg(ps, PROC_SERVER, pid - 1,
imsg->hdr.type, 0, -1, iov, n) == -1) type, 0, -1, iov, n) == -1)
fatal("proc_composev_imsg"); fatal("proc_composev_imsg");
if (proc_flush_imsg(ps, PROC_SERVER, imsg->hdr.pid - 1) == -1) if (proc_flush_imsg(ps, PROC_SERVER, pid - 1) == -1)
fatal("proc_flush_imsg"); fatal("proc_flush_imsg");
free(to); free(to);
@ -181,12 +181,10 @@ crypto_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
break; break;
case IMSG_CRYPTO_ECDSA_SIGN: case IMSG_CRYPTO_ECDSA_SIGN:
if (datalen < sizeof(req)) if (ibuf_get(&ibuf, &req, sizeof(req)) == -1 ||
fatalx("size mismatch for imsg %d", imsg->hdr.type); ibuf_size(&ibuf) != req.flen)
memcpy(&req, data, sizeof(req)); fatalx("size mismatch for imsg %d", type);
if (datalen != sizeof(req) + req.flen) from = ibuf_data(&ibuf);
fatalx("size mismatch for imsg %d", imsg->hdr.type);
from = data + sizeof(req);
if ((pkey = get_pkey(req.hash)) == NULL || if ((pkey = get_pkey(req.hash)) == NULL ||
(ecdsa = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) (ecdsa = EVP_PKEY_get1_EC_KEY(pkey)) == NULL)
@ -214,12 +212,12 @@ crypto_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
n++; n++;
} }
log_debug("replying to server #%d", imsg->hdr.pid); log_debug("replying to server #%d", pid);
if (proc_composev_imsg(ps, PROC_SERVER, imsg->hdr.pid - 1, if (proc_composev_imsg(ps, PROC_SERVER, pid - 1,
imsg->hdr.type, 0, -1, iov, n) == -1) type, 0, -1, iov, n) == -1)
fatal("proc_composev_imsg"); fatal("proc_composev_imsg");
if (proc_flush_imsg(ps, PROC_SERVER, imsg->hdr.pid - 1) == -1) if (proc_flush_imsg(ps, PROC_SERVER, pid - 1) == -1)
fatal("proc_flush_imsg"); fatal("proc_flush_imsg");
free(to); free(to);
@ -251,14 +249,13 @@ rsae_send_imsg(int flen, const unsigned char *from, unsigned char *to,
struct imsgev *iev; struct imsgev *iev;
struct privsep_proc *p; struct privsep_proc *p;
struct privsep *ps = conf->ps; struct privsep *ps = conf->ps;
struct imsgbuf *ibuf; struct imsgbuf *imsgbuf;
struct imsg imsg; struct imsg imsg;
struct ibuf ibuf;
int ret = 0; int ret = 0;
int n, done = 0; int n, done = 0;
const void *toptr; const void *toptr;
char *hash; char *hash;
unsigned char *data;
size_t datalen;
if ((hash = RSA_get_ex_data(rsa, 0)) == NULL) if ((hash = RSA_get_ex_data(rsa, 0)) == NULL)
return (0); return (0);
@ -289,56 +286,52 @@ rsae_send_imsg(int flen, const unsigned char *from, unsigned char *to,
iev = ps->ps_ievs[PROC_CRYPTO]; iev = ps->ps_ievs[PROC_CRYPTO];
p = iev->proc; p = iev->proc;
ibuf = &iev->ibuf; imsgbuf = &iev->ibuf;
while (!done) { while (!done) {
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) if ((n = imsg_read(imsgbuf)) == -1 && errno != EAGAIN)
fatalx("imsg_read"); fatalx("imsg_read");
if (n == 0) if (n == 0)
fatalx("pipe closed"); fatalx("pipe closed");
while (!done) { while (!done) {
if ((n = imsg_get(ibuf, &imsg)) == -1) if ((n = imsg_get(imsgbuf, &imsg)) == -1)
fatalx("imsg_get error"); fatalx("imsg_get error");
if (n == 0) if (n == 0)
break; break;
#if DEBUG > 1 #if DEBUG > 1
log_debug( log_debug(
"%s: %s %d got imsg %d peerid %d from %s %d", "%s: %s %d got imsg %d id %d from %s %d",
__func__, title, 1, imsg.hdr.type, __func__, title, 1, imsg_get_type(&imsg),
imsg.hdr.peerid, "crypto", imsg.hdr.pid); imsg_get_id(&imsg), "crypto", imsg_get_pid(&imsg));
#endif #endif
if ((p->p_cb)(ibuf->fd, p, &imsg) == 0) { if ((p->p_cb)(imsgbuf->fd, p, &imsg) == 0) {
/* Message was handled by the callback */ /* Message was handled by the callback */
imsg_free(&imsg); imsg_free(&imsg);
continue; continue;
} }
switch (imsg.hdr.type) { switch (imsg_get_type(&imsg)) {
case IMSG_CRYPTO_RSA_PRIVENC: case IMSG_CRYPTO_RSA_PRIVENC:
case IMSG_CRYPTO_RSA_PRIVDEC: case IMSG_CRYPTO_RSA_PRIVDEC:
break; break;
default: default:
fatalx("%s: %s %d got invalid imsg %d" fatalx("%s: %s %d got invalid imsg %d"
" peerid %d from %s %d", " id %d from %s %d",
__func__, "server", ps->ps_instance + 1, __func__, "server", ps->ps_instance + 1,
imsg.hdr.type, imsg.hdr.peerid, imsg_get_type(&imsg), imsg_get_id(&imsg),
"crypto", imsg.hdr.pid); "crypto", imsg_get_pid(&imsg));
} }
data = imsg.data; if (imsg_get_ibuf(&imsg, &ibuf) == -1 ||
datalen = IMSG_DATA_SIZE(&imsg); ibuf_get(&ibuf, &res, sizeof(res)) == -1 ||
if (datalen < sizeof(res)) (int)ibuf_size(&ibuf) != res.ret)
fatalx("size mismatch for imsg %d",
imsg.hdr.type);
memcpy(&res, data, sizeof(res));
if (datalen != sizeof(res) + res.ret)
fatalx("size mismatch for imsg %d", fatalx("size mismatch for imsg %d",
imsg.hdr.type); imsg.hdr.type);
ret = res.ret; ret = res.ret;
toptr = data + sizeof(res); toptr = ibuf_data(&ibuf);
if (res.id != reqid) if (res.id != reqid)
fatalx("invalid id; got %llu, want %llu", fatalx("invalid id; got %llu, want %llu",
@ -399,13 +392,12 @@ ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len,
struct imsgev *iev; struct imsgev *iev;
struct privsep_proc *p; struct privsep_proc *p;
struct privsep *ps = conf->ps; struct privsep *ps = conf->ps;
struct imsgbuf *ibuf; struct imsgbuf *imsgbuf;
struct imsg imsg; struct imsg imsg;
struct ibuf ibuf;
int n, done = 0; int n, done = 0;
const void *toptr; const void *toptr;
char *hash; char *hash;
unsigned char *data;
size_t datalen;
if ((hash = EC_KEY_get_ex_data(eckey, 0)) == NULL) if ((hash = EC_KEY_get_ex_data(eckey, 0)) == NULL)
return (0); return (0);
@ -434,16 +426,16 @@ ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len,
iev = ps->ps_ievs[PROC_CRYPTO]; iev = ps->ps_ievs[PROC_CRYPTO];
p = iev->proc; p = iev->proc;
ibuf = &iev->ibuf; imsgbuf = &iev->ibuf;
while (!done) { while (!done) {
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) if ((n = imsg_read(imsgbuf)) == -1 && errno != EAGAIN)
fatalx("imsg_read"); fatalx("imsg_read");
if (n == 0) if (n == 0)
fatalx("pipe closed"); fatalx("pipe closed");
while (!done) { while (!done) {
if ((n = imsg_get(ibuf, &imsg)) == -1) if ((n = imsg_get(imsgbuf, &imsg)) == -1)
fatalx("imsg_get error"); fatalx("imsg_get error");
if (n == 0) if (n == 0)
break; break;
@ -456,7 +448,8 @@ ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len,
#endif #endif
if (imsg.hdr.type != IMSG_CRYPTO_ECDSA_SIGN && if (imsg.hdr.type != IMSG_CRYPTO_ECDSA_SIGN &&
crypto_dispatch_server(ibuf->fd, p, &imsg) == 0) { crypto_dispatch_server(imsgbuf->fd, p, &imsg)
== 0) {
/* Message was handled by the callback */ /* Message was handled by the callback */
imsg_free(&imsg); imsg_free(&imsg);
continue; continue;
@ -469,16 +462,13 @@ ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len,
imsg.hdr.type, imsg.hdr.peerid, imsg.hdr.type, imsg.hdr.peerid,
"crypto", imsg.hdr.pid); "crypto", imsg.hdr.pid);
data = imsg.data; if (imsg_get_ibuf(&imsg, &ibuf) == -1 ||
datalen = IMSG_DATA_SIZE(&imsg); ibuf_get(&ibuf, &res, sizeof(res)) == -1 ||
if (datalen < sizeof(res)) ibuf_size(&ibuf) != res.len)
fatalx("size mismatch for imsg %d", fatalx("size mismatch for imsg %d",
imsg.hdr.type); imsg.hdr.type);
memcpy(&res, data, sizeof(res));
if (datalen != sizeof(res) + res.len) toptr = ibuf_data(&ibuf);
fatalx("size mismatch for imsg %d",
imsg.hdr.type);
toptr = data + sizeof(res);
if (res.id != reqid) if (res.id != reqid)
fatalx("invalid response id"); fatalx("invalid response id");

4
gg.1
View File

@ -20,7 +20,7 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Bk -words .Bk -words
.Op Fl 23Nn .Op Fl 23Nnq
.Op Fl C Ar cert .Op Fl C Ar cert
.Op Fl d Ar mode .Op Fl d Ar mode
.Op Fl H Ar sni .Op Fl H Ar sni
@ -82,6 +82,8 @@ and
to do the request instead of the ones extracted by the IRI. to do the request instead of the ones extracted by the IRI.
.Ar port .Ar port
is by default 1965. is by default 1965.
.It Fl q
Don't print server error messages to standard error.
.It Fl T Ar seconds .It Fl T Ar seconds
Kill Kill
.Nm .Nm

15
gg.c
View File

@ -41,6 +41,7 @@ int flag3;
int nop; int nop;
int redirects = 5; int redirects = 5;
int timer; int timer;
int quiet;
const char *cert; const char *cert;
const char *key; const char *key;
const char *proxy_host; const char *proxy_host;
@ -308,8 +309,11 @@ get(const char *r)
assert(t != NULL); assert(t != NULL);
if (code < 20 || code >= 30) { if (code < 20 || code >= 30) {
*t = '\0'; *t = '\0';
fprintf(stderr, "Server says: "); if (!quiet) {
safeprint(stderr, buf + 3); /* skip return code */ fprintf(stderr, "Server says: ");
/* skip return code */
safeprint(stderr, buf + 3);
}
} }
t += 2; /* skip \r\n */ t += 2; /* skip \r\n */
len -= t - buf; len -= t - buf;
@ -335,7 +339,7 @@ static void __attribute__((noreturn))
usage(void) usage(void)
{ {
fprintf(stderr, "version: " GG_STRING "\n"); fprintf(stderr, "version: " GG_STRING "\n");
fprintf(stderr, "usage: %s [-23Nn] [-C cert] [-d mode] [-H sni] " fprintf(stderr, "usage: %s [-23Nnq] [-C cert] [-d mode] [-H sni] "
"[-K key] [-P host[:port]]\n", "[-K key] [-P host[:port]]\n",
getprogname()); getprogname());
fprintf(stderr, " [-T seconds] gemini://...\n"); fprintf(stderr, " [-T seconds] gemini://...\n");
@ -385,7 +389,7 @@ main(int argc, char **argv)
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
while ((ch = getopt(argc, argv, "23C:d:H:K:NP:T:")) != -1) { while ((ch = getopt(argc, argv, "23C:d:H:K:nNP:qT:")) != -1) {
switch (ch) { switch (ch) {
case '2': case '2':
flag2 = 1; flag2 = 1;
@ -415,6 +419,9 @@ main(int argc, char **argv)
parse_proxy(optarg); parse_proxy(optarg);
dont_verify_name = 1; dont_verify_name = 1;
break; break;
case 'q':
quiet = 1;
break;
case 'T': case 'T':
timer = strtonum(optarg, 1, 1000, &errstr); timer = strtonum(optarg, 1, 1000, &errstr);
if (errstr != NULL) if (errstr != NULL)

7
gmid.8
View File

@ -1,4 +1,4 @@
.\" Copyright (c) 2021, 2022, 2023 Omar Polo <op@omarpolo.com> .\" Copyright (c) 2021, 2022, 2023, 2024 Omar Polo <op@omarpolo.com>
.\" .\"
.\" Permission to use, copy, modify, and distribute this software for any .\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above .\" purpose with or without fee is hereby granted, provided that the above
@ -11,7 +11,7 @@
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.Dd October 20, 2023 .Dd April 27, 2024
.Dt GMID 8 .Dt GMID 8
.Os .Os
.Sh NAME .Sh NAME
@ -52,7 +52,8 @@ Overrides the definition of
.Ar macro .Ar macro
in the config file if present. in the config file if present.
.It Fl f .It Fl f
Stays and logs on the foreground. Do not daemonize.
Stay and log in the foreground.
.It Fl h , Fl -help .It Fl h , Fl -help
Print the usage and exit. Print the usage and exit.
.It Fl n .It Fl n

12
gmid.c
View File

@ -412,7 +412,7 @@ main_send_logfd(struct conf *conf)
goto done; goto done;
} }
fd = open(conf->log_access, O_WRONLY|O_CREAT|O_APPEND, 0600); fd = open(path, O_WRONLY|O_CREAT|O_APPEND, 0600);
if (fd == -1) if (fd == -1)
log_warn("can't open %s", conf->log_access); log_warn("can't open %s", conf->log_access);
} }
@ -526,7 +526,7 @@ main_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
struct privsep *ps = p->p_ps; struct privsep *ps = p->p_ps;
struct conf *conf = ps->ps_env; struct conf *conf = ps->ps_env;
switch (imsg->hdr.type) { switch (imsg_get_type(imsg)) {
case IMSG_RECONF_DONE: case IMSG_RECONF_DONE:
main_configure_done(conf); main_configure_done(conf);
break; break;
@ -543,7 +543,7 @@ main_dispatch_crypto(int fd, struct privsep_proc *p, struct imsg *imsg)
struct privsep *ps = p->p_ps; struct privsep *ps = p->p_ps;
struct conf *conf = ps->ps_env; struct conf *conf = ps->ps_env;
switch (imsg->hdr.type) { switch (imsg_get_type(imsg)) {
case IMSG_RECONF_DONE: case IMSG_RECONF_DONE:
main_configure_done(conf); main_configure_done(conf);
break; break;
@ -560,7 +560,7 @@ main_dispatch_logger(int fd, struct privsep_proc *p, struct imsg *imsg)
struct privsep *ps = p->p_ps; struct privsep *ps = p->p_ps;
struct conf *conf = ps->ps_env; struct conf *conf = ps->ps_env;
switch (imsg->hdr.type) { switch (imsg_get_type(imsg)) {
case IMSG_RECONF_DONE: case IMSG_RECONF_DONE:
main_configure_done(conf); main_configure_done(conf);
break; break;
@ -605,8 +605,8 @@ main_print_conf(struct conf *conf)
TAILQ_FOREACH(h, &conf->hosts, vhosts) { TAILQ_FOREACH(h, &conf->hosts, vhosts) {
printf("\nserver \"%s\" {\n", h->domain); printf("\nserver \"%s\" {\n", h->domain);
printf(" cert \"%s\"\n", h->cert); printf(" cert \"%s\"\n", h->cert_path);
printf(" key \"%s\"\n", h->key); printf(" key \"%s\"\n", h->key_path);
/* TODO: print locations... */ /* TODO: print locations... */
printf("}\n"); printf("}\n");
} }

View File

@ -11,7 +11,7 @@
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.Dd January 11, 2024 .Dd April 4, 2024
.Dt GMID.CONF 5 .Dt GMID.CONF 5
.Os .Os
.Sh NAME .Sh NAME
@ -384,7 +384,7 @@ The port the server is listening on.
.Dq GEMINI .Dq GEMINI
.It Ev SERVER_SOFTWARE .It Ev SERVER_SOFTWARE
The name and version of the server, i.e. The name and version of the server, i.e.
.Dq gmid/2.0 .Dq gmid/2.0.2
.It Ev REMOTE_USER .It Ev REMOTE_USER
The subject of the client certificate if provided, otherwise unset. The subject of the client certificate if provided, otherwise unset.
.It Ev TLS_CLIENT_ISSUER .It Ev TLS_CLIENT_ISSUER

4
gmid.h
View File

@ -114,6 +114,9 @@ struct address {
socklen_t slen; socklen_t slen;
int16_t port; int16_t port;
/* pretty-printed version of `ss' */
char pp[NI_MAXHOST];
/* used in the server */ /* used in the server */
struct conf *conf; struct conf *conf;
int sock; int sock;
@ -412,7 +415,6 @@ void mark_nonblock(int);
void client_write(struct bufferevent *, void *); void client_write(struct bufferevent *, void *);
int start_reply(struct client*, int, const char*); int start_reply(struct client*, int, const char*);
void client_close(struct client *); void client_close(struct client *);
struct client *client_by_id(int);
void server_accept(int, short, void *); void server_accept(int, short, void *);
void server_init(struct privsep *, struct privsep_proc *, void *); void server_init(struct privsep *, struct privsep_proc *, void *);
int server_configure_done(struct conf *); int server_configure_done(struct conf *);

View File

@ -24,7 +24,8 @@ int
main(void) main(void)
{ {
struct imsgbuf buf; struct imsgbuf buf;
struct imsg imsg;
imsg_init(&buf, -1); imsg_init(&buf, -1);
return 0; return imsg_get_fd(&imsg);
} }

View File

@ -19,6 +19,8 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <stddef.h>
#ifndef landlock_create_ruleset #ifndef landlock_create_ruleset
static inline int static inline int
landlock_create_ruleset(const struct landlock_ruleset_attr *attr, size_t size, landlock_create_ruleset(const struct landlock_ruleset_attr *attr, size_t size,

48
iri.c
View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, 2022 Omar Polo <op@omarpolo.com> * Copyright (c) 2020, 2022, 2024 Omar Polo <op@omarpolo.com>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -177,25 +177,47 @@ parse_port(struct parser *p)
return 1; return 1;
} }
/* TODO: add support for ip-literal and ipv4addr ? */
/* *( unreserved / sub-delims / pct-encoded ) */ /* *( unreserved / sub-delims / pct-encoded ) */
static int static int
parse_authority(struct parser *p) parse_authority(struct parser *p)
{ {
p->parsed->host = p->iri; struct addrinfo hints, *ai;
char *end;
int err;
while (unreserved(*p->iri) if (*p->iri == '[') {
|| sub_delimiters(*p->iri)
|| parse_pct_encoded(p)
|| valid_multibyte_utf8(p)) {
/* normalize the host name. */
if (*p->iri < 0x7F)
*p->iri = tolower(*p->iri);
p->iri++; p->iri++;
} p->parsed->host = p->iri;
if ((end = strchr(p->iri, ']')) == NULL) {
p->err = "invalid IPv6 address";
return 0;
}
*end++ = '\0';
p->iri = end;
if (p->err != NULL) memset(&hints, 0, sizeof(hints));
return 0; hints.ai_flags = AI_NUMERICHOST;
err = getaddrinfo(p->parsed->host, NULL, &hints, &ai);
if (err != 0) {
p->err = "invalid IPv6 address";
return 0;
}
freeaddrinfo(ai);
} else {
p->parsed->host = p->iri;
while (unreserved(*p->iri)
|| sub_delimiters(*p->iri)
|| parse_pct_encoded(p)
|| valid_multibyte_utf8(p)) {
/* normalize the host name. */
if (*p->iri < 0x7F)
*p->iri = tolower(*p->iri);
p->iri++;
}
if (p->err != NULL)
return 0;
}
if (*p->iri == ':') { if (*p->iri == ':') {
*p->iri = '\0'; *p->iri = '\0';

View File

@ -79,24 +79,20 @@ logger_shutdown(void)
static int static int
logger_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) logger_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
{ {
switch (imsg->hdr.type) { switch (imsg_get_type(imsg)) {
case IMSG_LOG_FACILITY: case IMSG_LOG_FACILITY:
if (IMSG_DATA_SIZE(imsg) != sizeof(facility)) if (imsg_get_data(imsg, &facility, sizeof(facility)) == -1)
fatal("corrupted IMSG_LOG_SYSLOG"); fatal("corrupted IMSG_LOG_SYSLOG");
memcpy(&facility, imsg->data, sizeof(facility));
break; break;
case IMSG_LOG_SYSLOG: case IMSG_LOG_SYSLOG:
if (IMSG_DATA_SIZE(imsg) != sizeof(log_to_syslog)) if (imsg_get_data(imsg, &log_to_syslog,
sizeof(log_to_syslog)) == -1)
fatal("corrupted IMSG_LOG_SYSLOG"); fatal("corrupted IMSG_LOG_SYSLOG");
memcpy(&log_to_syslog, imsg->data, sizeof(log_to_syslog));
break; break;
case IMSG_LOG_ACCESS: case IMSG_LOG_ACCESS:
if (logfd != -1) if (logfd != -1)
close(logfd); close(logfd);
logfd = -1; logfd = imsg_get_fd(imsg);
if (imsg->fd != -1)
logfd = imsg->fd;
break; break;
default: default:
return -1; return -1;
@ -109,14 +105,15 @@ static int
logger_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg) logger_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
{ {
char *msg; char *msg;
size_t datalen; size_t datalen = 0;
struct ibuf ibuf;
switch (imsg->hdr.type) { switch (imsg_get_type(imsg)) {
case IMSG_LOG_REQUEST: case IMSG_LOG_REQUEST:
msg = imsg->data; if (imsg_get_ibuf(imsg, &ibuf) == -1 ||
datalen = IMSG_DATA_SIZE(imsg); (datalen = ibuf_size(&ibuf)) == 0)
if (datalen == 0)
fatal("got invalid IMSG_LOG_REQUEST"); fatal("got invalid IMSG_LOG_REQUEST");
msg = ibuf_data(&ibuf);
msg[datalen - 1] = '\0'; msg[datalen - 1] = '\0';
if (logfd != -1) if (logfd != -1)
dprintf(logfd, "%s\n", msg); dprintf(logfd, "%s\n", msg);

103
parse.y
View File

@ -1,7 +1,7 @@
%{ %{
/* /*
* Copyright (c) 2021, 2022, 2023 Omar Polo <op@omarpolo.com> * Copyright (c) 2021-2024 Omar Polo <op@omarpolo.com>
* Copyright (c) 2018 Florian Obser <florian@openbsd.org> * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
* Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
* Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
@ -46,7 +46,7 @@ static struct file {
TAILQ_ENTRY(file) entry; TAILQ_ENTRY(file) entry;
FILE *stream; FILE *stream;
char *name; char *name;
size_t ungetpos; size_t ungetpos;
size_t ungetsize; size_t ungetsize;
u_char *ungetbuf; u_char *ungetbuf;
int eof_reached; int eof_reached;
@ -92,11 +92,9 @@ char *ensure_absolute_path(char*);
int check_block_code(int); int check_block_code(int);
char *check_block_fmt(char*); char *check_block_fmt(char*);
int check_strip_no(int); int check_strip_no(int);
int check_port_num(int);
int check_prefork_num(int); int check_prefork_num(int);
void advance_loc(void); void advance_loc(void);
void advance_proxy(void); void advance_proxy(void);
void parsehp(char *, char **, const char **, const char *);
int fastcgi_conf(const char *, const char *); int fastcgi_conf(const char *, const char *);
void add_param(char *, char *); void add_param(char *, char *);
int getservice(const char *); int getservice(const char *);
@ -125,12 +123,12 @@ typedef struct {
%token ACCESS ALIAS AUTO %token ACCESS ALIAS AUTO
%token BLOCK %token BLOCK
%token CA CERT CHROOT CLIENT COMBINED COMMON CONDENSED %token CA CERT CHROOT CLIENT
%token DEFAULT %token DEFAULT
%token FACILITY FASTCGI FOR_HOST %token FACILITY FASTCGI FOR_HOST
%token INCLUDE INDEX IPV6 %token INCLUDE INDEX IPV6
%token KEY %token KEY
%token LANG LEGACY LISTEN LOCATION LOG %token LANG LISTEN LOCATION LOG
%token OCSP OFF ON %token OCSP OFF ON
%token PARAM PORT PREFORK PROTO PROTOCOLS PROXY %token PARAM PORT PREFORK PROTO PROTOCOLS PROXY
%token RELAY_TO REQUIRE RETURN ROOT %token RELAY_TO REQUIRE RETURN ROOT
@ -150,13 +148,12 @@ typedef struct {
%% %%
conf : /* empty */ conf : /* empty */
| conf include '\n' | conf include nl
| conf '\n' | conf varset nl
| conf varset '\n' | conf option nl
| conf option '\n' | conf vhost nl
| conf vhost '\n' | conf types nl
| conf types '\n' | conf error nl { file->errors++; }
| conf error '\n' { file->errors++; }
; ;
include : INCLUDE STRING { include : INCLUDE STRING {
@ -265,17 +262,18 @@ logopt : ACCESS string {
free(conf->log_access); free(conf->log_access);
conf->log_access = $2; conf->log_access = $2;
} }
| STYLE COMMON { | STYLE string {
conf->log_format = LOG_FORMAT_COMMON; if (!strcmp("combined", $2))
} conf->log_format = LOG_FORMAT_COMBINED;
| STYLE COMBINED { else if (!strcmp("common", $2))
conf->log_format = LOG_FORMAT_COMBINED; conf->log_format = LOG_FORMAT_COMMON;
} else if (!strcmp("condensed", $2))
| STYLE CONDENSED { conf->log_format = LOG_FORMAT_CONDENSED;
conf->log_format = LOG_FORMAT_CONDENSED; else if (!strcmp("legacy", $2))
} conf->log_format = LOG_FORMAT_LEGACY;
| STYLE LEGACY { else
conf->log_format = LOG_FORMAT_LEGACY; yyerror("unknown log style: %s", $2);
free($2);
} }
| SYSLOG FACILITY string { | SYSLOG FACILITY string {
const char *str = $3; const char *str = $3;
@ -617,7 +615,7 @@ mediaopts_l : mediaopts_l mediaoptsl nl
mediaoptsl : STRING { mediaoptsl : STRING {
free(current_media); free(current_media);
current_media = $1; current_media = $1;
} medianames_l optsemicolon } medianames_l
| include | include
; ;
@ -633,17 +631,13 @@ medianamesl : numberstring {
; ;
nl : '\n' optnl nl : '\n' optnl
| ';' optnl
; ;
optnl : '\n' optnl /* zero or more newlines */ optnl : nl
| ';' optnl /* semicolons too */
| /*empty*/ | /*empty*/
; ;
optsemicolon : ';'
|
;
%% %%
static const struct keyword { static const struct keyword {
@ -659,9 +653,6 @@ static const struct keyword {
{"cert", CERT}, {"cert", CERT},
{"chroot", CHROOT}, {"chroot", CHROOT},
{"client", CLIENT}, {"client", CLIENT},
{"combined", COMBINED},
{"common", COMMON},
{"condensed", CONDENSED},
{"default", DEFAULT}, {"default", DEFAULT},
{"facility", FACILITY}, {"facility", FACILITY},
{"fastcgi", FASTCGI}, {"fastcgi", FASTCGI},
@ -671,7 +662,6 @@ static const struct keyword {
{"ipv6", IPV6}, {"ipv6", IPV6},
{"key", KEY}, {"key", KEY},
{"lang", LANG}, {"lang", LANG},
{"legacy", LEGACY},
{"listen", LISTEN}, {"listen", LISTEN},
{"location", LOCATION}, {"location", LOCATION},
{"log", LOG}, {"log", LOG},
@ -1211,16 +1201,6 @@ check_strip_no(int n)
return n; return n;
} }
int
check_port_num(int n)
{
if (n <= 0 || n >= UINT16_MAX)
yyerror("port number is %s: %d",
n <= 0 ? "too small" : "too large",
n);
return n;
}
int int
check_prefork_num(int n) check_prefork_num(int n)
{ {
@ -1243,25 +1223,6 @@ advance_proxy(void)
TAILQ_INSERT_TAIL(&host->proxies, proxy, proxies); TAILQ_INSERT_TAIL(&host->proxies, proxy, proxies);
} }
void
parsehp(char *str, char **host, const char **port, const char *def)
{
char *at;
const char *errstr;
*host = str;
if ((at = strchr(str, ':')) != NULL) {
*at++ = '\0';
*port = at;
} else
*port = def;
strtonum(*port, 1, UINT16_MAX, &errstr);
if (errstr != NULL)
yyerror("port is %s: %s", errstr, *port);
}
int int
fastcgi_conf(const char *path, const char *port) fastcgi_conf(const char *path, const char *port)
{ {
@ -1319,7 +1280,7 @@ getservice(const char *n)
} }
static void static void
add_to_addr_queue(struct addrhead *a, struct addrinfo *ai) add_to_addr_queue(struct addrhead *a, struct addrinfo *ai, const char *pp)
{ {
struct address *addr; struct address *addr;
struct sockaddr_in *sin; struct sockaddr_in *sin;
@ -1345,6 +1306,7 @@ add_to_addr_queue(struct addrhead *a, struct addrinfo *ai)
addr->ai_protocol = ai->ai_protocol; addr->ai_protocol = ai->ai_protocol;
addr->slen = ai->ai_addrlen; addr->slen = ai->ai_addrlen;
memcpy(&addr->ss, ai->ai_addr, ai->ai_addrlen); memcpy(&addr->ss, ai->ai_addr, ai->ai_addrlen);
strlcpy(addr->pp, pp, sizeof(addr->pp));
/* for commodity */ /* for commodity */
switch (addr->ai_family) { switch (addr->ai_family) {
@ -1369,6 +1331,7 @@ void
listen_on(const char *hostname, const char *servname) listen_on(const char *hostname, const char *servname)
{ {
struct addrinfo hints, *res, *res0; struct addrinfo hints, *res, *res0;
char pp[NI_MAXHOST];
int error; int error;
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
@ -1383,8 +1346,14 @@ listen_on(const char *hostname, const char *servname)
} }
for (res = res0; res; res = res->ai_next) { for (res = res0; res; res = res->ai_next) {
add_to_addr_queue(&host->addrs, res); if (getnameinfo(res->ai_addr, res->ai_addrlen, pp, sizeof(pp),
add_to_addr_queue(&conf->addrs, res); NULL, 0, NI_NUMERICHOST) == -1) {
yyerror("getnameinfo failed: %s", strerror(errno));
break;
}
add_to_addr_queue(&host->addrs, res, pp);
add_to_addr_queue(&conf->addrs, res, pp);
} }
freeaddrinfo(res0); freeaddrinfo(res0);

14
proc.c
View File

@ -671,9 +671,9 @@ proc_dispatch(int fd, short event, void *arg)
*/ */
switch (imsg.hdr.type) { switch (imsg.hdr.type) {
case IMSG_CTL_PROCFD: case IMSG_CTL_PROCFD:
IMSG_SIZE_CHECK(&imsg, &pf); if (imsg_get_data(&imsg, &pf, sizeof(pf)))
memcpy(&pf, imsg.data, sizeof(pf)); fatalx("bad length imsg CTL_PROCFD");
proc_accept(ps, imsg.fd, pf.pf_procid, proc_accept(ps, imsg_get_fd(&imsg), pf.pf_procid,
pf.pf_instance); pf.pf_instance);
break; break;
default: default:
@ -799,14 +799,6 @@ proc_composev(struct privsep *ps, enum privsep_procid id,
return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt)); return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt));
} }
int
proc_forward_imsg(struct privsep *ps, struct imsg *imsg,
enum privsep_procid id, int n)
{
return (proc_compose_imsg(ps, id, n, imsg->hdr.type,
imsg->hdr.peerid, imsg->fd, imsg->data, IMSG_DATA_SIZE(imsg)));
}
struct imsgbuf * struct imsgbuf *
proc_ibuf(struct privsep *ps, enum privsep_procid id, int n) proc_ibuf(struct privsep *ps, enum privsep_procid id, int n)
{ {

2
proc.h
View File

@ -114,8 +114,6 @@ int proc_composev_imsg(struct privsep *, enum privsep_procid, int,
uint16_t, uint32_t, int, const struct iovec *, int); uint16_t, uint32_t, int, const struct iovec *, int);
int proc_composev(struct privsep *, enum privsep_procid, int proc_composev(struct privsep *, enum privsep_procid,
uint16_t, const struct iovec *, int); uint16_t, const struct iovec *, int);
int proc_forward_imsg(struct privsep *, struct imsg *,
enum privsep_procid, int);
struct imsgbuf * struct imsgbuf *
proc_ibuf(struct privsep *, enum privsep_procid, int); proc_ibuf(struct privsep *, enum privsep_procid, int);
struct imsgev * struct imsgev *

View File

@ -22,7 +22,7 @@
#include "log.h" #include "log.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MINIMUM(a, b) ((a) < (b) ? (a) : (b))
static const struct timeval handshake_timeout = { 5, 0 }; static const struct timeval handshake_timeout = { 5, 0 };
@ -50,7 +50,7 @@ proxy_tls_readcb(int fd, short event, void *d)
} }
if (bufev->wm_read.high != 0) if (bufev->wm_read.high != 0)
howmuch = MIN(sizeof(buf), bufev->wm_read.high); howmuch = MINIMUM(sizeof(buf), bufev->wm_read.high);
switch (ret = tls_read(c->proxyctx, buf, howmuch)) { switch (ret = tls_read(c->proxyctx, buf, howmuch)) {
case TLS_WANT_POLLIN: case TLS_WANT_POLLIN:

View File

@ -7,6 +7,9 @@ GENCERT_FLAGS=
# host to bind to during regress # host to bind to during regress
REGRESS_HOST = localhost REGRESS_HOST = localhost
# set to no if don't have IPv6 working (need to listen on ::1)
HAVE_IPV6 = yes
DISTFILES = Makefile \ DISTFILES = Makefile \
env \ env \
err \ err \
@ -39,7 +42,7 @@ IRI_OBJS = ${IRI_SRCS:.c=.o} ${REG_COMPATS}
.PHONY: all data clean dist .PHONY: all data clean dist
all: data puny-test iri_test fcgi-test all: data puny-test iri_test fcgi-test
env REGRESS_HOST="${REGRESS_HOST}" ./regress ${TESTS} env HAVE_IPV6="${HAVE_IPV6}" REGRESS_HOST="${REGRESS_HOST}" ./regress ${TESTS}
data: testdata localhost.pem testca.pem valid.crt invalid.pem data: testdata localhost.pem testca.pem valid.crt invalid.pem

View File

@ -162,6 +162,14 @@ main(void)
PASS, PASS,
IRI("gemini", "naïve.omarpolo.com", "", "", "", ""), IRI("gemini", "naïve.omarpolo.com", "", "", "", ""),
"Can percent decode hostnames"); "Can percent decode hostnames");
TEST("gemini://100.64.3.27/",
PASS,
IRI("gemini", "100.64.3.27", "", "", "", ""),
"Accepts IPv4 addresses");
TEST("gemini://[::1]/",
PASS,
IRI("gemini", "::1", "", "", "", ""),
"Accepts IPv6 addresses");
/* path */ /* path */
TEST("gemini://omarpolo.com/foo/bar/baz", TEST("gemini://omarpolo.com/foo/bar/baz",

View File

@ -6,9 +6,12 @@ gemexp="./../gemexp"
gg="./../gg" gg="./../gg"
gmid="./../gmid" gmid="./../gmid"
current_test= current_test=
server_name=
gghost=
run_test() { run_test() {
ggflags= ggflags=
host="$REGRESS_HOST"
port=10965 port=10965
config_common="log syslog off" config_common="log syslog off"
hdr= hdr=
@ -18,9 +21,15 @@ run_test() {
ran_no=$((ran_no + 1)) ran_no=$((ran_no + 1))
current_test=$1 current_test=$1
server_name=localhost
gghost=localhost
rm -f reg.conf rm -f reg.conf
if ! $1; then if [ "$2" = "need_ipv6" -a "$HAVE_IPV6" != "yes" ]; then
echo "$1 skipped (needs HAVE_IPV6='yes')"
return
elif ! $1; then
echo "$1 failed" echo "$1 failed"
failed="$failed $1" failed="$failed $1"
failed_no=$((failed_no + 1)) failed_no=$((failed_no + 1))
@ -58,11 +67,11 @@ gen_config() {
cat <<EOF > reg.conf cat <<EOF > reg.conf
$config_common $config_common
$1 $1
server "localhost" { server "$server_name" {
cert "$PWD/localhost.pem" cert "$PWD/localhost.pem"
key "$PWD/localhost.key" key "$PWD/localhost.key"
root "$PWD/testdata" root "$PWD/testdata"
listen on $REGRESS_HOST port $port listen on $host port $port
$2 $2
} }
EOF EOF
@ -77,7 +86,7 @@ set_proxy() {
server "localhost.local" { server "localhost.local" {
cert "$PWD/localhost.pem" cert "$PWD/localhost.pem"
key "$PWD/localhost.key" key "$PWD/localhost.key"
listen on $REGRESS_HOST port $port listen on $host port $port
proxy { proxy {
relay-to localhost port $port relay-to localhost port $port
$1 $1
@ -108,13 +117,13 @@ setup_simple_test() {
# usage: get <path> # usage: get <path>
# return the body of the request on stdout # return the body of the request on stdout
get() { get() {
$gg -T10 $ggflags "gemini://localhost:10965/$1" || true $gg -q -T10 $ggflags "gemini://$gghost:10965/$1" || true
} }
# usage: head <path> # usage: head <path>
# return the meta response line on stdout # return the meta response line on stdout
head() { head() {
$gg -T10 -d header $ggflags "gemini://localhost:10965/$1" || true $gg -q -T10 -d header $ggflags "gemini://$gghost:10965/$1" || true
} }
# usage: fetch <path> # usage: fetch <path>

View File

@ -20,6 +20,9 @@ fi
run_test test_punycode run_test test_punycode
run_test test_iri run_test test_iri
# Run configuration dumping test.
run_test test_dump_config
if [ "${SKIP_RUNTIME_TESTS:-0}" -eq 1 ]; then if [ "${SKIP_RUNTIME_TESTS:-0}" -eq 1 ]; then
echo echo
echo "======================" echo "======================"
@ -59,6 +62,9 @@ run_test test_proxy_with_certs
# run_test test_unknown_host # XXX: breaks on some distro # run_test test_unknown_host # XXX: breaks on some distro
run_test test_include_mime run_test test_include_mime
run_test test_log_file run_test test_log_file
run_test test_ipv4_addr
run_test test_ipv6_addr need_ipv6
run_test test_ipv6_server need_ipv6
# TODO: add test that uses only a TLSv1.2 or TLSv1.3 # TODO: add test that uses only a TLSv1.2 or TLSv1.3
# TODO: add a test that attempt to serve a non-regular file # TODO: add a test that attempt to serve a non-regular file

View File

@ -8,6 +8,34 @@ test_iri() {
./iri_test ./iri_test
} }
test_dump_config() {
dont_check_server_alive=yes
gen_config '' ''
exp="$(mktemp)"
got="$(mktemp)"
cat <<EOF >$exp
prefork 3
server "localhost" {
cert "$PWD/localhost.pem"
key "$PWD/localhost.key"
}
EOF
$gmid -nn -c reg.conf > $got 2>/dev/null
ret=0
if ! cmp -s "$exp" "$got"; then
echo "config differs!" >&2
diff -u "$exp" "$got" >&2
ret=1
fi
rm "$exp" "$got"
return $ret
}
test_gemexp() { test_gemexp() {
dont_check_server_alive=yes dont_check_server_alive=yes
@ -287,13 +315,15 @@ test_fastcgi_deprecated_syntax() {
test_macro_expansion() { test_macro_expansion() {
cat <<EOF > reg.conf cat <<EOF > reg.conf
pwd = "$PWD" pwd = "$PWD"
common = "lang it; auto index on"
server "localhost" { server "localhost" {
# the quoting of \$ is for sh # the quoting of \$ is for sh
cert \$pwd "/localhost.pem" cert \$pwd "/localhost.pem"
key \$pwd "/localhost.key" key \$pwd "/localhost.key"
root \$pwd "/testdata" root \$pwd "/testdata"
listen on $REGRESS_HOST port $port listen on $host port $port
@common
} }
EOF EOF
@ -305,7 +335,7 @@ EOF
run run
fetch / fetch /
check_reply "20 text/gemini" "# hello world" check_reply "20 text/gemini;lang=it" "# hello world"
} }
test_proxy_relay_to() { test_proxy_relay_to() {
@ -400,3 +430,36 @@ log style legacy'
rm -f log log.edited rm -f log log.edited
return 0 return 0
} }
test_ipv4_addr() {
server_name="*"
host="127.0.0.1"
gghost=127.0.0.1
ggflags=-N
setup_simple_test
fetch /
check_reply "20 text/gemini" "# hello world" || return 1
}
test_ipv6_addr() {
server_name="*"
host="::1"
gghost="[::1]"
ggflags=-N
setup_simple_test
fetch /
check_reply "20 text/gemini" "# hello world" || return 1
}
test_ipv6_server() {
server_name="::1"
host="::1"
gghost="[::1]"
ggflags=-N
setup_simple_test
fetch /
check_reply "20 text/gemini" "# hello world" || return 1
}

View File

@ -31,7 +31,7 @@
#include "log.h" #include "log.h"
#include "proc.h" #include "proc.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MINIMUM(a, b) ((a) < (b) ? (a) : (b))
#ifndef nitems #ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
@ -119,6 +119,9 @@ match_host(struct vhost *v, struct client *c)
if (addr == NULL) if (addr == NULL)
return 0; return 0;
if (*c->domain == '\0')
strlcpy(c->domain, addr->pp, sizeof(c->domain));
if (matches(v->domain, c->domain)) if (matches(v->domain, c->domain))
return 1; return 1;
@ -403,16 +406,19 @@ handle_handshake(int fd, short ev, void *d)
evbuffer_unfreeze(c->bev->output, 1); evbuffer_unfreeze(c->bev->output, 1);
#endif #endif
if ((servname = tls_conn_servername(c->ctx)) == NULL) { if ((servname = tls_conn_servername(c->ctx)) == NULL)
log_debug("handshake: missing SNI"); log_debug("handshake: missing SNI");
goto err;
}
if (!puny_decode(servname, c->domain, sizeof(c->domain), &parse_err)) { if (!puny_decode(servname, c->domain, sizeof(c->domain), &parse_err)) {
log_info("puny_decode: %s", parse_err); log_info("puny_decode: %s", parse_err);
goto err; start_reply(c, BAD_REQUEST, "Wrong/malformed host");
return;
} }
/*
* match_addr will serialize the (matching) address if c->domain
* is empty, so that we can support requests for raw IPv6 address
* that can't have a SNI.
*/
TAILQ_FOREACH(h, &conf->hosts, vhosts) TAILQ_FOREACH(h, &conf->hosts, vhosts)
if (match_host(h, c)) if (match_host(h, c))
break; break;
@ -428,8 +434,7 @@ handle_handshake(int fd, short ev, void *d)
return; return;
} }
err: start_reply(c, BAD_REQUEST, "Wrong/malformed host");
start_reply(c, BAD_REQUEST, "Wrong/malformed host or missing SNI");
} }
static void static void
@ -853,7 +858,7 @@ client_tls_readcb(int fd, short event, void *d)
} }
if (bufev->wm_read.high != 0) if (bufev->wm_read.high != 0)
howmuch = MIN(sizeof(buf), bufev->wm_read.high); howmuch = MINIMUM(sizeof(buf), bufev->wm_read.high);
switch (ret = tls_read(client->ctx, buf, howmuch)) { switch (ret = tls_read(client->ctx, buf, howmuch)) {
case TLS_WANT_POLLIN: case TLS_WANT_POLLIN:
@ -1329,15 +1334,6 @@ server_accept(int sock, short et, void *d)
connected_clients++; connected_clients++;
} }
struct client *
client_by_id(int id)
{
struct client find;
find.id = id;
return SPLAY_FIND(client_tree_id, &clients, &find);
}
static void static void
handle_siginfo(int fd, short ev, void *d) handle_siginfo(int fd, short ev, void *d)
{ {
@ -1496,7 +1492,7 @@ server_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
struct privsep *ps = p->p_ps; struct privsep *ps = p->p_ps;
struct conf *conf = ps->ps_env; struct conf *conf = ps->ps_env;
switch (imsg->hdr.type) { switch (imsg_get_type(imsg)) {
case IMSG_RECONF_START: case IMSG_RECONF_START:
case IMSG_RECONF_LOG_FMT: case IMSG_RECONF_LOG_FMT:
case IMSG_RECONF_MIME: case IMSG_RECONF_MIME:

View File

@ -21,7 +21,7 @@ REPOLOGY_URL = https://repology.org/project/gmid/versions
SUBST = ./subst GITHUB=https://github.com/omar-polo/gmid \ SUBST = ./subst GITHUB=https://github.com/omar-polo/gmid \
SITE=https://ftp.omarpolo.com \ SITE=https://ftp.omarpolo.com \
VERS=2.0 \ VERS=2.0.2 \
PUBKEY=gmid-2.0.pub \ PUBKEY=gmid-2.0.pub \
TREE=https://github.com/omar-polo/gmid/blob/master TREE=https://github.com/omar-polo/gmid/blob/master

View File

@ -1,5 +1,19 @@
# change log # change log
## 2024/04/04 - 2.0.2 “Lady Stardust” bugfix release
- fix `log access path' with `chroot' enabled.
- fix config dumping (-nn).
- rework grammar to allow semicolons after top-level statements.
- don't make the log styles reserved keywords.
- contrib/vim: fixed indent, from Anna “CyberTailor”, thanks!
## 2024/01/24 - 2.0.1 “Lady Stardust” bugfix release
* convert gmid to the new imsg API
* update bundled imsg
* configure: fix --mandir handling; from Anna “CyberTailor”, thanks!
## 2024/01/11 - 2.0 “Lady Stardust” ## 2024/01/11 - 2.0 “Lady Stardust”
### New Features ### New Features
@ -33,6 +47,7 @@
### Breaking Changes ### Breaking Changes
* removed CGI support
* gg now warns when the server doesn't use TLS' close_notify * gg now warns when the server doesn't use TLS' close_notify
* deprecated the global `ipv6' and `port' settings in favour of the per-server `listen on` directive * deprecated the global `ipv6' and `port' settings in favour of the per-server `listen on` directive
* removed the already deprecated config options `mime' and `map' * removed the already deprecated config options `mime' and `map'

View File

@ -46,7 +46,7 @@ $ sudo make install # eventually
A SHA256 file is available. However, it only checks for accidental corruption. You can use signify (gmid-VERS.sha256.sig) and the public key PUBKEY to cryptographically verify the downloaded tarball. The signify public key for the previous and the next release is included in the tarball. A SHA256 file is available. However, it only checks for accidental corruption. You can use signify (gmid-VERS.sha256.sig) and the public key PUBKEY to cryptographically verify the downloaded tarball. The signify public key for the previous and the next release is included in the tarball.
=> SITE/gmid-VERS.sha256 gmid-VERS.sha256 => SITE/gmid-VERS.sha256 gmid-VERS.sha256
=> SITE/gmid-VERS.sha25.sig gmid-VERS.sha256.sig => SITE/gmid-VERS.sha256.sig gmid-VERS.sha256.sig
To verify the signatures with signify(1): To verify the signatures with signify(1):