aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/transport/webrtc/admin.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/transport/webrtc/admin.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/transport/webrtc/admin.py27
1 files changed, 26 insertions, 1 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc/admin.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc/admin.py
index f42d8e8..daaee62 100644
--- a/packages/meshbay-node/src/meshbay_node/transport/webrtc/admin.py
+++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc/admin.py
@@ -5,6 +5,7 @@ import base64
import os
import time
+import msgpack
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
from meshbay_common import MNP_VERSION
from meshbay_common.adminop import (
@@ -42,6 +43,16 @@ from meshbay_common.adminop import (
from meshbay_common.crypto import pk_to_b64
from meshbay_common.protocol import MNP
+# What one connection may have waiting for a signature. Anyone authenticated can
+# ask for a challenge — the signature is what is checked, and it comes later — so
+# without a bound a member who never answers makes the node keep every request,
+# payload and all, for the life of the connection (§13.5b). A person signs one
+# operation at a time; a handful covers a settings page saving several at once.
+MAX_PENDING_ADMIN_OPS = 8
+# The subject and payload of one pending operation, packed. A path is at most a
+# few KiB, and the largest field a legitimate request carries is a directory list.
+MAX_ADMIN_OP_BYTES = 64 * 1024
+
# Which executor runs each signed operation once its signature has been
# checked. Every one runs as a task of the session.
_ADMIN_EXECUTORS = {
@@ -98,8 +109,22 @@ class AdminMixin:
(e.g. root management from a NodePage connection).
"""
gid = group_id if group_id is not None else (self._group_id or "")
+ now = time.time()
+ for op_id, pending in list(self._admin_ops.items()):
+ if now - pending["ts"] > ADMIN_CHALLENGE_TTL:
+ del self._admin_ops[op_id]
+ if len(self._admin_ops) >= MAX_PENDING_ADMIN_OPS:
+ self._send({"type": "error", "detail": "Too many operations waiting for a "
+ "signature", "code": "too_many_pending"})
+ self._audit("admin_pending_flood", op)
+ return
+ if len(msgpack.packb([subject, payload or {}], use_bin_type=True)) \
+ > MAX_ADMIN_OP_BYTES:
+ self._send({"type": "error", "detail": "Request too large",
+ "code": "too_large"})
+ return
nonce = os.urandom(32)
- ts = int(time.time())
+ ts = int(now)
op_id = base64.b64encode(os.urandom(16)).decode()
self._admin_ops[op_id] = {
"op": op, "subject": subject, "nonce": nonce, "ts": ts,