cg-net: listen on this machine only by default
Finding 1 of the 2026-07-29 security review: hosting bound 0.0.0.0 -- every interface -- with no password and no encryption, so anyone who could reach the machine's port was handed a seat and the game state. New defcustom cg-net-host-address defaults to 127.0.0.1; the wide-open value remains available and its docstring says exactly what choosing it means. This converts "anyone who can reach you" into "someone you deliberately let in", and bounds Finding 4's unauthenticated memory-exhaustion route to local callers along the way. Test cgt-net-host-loopback-default checks both the default value and the actual bound address (process-contact :local). Against the previous code it fails with (void-variable cg-net-host-address); the old bind, captured before the patch: [0 0 0 0 PORT].
This commit is contained in:
parent
9d3ec08d3c
commit
f8873fb8fa
2 changed files with 39 additions and 2 deletions
22
cg-net.el
22
cg-net.el
|
|
@ -60,6 +60,23 @@
|
||||||
"Default TCP port used to host or join a game."
|
"Default TCP port used to host or join a game."
|
||||||
:type 'integer :group 'cg-net)
|
:type 'integer :group 'cg-net)
|
||||||
|
|
||||||
|
(defcustom cg-net-host-address "127.0.0.1"
|
||||||
|
"Address the host's listening socket binds when hosting a game.
|
||||||
|
The default, \"127.0.0.1\", accepts connections only from this
|
||||||
|
machine; remote players reach it through a tunnel they were
|
||||||
|
deliberately given (for example ssh port forwarding), which also
|
||||||
|
encrypts the traffic in transit.
|
||||||
|
|
||||||
|
Setting this to \"0.0.0.0\" listens on every network interface, which
|
||||||
|
means anyone able to reach this machine's port can take a seat: there
|
||||||
|
is no password and no encryption on the wire. That can be a
|
||||||
|
reasonable choice on a trusted LAN, but it is a choice -- make it
|
||||||
|
deliberately."
|
||||||
|
:type '(choice (const :tag "This machine only (recommended)" "127.0.0.1")
|
||||||
|
(const :tag "Every interface (anyone who can reach you)" "0.0.0.0")
|
||||||
|
(string :tag "A specific interface address"))
|
||||||
|
:group 'cg-net)
|
||||||
|
|
||||||
(defvar cg-net-state-functions nil
|
(defvar cg-net-state-functions nil
|
||||||
"Abnormal hook run on a client after the game state is updated.
|
"Abnormal hook run on a client after the game state is updated.
|
||||||
Each function is called with the client's game object.")
|
Each function is called with the client's game object.")
|
||||||
|
|
@ -157,11 +174,12 @@ properties stripped (`cg-net--scrub') before HANDLER sees them."
|
||||||
(and cg-net--host (process-live-p (cg-net-host-server cg-net--host))))
|
(and cg-net--host (process-live-p (cg-net-host-server cg-net--host))))
|
||||||
|
|
||||||
(defun cg-net-host-start (game &optional port)
|
(defun cg-net-host-start (game &optional port)
|
||||||
"Begin hosting GAME on PORT (default `cg-net-port'). Return the server process."
|
"Begin hosting GAME on PORT (default `cg-net-port'). Return the server process.
|
||||||
|
The socket binds `cg-net-host-address' -- by default, this machine only."
|
||||||
(let* ((port (or port cg-net-port))
|
(let* ((port (or port cg-net-port))
|
||||||
(server (make-network-process
|
(server (make-network-process
|
||||||
:name "cg-host" :server t :service port
|
:name "cg-host" :server t :service port
|
||||||
:host "0.0.0.0" :family 'ipv4 :coding 'utf-8
|
:host cg-net-host-address :family 'ipv4 :coding 'utf-8
|
||||||
:log #'cg-net--host-accept)))
|
:log #'cg-net--host-accept)))
|
||||||
(setq cg-net--host (cg-net--host-make :server server :game game))
|
(setq cg-net--host (cg-net--host-make :server server :game game))
|
||||||
server))
|
server))
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,25 @@ host must reach the client bare."
|
||||||
(cg-net-disconnect)
|
(cg-net-disconnect)
|
||||||
(cg-net-host-stop)))))
|
(cg-net-host-stop)))))
|
||||||
|
|
||||||
|
(ert-deftest cgt-net-host-loopback-default ()
|
||||||
|
"Hosting binds to this machine only unless deliberately widened.
|
||||||
|
Finding 1 of the 2026-07-29 review: the default of
|
||||||
|
`cg-net-host-address' is loopback, and the listening socket really
|
||||||
|
binds it -- wider exposure is a setting the user turns on, not a
|
||||||
|
silent default."
|
||||||
|
(should (equal "127.0.0.1" (default-value 'cg-net-host-address)))
|
||||||
|
(condition-case _
|
||||||
|
(delete-process
|
||||||
|
(make-network-process :name "cgt-probe4" :server t :service 0
|
||||||
|
:host "127.0.0.1" :family 'ipv4))
|
||||||
|
(error (ert-skip "TCP not available")))
|
||||||
|
(let* ((hgame (make-instance 'cgt-net-game :env (list :counter 0)))
|
||||||
|
(srv (cg-net-host-start hgame 0)))
|
||||||
|
(unwind-protect
|
||||||
|
(should (equal [127 0 0 1]
|
||||||
|
(substring (process-contact srv :local) 0 4)))
|
||||||
|
(cg-net-host-stop))))
|
||||||
|
|
||||||
;;;; Gaps
|
;;;; Gaps
|
||||||
|
|
||||||
(ert-deftest cgt-gaps-deal ()
|
(ert-deftest cgt-gaps-deal ()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue