113 lines
4.2 KiB
Python
113 lines
4.2 KiB
Python
"""Synthetic D-Bus boundary; real SecretStorage negotiates and decrypts sessions."""
|
|
|
|
import json
|
|
from dataclasses import asdict
|
|
|
|
from jeepney import HeaderFields, new_error, new_method_return
|
|
from secretstorage.dhcrypto import Session
|
|
from secretstorage.util import format_secret
|
|
|
|
|
|
class KeyringBus:
|
|
def __init__(self, profile=None):
|
|
self.names = {
|
|
"org.gnome.keyring": ":1.10",
|
|
"org.kde.secretservicecompat": ":1.20",
|
|
"org.freedesktop.secrets": ":1.10",
|
|
}
|
|
self.activatable = set(self.names)
|
|
self.locked = set()
|
|
self.plain = set()
|
|
self.missing_default = set()
|
|
self.calls = []
|
|
self.sessions = {}
|
|
self.closed = False
|
|
self.switch_default_on_read = False
|
|
self.payload = (
|
|
json.dumps({"version": 1, "profile": asdict(profile)}).encode()
|
|
if profile
|
|
else None
|
|
)
|
|
|
|
def close(self):
|
|
self.closed = True
|
|
|
|
def send_and_get_reply(self, message, *, timeout=None):
|
|
fields = message.header.fields
|
|
destination = fields[HeaderFields.destination]
|
|
method = fields[HeaderFields.member]
|
|
self.calls.append((destination, method, message.body, timeout))
|
|
|
|
def reply(signature, *body):
|
|
return new_method_return(message, signature, body)
|
|
|
|
def error(name):
|
|
return new_error(message, name, "s", ("synthetic error",))
|
|
|
|
if destination == "org.freedesktop.DBus":
|
|
if method == "ListNames":
|
|
return reply("as", list(self.names))
|
|
if method == "ListActivatableNames":
|
|
return reply("as", list(self.activatable))
|
|
if method == "GetNameOwner":
|
|
if message.body[0] not in self.names:
|
|
return error("org.freedesktop.DBus.Error.NameHasNoOwner")
|
|
return reply("s", self.names[message.body[0]])
|
|
if method == "GetConnectionUnixProcessID":
|
|
return reply("u", 123)
|
|
owner = self.names.get(destination, destination)
|
|
if owner not in {":1.10", ":1.20", ":1.30"}:
|
|
return error("org.freedesktop.DBus.Error.ServiceUnknown")
|
|
if method == "ReadAlias":
|
|
if self.switch_default_on_read:
|
|
self.names["org.freedesktop.secrets"] = ":1.20"
|
|
return reply(
|
|
"o",
|
|
"/"
|
|
if owner in self.missing_default
|
|
else "/org/freedesktop/secrets/collection/default",
|
|
)
|
|
if method == "Get":
|
|
name = message.body[1]
|
|
values = {
|
|
"Label": ("s", "Synthetic wallet"),
|
|
"Locked": ("b", owner in self.locked),
|
|
"Attributes": (
|
|
"a{ss}",
|
|
{"application": "hermes-caldav-mcp", "profile": "default"},
|
|
),
|
|
}
|
|
return reply("v", values[name])
|
|
if method == "OpenSession":
|
|
if owner in self.plain:
|
|
if message.body[0] != "plain":
|
|
return error("org.freedesktop.DBus.Error.NotSupported")
|
|
return reply("vo", ("s", ""), "/org/freedesktop/secrets/session/plain")
|
|
session = Session()
|
|
session.object_path = "/org/freedesktop/secrets/session/encrypted"
|
|
session.set_server_public_key(int.from_bytes(message.body[1][1], "big"))
|
|
self.sessions[owner] = session
|
|
return reply(
|
|
"vo",
|
|
("ay", session.my_public_key.to_bytes(128, "big")),
|
|
session.object_path,
|
|
)
|
|
if method == "SearchItems":
|
|
return reply(
|
|
"ao",
|
|
["/org/freedesktop/secrets/collection/default/item"]
|
|
if self.payload
|
|
else [],
|
|
)
|
|
if method == "GetSecret":
|
|
return reply(
|
|
"(oayays)",
|
|
format_secret(self.sessions[owner], self.payload, "application/json"),
|
|
)
|
|
if method == "CreateItem":
|
|
return reply("oo", "/org/freedesktop/secrets/collection/default/item", "/")
|
|
if method == "Delete":
|
|
self.payload = None
|
|
return reply("o", "/")
|
|
raise AssertionError("Unexpected D-Bus method: " + method)
|