From: Stefan Sperling Subject: Re: Stop non root users from blocking gotctl(8) reload To: Henry Ford Cc: gameoftrees@openbsd.org Date: Sat, 14 Mar 2026 21:03:14 +0100 On Sat, Mar 14, 2026 at 12:00:19PM -0400, Henry Ford wrote: > 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). Applied, thanks! > 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; > >