]> mop.ddnsfree.com - git repositories - brisk.git/commitdiff
answer the three refusals of the admin check alike
authorMatteo Nastasi <nastasi@alternativeoutput.it>
Wed, 7 Oct 2026 07:23:57 +0000 (09:23 +0200)
committerMatteo Nastasi <nastasi@alternativeoutput.it>
Wed, 7 Oct 2026 07:23:57 +0000 (09:23 +0200)
userauth answered 402 for a session that does not exist, 403 for one that
is not an admin's and 404 for a wrong hash. Whoever reaches the control
channel could tell the three apart and so learn, session by session,
which ones are alive and which belong to an admin.

All three now answer 403 "denied". usermgmt.php, the only client, looks
for 200 and nothing else; test/nonblocking.php does not read the reply.
The reason is written to the daemon log, with the name and the ip of the
user where there is one and never the session. A malformed command still
answers 401: it says nothing about any session.

Checked with the same harness as the previous commit: a good hash answers
200, an unknown session, a non-admin and a wrong hash all answer 403
"denied", each with its own line in the log.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
web/Obj/sac-a-push.phh

index 24cd5dfb5bf6aab32bba10ad781ca62b8f97f301..6863f45ab8d1e83e26fe10e6abef671d98e42816 100644 (file)
@@ -1468,23 +1468,30 @@ class Sac_a_push {
             if (!isset($cmd['sess']) || !isset($cmd['private'])) {
                 return cmd_return(401, 'malformed cmd');
             }
+            /* The reply goes back to whoever reached the control channel, so
+               the three refusals answer alike: telling them apart would let
+               anyone probing sessions learn which ones exist and which belong
+               to an admin. usermgmt.php only looks for 200. The reason goes
+               to the log, without the session, which is a credential, and
+               never with G_alarm_passwd, which is also the password of
+               admin.php and the key of the mail verification hashes.
+               hash_equals: a != stops at the first differing byte. */
             $idx = -1;
-            if (($user = $this->app->get_user($cmd['sess'], $idx)) == FALSE)
-                return cmd_return(402, 'user not found');
-
-            if (($user->flags & USER_FLAG_TY_ADMIN) == 0x00)
-                return cmd_return(403, 'permission denied');
-
-            /* The reply goes back to whoever reached the control channel:
-               it must not carry G_alarm_passwd, which is also the password
-               of admin.php and the key of the mail verification hashes.
-               The failure goes to the log instead, without the session,
-               which is a credential too. hash_equals: a != stops at the
-               first differing byte. */
+            if (($user = $this->app->get_user($cmd['sess'], $idx)) == FALSE) {
+                fprintf(STDERR, "DIRECT: userauth refused: no such session\n");
+                return cmd_return(403, 'denied');
+            }
+
+            if (($user->flags & USER_FLAG_TY_ADMIN) == 0x00) {
+                fprintf(STDERR, "DIRECT: userauth refused: [%s] from [%s] is not an admin\n",
+                        $user->name, $user->ip);
+                return cmd_return(403, 'denied');
+            }
+
             if (!hash_equals(md5($G_alarm_passwd.$user->ip.$user->sess), (string)$cmd['private'])) {
-                fprintf(STDERR, "DIRECT: userauth failed for admin [%s] from [%s]\n",
+                fprintf(STDERR, "DIRECT: userauth refused: wrong hash for admin [%s] from [%s]\n",
                         $user->name, $user->ip);
-                return cmd_return(404, 'authentication failed');
+                return cmd_return(403, 'denied');
             }
 
             return cmd_return(200, 'success');