"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Henry Ford <henryfordkjv@gmail.com>
Subject:
Stop non root users from blocking gotctl(8) reload
To:
gameoftrees@openbsd.org
Date:
Sat, 14 Mar 2026 12:00:19 -0400

Download raw body.

Thread
A gotctl(8) reload request is composed of two messages: a GOTD_IMSG_RELOAD_SECRETS
message, optionally with a secrets file, and a GOTD_IMSG_RELOAD message, with a
config file. If no secrets file is specified, gotctl(8) will still send a
GOTD_IMSG_RELOAD_SECRETS message with no payload to indicate this.

If a GOTD_IMSG_RELOAD message is sent by a non root user, then gotd(8) will deny
the request. However, gotd(8) will accept a GOTD_IMSG_RELOAD_SECRETS message
from any client regardless of their uid.

After receiving a GOTD_IMSG_RELOAD_SECRETS message gotd(8) will not accept
another such message until it receives a GOTD_IMSG_RELOAD message to complete
the reload request.

Thus, a non root user can block any future gotctl(8) reload requests from
succeeding by sending a GOTD_IMSG_RELOAD_SECRETS message to gotd(8).

You can reproduce this by running the following commands with gotd(8) running:
$ gotctl reload -c /dev/null
gotctl: reload: Operation not permitted
# gotctl reload
gotctl: received unexpected privsep message

To prevent this we can just have gotd(8) reject GOTD_IMSG_RELOAD_SECRETS
requests from non root users. In the patch at the bottom of this email I
do this. I chose to make the error message say "reload" instead of
"reload secrets" because this is what users will see if they try to run
gotctl(8) reload when they are not root.

A user with root access can still block gotctl(8) reload requests from
succeeding with this method, but they could also just SIGSTOP gotd(8).

diff /home/user/ext/got
path + /home/user/ext/got
commit - 7c0bf6cc9b78f83c42c53d38c9ecacb7420c5e0f
blob - 749adfaf1adfe968546d819330f3dbcfa89666b6
file + gotd/gotd.c
--- gotd/gotd.c
+++ gotd/gotd.c
@@ -983,6 +987,10 @@ gotd_request(int fd, short events, void *arg)
 			err = stop_gotd(client);
 			break;
 		case GOTD_IMSG_RELOAD_SECRETS:
+			if (client->euid != 0) {
+				err = got_error_set_errno(EPERM, "reload");
+				break;
+			}
 			if (have_reload_secrets) {
 				err = got_error(GOT_ERR_PRIVSEP_MSG);
 				break;