- commit
- 6cf5c614e5be2d8939f13486f0d8cc7e5d8bb33c
- parent
- da6b828060bd429f95bfeab8ccbe8b708f9f51df
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2026-05-24 10:59
pass pid instead of app_id
Diffstat
| D | xikeyring/app_id.py | 21 | --------------------- |
| M | xikeyring/dbus.py | 61 | +++++++++++++++++++++++++++++++------------------------------ |
| M | xikeyring/keyring.py | 47 | ++++++++++++++++++++++++----------------------- |
| A | xikeyring/pidfd.py | 4 | ++++ |
4 files changed, 59 insertions, 74 deletions
diff --git a/xikeyring/app_id.py b/xikeyring/app_id.py
@@ -1,21 +0,0 @@1 -1 import configparser2 -1 import selectors3 -1 from pathlib import Path4 -15 -16 -1 def get_app_id(pid: int, pidfd: int) -> str:7 -1 path = Path('/proc') / str(pid) / 'root' / '.flatpak-info'8 -1 config = configparser.ConfigParser()9 -1 try:10 -1 with path.open() as fh:11 -1 config.read_file(fh)12 -1 app_id = config['Application']['name']13 -1 except Exception:14 -1 return ''15 -116 -1 with selectors.DefaultSelector() as sel:17 -1 sel.register(pidfd, selectors.EVENT_READ)18 -1 if sel.select(0) != []:19 -1 raise ValueError('Calling process has quit')20 -121 -1 return app_id
diff --git a/xikeyring/dbus.py b/xikeyring/dbus.py
@@ -8,10 +8,10 @@ import gi 8 8 from gi.repository import Gio 9 9 from gi.repository import GLib 10 1011 -1 from .app_id import get_app_id12 11 from .dbus_sessions import create_session 13 12 from .keyring import AccessDeniedError 14 13 from .keyring import NotFoundError -1 14 from .pidfd import PID 15 15 16 16 OFSP = '/org/freedesktop/secrets' 17 17 OFSI = 'org.freedesktop.Secret' @@ -115,7 +115,7 @@ class BaseDBusService: 115 115 self._call(conn, sender, path, iface, f'Set{prop}', [value], error) 116 116 return True 117 117118 -1 def get_app_id(self, conn, sender) -> str:-1 118 def get_pid(self, conn, sender) -> PID: 119 119 (cred,), fds = conn.call_with_unix_fd_list_sync( 120 120 'org.freedesktop.DBus', 121 121 '/org/freedesktop/DBus', @@ -128,7 +128,7 @@ class BaseDBusService: 128 128 Gio.UnixFDList(), 129 129 None, 130 130 )131 -1 return get_app_id(cred['ProcessID'], fds.get(cred['ProcessFD']))-1 131 return PID(cred['ProcessID'], fds.get(cred['ProcessFD'])) 132 132 133 133 134 134 class DBusService(BaseDBusService): @@ -185,8 +185,8 @@ class DBusService(BaseDBusService): 185 185 ), 186 186 ) 187 187188 -1 def search_items(self, app_id, conn, query={}):189 -1 items = self.keyring.search_items(app_id, query)-1 188 def search_items(self, pid, conn, query={}): -1 189 items = self.keyring.search_items(pid, query) 190 190 self.update_items(conn, add=items, emit=False) 191 191 return items 192 192 @@ -231,8 +231,8 @@ class DBusService(BaseDBusService): 231 231 self.session_close(conn, sender, path) 232 232 233 233 def service_search_items(self, conn, sender, path, query):234 -1 app_id = self.get_app_id(conn, sender)235 -1 items = self.search_items(app_id, conn, query)-1 234 pid = self.get_pid(conn, sender) -1 235 items = self.search_items(pid, conn, query) 236 236 return GLib.Variant('(aoao)', (self.ids_to_paths(items), [])) 237 237 238 238 def service_unlock(self, conn, sender, path, objects): @@ -244,11 +244,11 @@ class DBusService(BaseDBusService): 244 244 245 245 def service_get_secrets(self, conn, sender, path, items, session_path): 246 246 session = self.sessions[session_path][2]247 -1 app_id = self.get_app_id(conn, sender)-1 247 pid = self.get_pid(conn, sender) 248 248 result = [] 249 249 for path in items: 250 250 id = int(path.rsplit('/', 1)[1], 10)251 -1 secret = self.keyring.get_secret(app_id, id)-1 251 secret = self.keyring.get_secret(pid, id) 252 252 secret_tuple = session.encode(session_path, secret) 253 253 result.append((path, secret_tuple)) 254 254 return GLib.Variant('(a{o(oayays)})', [result]) @@ -263,8 +263,8 @@ class DBusService(BaseDBusService): 263 263 return GLib.Variant('ao', [f'{OFSP}/collection/it']) 264 264 265 265 def collection_search_items(self, conn, sender, path, query):266 -1 app_id = self.get_app_id(conn, sender)267 -1 items = self.search_items(app_id, conn, query)-1 266 pid = self.get_pid(conn, sender) -1 267 items = self.search_items(pid, conn, query) 268 268 return GLib.Variant('(ao)', [self.ids_to_paths(items)]) 269 269 270 270 def collection_create_item( @@ -273,21 +273,21 @@ class DBusService(BaseDBusService): 273 273 session = self.sessions[secret_tuple[0]][2] 274 274 secret = session.decode(secret_tuple) 275 275 attributes = properties.get(f'{OFSI}.Item.Attributes', {})276 -1 app_id = self.get_app_id(conn, sender)-1 276 pid = self.get_pid(conn, sender) 277 277 id = None 278 278 if replace:279 -1 matches = self.search_items(app_id, conn, attributes)-1 279 matches = self.search_items(pid, conn, attributes) 280 280 if matches: 281 281 id = matches[0]282 -1 self.keyring.update_secret(app_id, id, secret)-1 282 self.keyring.update_secret(pid, id, secret) 283 283 if not id:284 -1 id = self.keyring.create_item(app_id, attributes, secret)-1 284 id = self.keyring.create_item(pid, attributes, secret) 285 285 self.update_items(conn, add=[id]) 286 286 return GLib.Variant('(oo)', (f'{OFSP}/collection/it/{id}', '/')) 287 287 288 288 def collection_get_items(self, conn, sender, path):289 -1 app_id = self.get_app_id(conn, sender)290 -1 items = self.search_items(app_id, conn)-1 289 pid = self.get_pid(conn, sender) -1 290 items = self.search_items(pid, conn) 291 291 return GLib.Variant('ao', self.ids_to_paths(items)) 292 292 293 293 def collection_get_label(self, conn, sender, path): @@ -304,15 +304,15 @@ class DBusService(BaseDBusService): 304 304 305 305 def item_delete(self, conn, sender, path): 306 306 id = int(path.rsplit('/', 1)[1], 10)307 -1 app_id = self.get_app_id(conn, sender)308 -1 self.keyring.delete_item(app_id, id)-1 307 pid = self.get_pid(conn, sender) -1 308 self.keyring.delete_item(pid, id) 309 309 self.update_items(conn, rm=[id]) 310 310 return GLib.Variant('(o)', ['/']) 311 311 312 312 def item_get_secret(self, conn, sender, path, session_path): 313 313 id = int(path.rsplit('/', 1)[1], 10)314 -1 app_id = self.get_app_id(conn, sender)315 -1 secret = self.keyring.get_secret(app_id, id)-1 314 pid = self.get_pid(conn, sender) -1 315 secret = self.keyring.get_secret(pid, id) 316 316 session = self.sessions[session_path][2] 317 317 secret_tuple = session.encode(session_path, secret) 318 318 return GLib.Variant('((oayays))', [secret_tuple]) @@ -321,8 +321,8 @@ class DBusService(BaseDBusService): 321 321 id = int(path.rsplit('/', 1)[1], 10) 322 322 session = self.sessions[secret_tuple[0]][2] 323 323 secret = session.decode(secret_tuple)324 -1 app_id = self.get_app_id(conn, sender)325 -1 self.keyring.update_secret(app_id, id, secret)-1 324 pid = self.get_pid(conn, sender) -1 325 self.keyring.update_secret(pid, id, secret) 326 326 327 327 def item_get_label(self, conn, sender, path): 328 328 return GLib.Variant('s', path.rsplit('/', 1)[1]) @@ -341,15 +341,15 @@ class DBusService(BaseDBusService): 341 341 342 342 def item_get_attributes(self, conn, sender, path): 343 343 id = int(path.rsplit('/', 1)[1], 10)344 -1 app_id = self.get_app_id(conn, sender)345 -1 attributes = self.keyring.get_attributes(app_id, id)-1 344 pid = self.get_pid(conn, sender) -1 345 attributes = self.keyring.get_attributes(pid, id) 346 346 return GLib.Variant('a{ss}', attributes.items()) 347 347 348 348 def item_set_attributes(self, conn, sender, path, value): 349 349 id = int(path.rsplit('/', 1)[1], 10) 350 350 attributes = value.unpack()351 -1 app_id = self.get_app_id(conn, sender)352 -1 self.keyring.update_attributes(app_id, id, attributes)-1 351 pid = self.get_pid(conn, sender) -1 352 self.keyring.update_attributes(pid, id, attributes) 353 353 354 354 conn.emit_signal( 355 355 None, @@ -380,16 +380,17 @@ class DBusService(BaseDBusService): 380 380 conn, handle, 'org.freedesktop.impl.portal.Request' 381 381 ) 382 382 try: -1 383 pid = self.get_pid(conn, sender) 383 384 attrs = { 384 385 'application': 'org.freedesktop.portal.Secret', 385 386 'app_id': app_id, 386 387 }387 -1 ids = self.keyring.search_items('', attrs)-1 388 ids = self.keyring.search_items(pid, attrs) 388 389 if ids:389 -1 secret = self.keyring.get_secret('', ids[0])-1 390 secret = self.keyring.get_secret(pid, ids[0]) 390 391 else: 391 392 secret = os.urandom(64)392 -1 self.keyring.create_item('', attrs, secret)-1 393 self.keyring.create_item(pid, attrs, secret) 393 394 os.write(fd, secret) 394 395 os.close(fd) 395 396 finally:
diff --git a/xikeyring/keyring.py b/xikeyring/keyring.py
@@ -9,6 +9,7 @@ from cryptography.fernet import InvalidToken 9 9 10 10 from . import crypto 11 11 from .kernel_keyring import KernelKey -1 12 from .pidfd import PID 12 13 from .prompt import PinentryPrompt as Prompt 13 14 14 15 @@ -86,7 +87,7 @@ class Keyring: 86 87 write_bytes(path, encrypted) 87 88 return KernelKey(key) 88 8989 -1 def _read(self) -> dict[int, Item]:-1 90 def _read(self, pid: PID) -> dict[int, Item]: 90 91 if not self.path.exists(): 91 92 return {} 92 93 @@ -98,7 +99,7 @@ class Keyring: 98 99 for id, secret, attributes in raw 99 100 } 100 101101 -1 def _write(self, items: dict[int, Item]):-1 102 def _write(self, pid: PID, items: dict[int, Item]): 102 103 raw = [ 103 104 ( 104 105 id, @@ -125,50 +126,50 @@ class Keyring: 125 126 except KeyError as e: 126 127 raise NotFoundError from e 127 128128 -1 def search_items(self, app_id: str, query: dict[str, str] = {}) -> list[int]:129 -1 items = self._read()-1 129 def search_items(self, pid: PID, query: dict[str, str] = {}) -> list[int]: -1 130 items = self._read(pid) 130 131 return [ 131 132 id for id, item in items.items() 132 133 if all(item.attributes.get(k) == v for k, v in query.items()) 133 134 ] 134 135135 -1 def get_attributes(self, app_id: str, id: int) -> dict[str, str]:136 -1 items = self._read()137 -1 return self.get(items, app_id, id).attributes-1 136 def get_attributes(self, pid: PID, id: int) -> dict[str, str]: -1 137 items = self._read(pid) -1 138 return self.get(items, id).attributes 138 139139 -1 def get_secret(self, app_id: str, id: int) -> bytes:140 -1 items = self._read()-1 140 def get_secret(self, pid: PID, id: int) -> bytes: -1 141 items = self._read(pid) 141 142 item = self.get(items, id) 142 143 self.confirm_access() 143 144 return item.secret 144 145145 -1 def create_item(self, app_id: str, attributes: dict[str, str], secret: bytes) -> int:146 -1 items = self._read()-1 146 def create_item(self, pid: PID, attributes: dict[str, str], secret: bytes) -> int: -1 147 items = self._read(pid) 147 148 id = max(items.keys(), default=0) + 1148 -1 items[id] = Item(secret, attributes, app_id)149 -1 self._write(items)-1 149 items[id] = Item(secret, attributes) -1 150 self._write(pid, items) 150 151 return id 151 152152 -1 def update_attributes(self, app_id: str, id: int, attributes: dict[str, str]) -> None:153 -1 items = self._read()-1 153 def update_attributes(self, pid: PID, id: int, attributes: dict[str, str]) -> None: -1 154 items = self._read(pid) 154 155 item = self.get(items, id) 155 156 self.confirm_change() 156 157 item.attributes = attributes157 -1 self._write(items)-1 158 self._write(pid, items) 158 159159 -1 def update_secret(self, app_id: str, id: int, secret: bytes) -> None:160 -1 items = self._read()-1 160 def update_secret(self, pid: PID, id: int, secret: bytes) -> None: -1 161 items = self._read(pid) 161 162 item = self.get(items, id) 162 163 self.confirm_change() 163 164 item.secret = secret164 -1 self._write(items)-1 165 self._write(pid, items) 165 166166 -1 def delete_item(self, app_id: str, id: int) -> None:167 -1 items = self._read()168 -1 self.get(items, app_id, id) # trigger appropriate exceptions-1 167 def delete_item(self, pid: PID, id: int) -> None: -1 168 items = self._read(pid) -1 169 self.get(items, id) # trigger appropriate exceptions 169 170 self.confirm_change() 170 171 del items[id]171 -1 self._write(items)-1 172 self._write(pid, items) 172 173 173 174 174 175 class KeyringProxy:
diff --git a/xikeyring/pidfd.py b/xikeyring/pidfd.py
@@ -0,0 +1,4 @@ -1 1 class PID: -1 2 def __init__(self, pid: int, pidfd: int): -1 3 self.pid = pid -1 4 self.pidfd = pidfd