Linux: Nextcloud клиент, qtkeychain и ошибка «The name org.freedesktop.secrets was not provided by any .service files»

Автор: | 11/28/2019
 

После установки Nextcloud (см. Nextcloud: запуск в Docker Compose на Debian с SSL от Let’s Encrypt), на следующий день его клиент запросил повторную аутентификацию.

Но после ввода логина и пароля он сообщает об ошибке:

Reading from keychain failed with error: ‘The name org.freedesktop.secrets was not provided by any .service files’

Гугление одним из первых результатов выдало ссылку на обсуждение этой проблемы тут>>>, где в первом же комментарием говорится:

Could be an upstream issue of the qtkeychain dependency frankosterfeld/qtkeychain#99, but I don’t have any KDE setup to test/debug this. Any help here is welcome.

Хорошо — отправная точка есть, дальше попробуем разобраться сами.

Что такое qtkeychain?

Из описания в README на Github:

QtKeychain is a Qt API to store passwords and other secret data securely.

Т.е. пакет предоставляет API для Qt-based приложений, используя которое они могут получить доступ к логинам/паролям, которые хранятся в системе.

Но при этом сам QtKeychain не хранит эти данные — он только транслирует запросы от приложений к системному хранилищу таких данных, что и указывается дальше в его README:

  • Mac OS X: Passwords are stored in the OS X Keychain.
  • Linux/Unix: If running, GNOME Keyring is used, otherwise qtkeychain tries to use KWallet (via D-Bus), if available.

Т.е. на Linux qtkeychain постарается через D-Bus вызвать GNOME Keyring или KWallet.

Используя эту подсказку — D-Bus — открываем исходный код qtkeychain, и посмотрим — как вообще это происходит.

Качаем исходники, и грепаем исходный код по слову dbus:

[setevoy@setevoy-arch-work /tmp/qtkeychain] [master*] $ grep -r dbus .
./qt5keychain.pri:    QT += dbus
./org.kde.KWallet.xml:<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
./keychain_unix.cpp:    /* The secret service dbus api, accessible through libsecret, is supposed

keychain_unix.cpp — вполне говорящее само за себя название файла, явно именно то, что нам надо.

Открываем его, и снова ищем слово dbus — находим функцию с говорящим названием detectKeyringBackend():

static KeyringBackend detectKeyringBackend()
{
    /* The secret service dbus api, accessible through libsecret, is supposed
     * to unify password services.
     *
     * Unfortunately at the time of Kubuntu 18.04 the secret service backend
     * in KDE is gnome-keyring-daemon - using it has several complications:
     * - the default collection isn't opened on session start, so users need
     *   to manually unlock it when the first application uses it
     * - it's separate from the kwallet5 keyring, so switching to it means the
     *   existing keyring data can't be accessed anymore
     *
     * Thus we still prefer kwallet backends on KDE even if libsecret is
     * available.
     */

    switch (detectDesktopEnvironment()) {
    case DesktopEnv_Kde4:
        return Backend_Kwallet4;

    case DesktopEnv_Plasma5:
        if (isKwallet5Available()) {
            return Backend_Kwallet5;
        }
        if (LibSecretKeyring::isAvailable()) {
            return Backend_LibSecretKeyring;
        }
        if (GnomeKeyring::isAvailable()) {
            return Backend_GnomeKeyring;
        }
        // During startup the keychain backend might just not have started yet
        return Backend_Kwallet5;

    case DesktopEnv_Gnome:
    case DesktopEnv_Unity:
    case DesktopEnv_Xfce:
    case DesktopEnv_Other:
    default:
        if (LibSecretKeyring::isAvailable()) {
            return Backend_LibSecretKeyring;
        }
        if (GnomeKeyring::isAvailable()) {
            return Backend_GnomeKeyring;
        }
        if (isKwallet5Available()) {
            return Backend_Kwallet5;
        }
...
}

