daemon: set restrictive permission on RPC-server unix domain socket
What changed, and why it matters
Electrum's background daemon runs a local control server using a Unix domain socket. Previously, that socket file was created with permissions 0775, meaning any user in the same group could connect to it and potentially control the wallet. This commit changes the permissions to 0600, so only the wallet's owner can access the socket. The commit itself notes a possible race condition: for a brief moment after the socket is created and before the permissions are tightened, other users might still be able to connect.
Apply the patch. For defense in depth, consider running the daemon with a umask that denies group/other access by default, or use a more controlled socket-creation path that avoids the race window noted in the FIXME comment.
Security signals we found
permission tightening on local RPC socket
acknowledged race condition between socket creation and chmod
local privilege boundary issue (group-writable socket)
no CVE or advisory referenced in commit
Evidence from the diff
The patch modifies electrum/daemon.py in CommandsServer.start(). After aiohttp starts the Unix socket server, it now calls os_chmod(sockpath, stat.S_IREAD | stat.S_IWRITE) to set mode 0600. The old default was 0775 (owner/group read/write/execute). The inline comment acknowledges a race window because aiohttp creates the socket before this chmod runs. The change reduces exposure from same-group users to owner-only, mitigating local privilege abuse against the JSON-RPC control interface.
Changed components
electrum/daemon.pyCommandsServer Unix domain socketInspect captured patch +8 / −1
diff --git a/electrum/daemon.py b/electrum/daemon.py
index a8fc2ce..12c7fbd 100644
--- a/electrum/daemon.py
+++ b/electrum/daemon.py
@@ -34,6 +34,7 @@ from typing import Dict, Optional, Tuple, Callable, Union, Sequence, Mapping, TY
from base64 import b64decode, b64encode
import json
import socket
+import stat
import aiohttp
from aiohttp import web, client_exceptions
@@ -43,7 +44,7 @@ from . import util
from .network import Network
from .util import (
json_decode, to_bytes, to_string, profiler, standardize_path, constant_time_compare, InvalidPassword,
- log_exceptions, randrange, OldTaskGroup, UserFacingException, JsonRPCError
+ log_exceptions, randrange, OldTaskGroup, UserFacingException, JsonRPCError, os_chmod
)
from .wallet import Wallet, Abstract_Wallet
from .storage import WalletStorage
@@ -327,6 +328,12 @@ class CommandsServer(AuthenticatedServer):
await site.start()
except Exception as e:
raise Exception(f"failed to start CommandsServer at {self._socket_config_str()}. got exc: {e!r}") from None
+ # now server has started.
+ if self.socktype == 'unix':
+ # set restrictive permissions on unix domain socket.
+ # FIXME race? we are late. should set this during socket-file creation but aiohttp API does not let us.
+ os_chmod(self.sockpath, stat.S_IREAD | stat.S_IWRITE)
+ # write server conn details into lockfile fd
if self.socktype == 'unix':
addr = self.sockpath
elif self.socktype == 'tcp':
Why this scored 65/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.