2022-11-11 16:30:04 +08:00
|
|
|
# Copyright (C) 2006, 2013 Red Hat, Inc.
|
|
|
|
# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.com>
|
|
|
|
#
|
|
|
|
# This work is licensed under the GNU GPLv2 or later.
|
|
|
|
# See the COPYING file in the top-level directory.
|
|
|
|
|
|
|
|
from gi.repository import Gio
|
|
|
|
from gi.repository import GLib
|
|
|
|
|
|
|
|
from virtinst import log
|
|
|
|
|
2024-05-17 13:37:53 +08:00
|
|
|
from ..baseclass import vmmGObject
|
2022-11-11 16:30:04 +08:00
|
|
|
|
2024-05-17 13:37:53 +08:00
|
|
|
|
|
|
|
class _vmmSecret(object):
|
2022-11-11 16:30:04 +08:00
|
|
|
def __init__(self, name, secret=None, attributes=None):
|
|
|
|
self.name = name
|
|
|
|
self.secret = secret
|
|
|
|
self.attributes = attributes
|
|
|
|
|
|
|
|
def get_secret(self):
|
|
|
|
return self.secret
|
|
|
|
def get_name(self):
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
|
2024-05-17 13:37:53 +08:00
|
|
|
class vmmKeyring(vmmGObject):
|
|
|
|
"""
|
|
|
|
freedesktop Secret API abstraction
|
|
|
|
"""
|
|
|
|
@classmethod
|
|
|
|
def get_instance(cls):
|
|
|
|
if not cls._instance:
|
|
|
|
cls._instance = vmmKeyring()
|
|
|
|
return cls._instance
|
2022-11-11 16:30:04 +08:00
|
|
|
|
|
|
|
def __init__(self):
|
2024-05-17 13:37:53 +08:00
|
|
|
vmmGObject.__init__(self)
|
|
|
|
|
2022-11-11 16:30:04 +08:00
|
|
|
self._collection = None
|
|
|
|
|
|
|
|
try:
|
|
|
|
self._dbus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
|
|
|
|
self._service = Gio.DBusProxy.new_sync(self._dbus, 0, None,
|
|
|
|
"org.freedesktop.secrets",
|
|
|
|
"/org/freedesktop/secrets",
|
|
|
|
"org.freedesktop.Secret.Service", None)
|
|
|
|
|
|
|
|
self._session = self._service.OpenSession("(sv)", "plain",
|
|
|
|
GLib.Variant("s", ""))[1]
|
|
|
|
|
|
|
|
self._collection = Gio.DBusProxy.new_sync(self._dbus, 0, None,
|
|
|
|
"org.freedesktop.secrets",
|
|
|
|
"/org/freedesktop/secrets/aliases/default",
|
|
|
|
"org.freedesktop.Secret.Collection", None)
|
|
|
|
|
|
|
|
log.debug("Using keyring session %s", self._session)
|
2024-05-17 13:37:53 +08:00
|
|
|
except Exception: # pragma: no cover
|
2022-11-11 16:30:04 +08:00
|
|
|
log.exception("Error determining keyring")
|
|
|
|
|
2024-05-17 13:37:53 +08:00
|
|
|
def _cleanup(self):
|
|
|
|
pass # pragma: no cover
|
|
|
|
|
|
|
|
def _find_secret_item_path(self, uuid, hvuri):
|
|
|
|
attributes = {
|
|
|
|
"uuid": uuid,
|
|
|
|
"hvuri": hvuri,
|
|
|
|
}
|
|
|
|
unlocked, locked = self._service.SearchItems("(a{ss})", attributes)
|
|
|
|
if not unlocked:
|
|
|
|
if locked:
|
|
|
|
log.warning( # pragma: no cover
|
|
|
|
"Item found, but it's locked")
|
|
|
|
return None
|
|
|
|
return unlocked[0]
|
|
|
|
|
|
|
|
def _do_prompt_if_needed(self, path):
|
|
|
|
if path == "/":
|
|
|
|
return
|
|
|
|
iface = Gio.DBusProxy.new_sync( # pragma: no cover
|
|
|
|
self._dbus, 0, None,
|
|
|
|
"org.freedesktop.secrets", path,
|
|
|
|
"org.freedesktop.Secret.Prompt", None)
|
|
|
|
iface.Prompt("(s)", "") # pragma: no cover
|
|
|
|
|
|
|
|
def _add_secret(self, secret):
|
2022-11-11 16:30:04 +08:00
|
|
|
try:
|
|
|
|
props = {
|
|
|
|
"org.freedesktop.Secret.Item.Label": GLib.Variant("s", secret.get_name()),
|
|
|
|
"org.freedesktop.Secret.Item.Attributes": GLib.Variant("a{ss}", secret.attributes),
|
|
|
|
}
|
|
|
|
params = (self._session, [],
|
|
|
|
[ord(v) for v in secret.get_secret()],
|
|
|
|
"text/plain; charset=utf8")
|
|
|
|
replace = True
|
|
|
|
|
2024-05-17 13:37:53 +08:00
|
|
|
dummy, prompt = self._collection.CreateItem("(a{sv}(oayays)b)",
|
|
|
|
props, params, replace)
|
|
|
|
self._do_prompt_if_needed(prompt)
|
|
|
|
except Exception: # pragma: no cover
|
2022-11-11 16:30:04 +08:00
|
|
|
log.exception("Failed to add keyring secret")
|
|
|
|
|
2024-05-17 13:37:53 +08:00
|
|
|
def _del_secret(self, uuid, hvuri):
|
2022-11-11 16:30:04 +08:00
|
|
|
try:
|
2024-05-17 13:37:53 +08:00
|
|
|
path = self._find_secret_item_path(uuid, hvuri)
|
|
|
|
if path is None:
|
|
|
|
return None
|
|
|
|
|
2022-11-11 16:30:04 +08:00
|
|
|
iface = Gio.DBusProxy.new_sync(self._dbus, 0, None,
|
|
|
|
"org.freedesktop.secrets", path,
|
|
|
|
"org.freedesktop.Secret.Item", None)
|
2024-05-17 13:37:53 +08:00
|
|
|
prompt = iface.Delete()
|
|
|
|
self._do_prompt_if_needed(prompt)
|
|
|
|
except Exception: # pragma: no cover
|
2022-11-11 16:30:04 +08:00
|
|
|
log.exception("Failed to delete keyring secret")
|
|
|
|
|
2024-05-17 13:37:53 +08:00
|
|
|
def _get_secret(self, uuid, hvuri):
|
2022-11-11 16:30:04 +08:00
|
|
|
ret = None
|
|
|
|
try:
|
2024-05-17 13:37:53 +08:00
|
|
|
path = self._find_secret_item_path(uuid, hvuri)
|
|
|
|
if path is None:
|
|
|
|
return None
|
|
|
|
|
2022-11-11 16:30:04 +08:00
|
|
|
iface = Gio.DBusProxy.new_sync(self._dbus, 0, None,
|
|
|
|
"org.freedesktop.secrets", path,
|
|
|
|
"org.freedesktop.Secret.Item", None)
|
|
|
|
|
|
|
|
secretbytes = iface.GetSecret("(o)", self._session)[2]
|
|
|
|
label = iface.get_cached_property("Label").unpack().strip("'")
|
|
|
|
dbusattrs = iface.get_cached_property("Attributes").unpack()
|
|
|
|
|
2024-05-17 13:37:53 +08:00
|
|
|
secret = "".join([chr(c) for c in secretbytes])
|
2022-11-11 16:30:04 +08:00
|
|
|
|
|
|
|
attrs = {}
|
|
|
|
for key, val in dbusattrs.items():
|
|
|
|
if key not in ["hvuri", "uuid"]:
|
|
|
|
continue
|
|
|
|
attrs["%s" % key] = "%s" % val
|
|
|
|
|
2024-05-17 13:37:53 +08:00
|
|
|
ret = _vmmSecret(label, secret, attrs)
|
|
|
|
except Exception: # pragma: no cover
|
|
|
|
log.exception("Failed to get keyring secret uuid=%r hvuri=%r", uuid, hvuri)
|
2022-11-11 16:30:04 +08:00
|
|
|
|
|
|
|
return ret
|
2024-05-17 13:37:53 +08:00
|
|
|
|
|
|
|
|
|
|
|
##############
|
|
|
|
# Public API #
|
|
|
|
##############
|
|
|
|
|
|
|
|
def is_available(self):
|
|
|
|
return not (self._collection is None)
|
|
|
|
|
|
|
|
def _get_secret_name(self, vm):
|
|
|
|
return "vm-console-" + vm.get_uuid()
|
|
|
|
|
|
|
|
def get_console_password(self, vm):
|
|
|
|
if not self.is_available():
|
|
|
|
return ("", "") # pragma: no cover
|
|
|
|
|
|
|
|
secret = self._get_secret(vm.get_uuid(), vm.conn.get_uri())
|
|
|
|
if secret is None:
|
|
|
|
return ("", "") # pragma: no cover
|
|
|
|
|
|
|
|
return (secret.get_secret(), vm.get_console_username() or "")
|
|
|
|
|
|
|
|
def set_console_password(self, vm, password, username=""):
|
|
|
|
if not self.is_available():
|
|
|
|
return # pragma: no cover
|
|
|
|
|
|
|
|
|
|
|
|
secret = _vmmSecret(self._get_secret_name(vm), password,
|
|
|
|
{"uuid": vm.get_uuid(),
|
|
|
|
"hvuri": vm.conn.get_uri()})
|
|
|
|
vm.set_console_username(username)
|
|
|
|
self._add_secret(secret)
|
|
|
|
|
|
|
|
def del_console_password(self, vm):
|
|
|
|
if not self.is_available():
|
|
|
|
return # pragma: no cover
|
|
|
|
|
|
|
|
self._del_secret(vm.get_uuid(), vm.conn.get_uri())
|
|
|
|
vm.del_console_username()
|