Тут ещё интересен комментарий по поводу kwallet vs gnome-keyring, но сейчас это не важно.

Далее выполняется инициализация типа бекенда:

...
static KeyringBackend getKeyringBackend()
{
    static KeyringBackend backend = detectKeyringBackend();
    return backend;
}
...

И затем — попытка получить данные, например пароль:

...
void ReadPasswordJobPrivate::scheduledStart() {
    switch ( getKeyringBackend() ) {
    case Backend_LibSecretKeyring: {
        if ( !LibSecretKeyring::findPassword(key, q->service(), this) ) {
            q->emitFinishedWithError( OtherError, tr("Unknown error") );
        }
    } break;
    case Backend_GnomeKeyring:
        this->mode = JobPrivate::Text;
        if ( !GnomeKeyring::find_network_password( key.toUtf8().constData(),
                                                   q->service().toUtf8().constData(),
                                                   "plaintext",
                                                   reinterpret_cast<GnomeKeyring::OperationGetStringCallback>( &JobPrivate::gnomeKeyring_readCb ),
                                                   this, 0 ) )
            q->emitFinishedWithError( OtherError, tr("Unknown error") );
        break;

    case Backend_Kwallet4:
        kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd", "/modules/kwalletd", this);
        break;
    case Backend_Kwallet5:
        kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd5", "/modules/kwalletd5", this);
        break;
    }
}
...

В нашем случае — будет вызвана:

...
  case Backend_LibSecretKeyring: { 
    if ( !LibSecretKeyring::findPassword(key, q->service(), this) ) {
...

Можно почитать код дальше, но суть ясна — выполняется вызов к libsecret.

Далее можно ещё закопаться в сам libsecret — но уже не в этот раз, просто поищем где вообще libsecret выполняет вызов к D-Bus, а потом можно будет повспоминать работу с D-Bus API.

В репозитории libsecret grep по dbus возвращает уже море результатов, потому что много файлов для тестирования, плюс пачка Python-скриптов.

Убираем их через grep -v:

[setevoy@setevoy-arch-work /tmp/libsecret] [master*] $ grep -r "/org/freedesktop/secrets" . | grep -v "test\|.py"
./tool/secret-tool.c:#define SECRET_ALIAS_PREFIX "/org/freedesktop/secrets/aliases/"
./libsecret/secret-util.c:              return g_strdup_printf ("/org/freedesktop/secrets/aliases/%s", collection);
./libsecret/secret-private.h:#define              SECRET_ALIAS_PREFIX                      "/org/freedesktop/secrets/aliases/"
./libsecret/secret-private.h:#define              SECRET_SERVICE_PATH                      "/org/freedesktop/secrets"

./tool/secret-tool.c:#define SECRET_ALIAS_PREFIX "/org/freedesktop/secrets/aliases/" — «Ага, вот эти ребята!» (с)

Итак, что просходит?

  • у меня Arch Linux и Openbox Window Manager, а Desktop Environment нет
  • значит — в qtkeychain в функции detectKeyringBackend() срабатывает следующее условие:
    ...
        case DesktopEnv_Other:
        default:
            if (LibSecretKeyring::isAvailable()) {
                return Backend_LibSecretKeyring;
            }
    ...
  • qtkeychain выполняет вызов libsecret, который пытается вызвать org.freedesktop.secrets, не находит его, и возвращает SECRET_ERROR_NO_SUCH_OBJECT

А почему не находит?

Проверяем сервис-файлы D-Bus в каталоге /usr/share/dbus-1/services/:

ls -l /usr/share/dbus-1/services/
total 196
...
-rw-r--r-- 1 root root 107 Sep 10 21:53 org.a11y.Bus.service
-rw-r--r-- 1 root root  95 Oct 31 12:09 org.bluez.obex.service
-rw-r--r-- 1 root root 116 Oct  9 12:26 org.freedesktop.ColorHelper.service
-rw-r--r-- 1 root root 104 Oct 27 16:08 org.freedesktop.Telepathy.AccountManager.service
-rw-r--r-- 1 root root 194 Nov  5 12:19 org.freedesktop.Telepathy.Client.KTp.Approver.service
-rw-r--r-- 1 root root  91 Nov  5 12:21 org.freedesktop.Telepathy.Client.KTp.CallUi.service
-rw-r--r-- 1 root root 106 Nov  5 12:20 org.freedesktop.Telepathy.Client.KTp.ConfAuthObserver.service
-rw-r--r-- 1 root root 117 Nov  5 12:26 org.freedesktop.Telepathy.Client.KTp.FileTransferHandler.service
-rw-r--r-- 1 root root 217 Nov  5 12:27 org.freedesktop.Telepathy.Client.KTp.KdedIntegrationModule.service
-rw-r--r-- 1 root root  88 Nov 12 10:26 org.freedesktop.Telepathy.Client.KTp.Proxy.service
-rw-r--r-- 1 root root 101 Nov  5 12:20 org.freedesktop.Telepathy.Client.KTp.SASLHandler.service
-rw-r--r-- 1 root root  91 Nov  5 12:29 org.freedesktop.Telepathy.Client.KTp.TextUi.service
-rw-r--r-- 1 root root 100 Nov  5 12:20 org.freedesktop.Telepathy.Client.KTp.TLSHandler.service
-rw-r--r-- 1 root root 102 Nov 10  2018 org.freedesktop.Telepathy.Client.Logger.service
-rw-r--r-- 1 root root  84 Sep 12  2018 org.freedesktop.Telepathy.Client.Vinagre.service
-rw-r--r-- 1 root root  95 Nov 10  2018 org.freedesktop.Telepathy.Logger.service
-rw-r--r-- 1 root root 188 Oct 27 16:08 org.freedesktop.Telepathy.MissionControl5.service
-rw-r--r-- 1 root root  60 Oct 27  2018 org.gnome.GConf.service
...

И вспоминаем саму ошибку — The name org.freedesktop.secrets was not provided by any .service files’: файла org.freedesktop.secrets.service нет.

Решение

Проверяем установлен ли сам qtkeychain:

[setevoy@setevoy-arch-work ~]  $ pacman -Qi | grep qtkeychain
Optional For    : chromium  qtkeychain
Optional For    : git  qtkeychain
Depends On      : qtkeychain  qt5-webkit  hicolor-icon-theme  xdg-utils
Required By     : appstream-qt...
Name            : qtkeychain
URL             : https://github.com/frankosterfeld/qtkeychain

Да, установлен. Собственно — он указан в зависимостях nextcloud-client, поэтому pacman его установил вместе с самим клиентом.

Собственно, что нам надо — так это установить любой подходящий backend storage для хранения секретов, который создаст файл org.freedesktop.secrets.

Устанавливаем gnome-keyring:

[setevoy@setevoy-arch-work /tmp/qtkeychain] [master*] $ sudo pacman -S gnome-keyring

Проверяем файлы D-Bus ещё раз:

[setevoy@setevoy-arch-work /tmp/qtkeychain] [master*] $ ls -l /usr/share/dbus-1/services/ | grep secret
-rw-r--r-- 1 root root 122 Oct 29 11:38 org.freedesktop.secrets.service

Проверяем его содержимое:

[setevoy@setevoy-arch-work /tmp/qtkeychain] [master*] $ cat /usr/share/dbus-1/services/org.freedesktop.secrets.service
[D-BUS Service]
Name=org.freedesktop.secrets
Exec=/usr/bin/gnome-keyring-daemon --start --foreground --components=secrets

И повторяем логин в Nexcloud-клиенте.

Готово.

P.S. тут ещё отдельный вопрос по работе Chrome/Chromium с хранилищами, но об этом, может быть, в другом посте.

Ссылки по теме