diff --git a/src/main.cpp b/src/main.cpp index 0d116e7e0..fdc4cfb38 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,242 +1,263 @@ /* main.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2001, 2002, 2004, 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "aboutdata.h" #include "kleopatraapplication.h" #include "mainwindow.h" #include #include #include #include #include #include "utils/kuniqueservice.h" +#include "utils/userinfo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include "kleopatra_options.h" #include #include #include #include // for Qt::escape #include #include #include #include #include #include #include #include #include #include #include static bool selfCheck() { Kleo::Commands::SelfTestCommand cmd(nullptr); cmd.setAutoDelete(false); cmd.setAutomaticMode(true); QEventLoop loop; QObject::connect(&cmd, &Kleo::Commands::SelfTestCommand::finished, &loop, &QEventLoop::quit); QTimer::singleShot(0, &cmd, &Kleo::Command::start); // start() may Q_EMIT finished()... loop.exec(); if (cmd.isCanceled()) { return false; } else { return true; } } static void fillKeyCache(Kleo::UiServer *server) { Kleo::ReloadKeysCommand *cmd = new Kleo::ReloadKeysCommand(nullptr); QObject::connect(cmd, SIGNAL(finished()), server, SLOT(enableCryptoCommands())); cmd->start(); } int main(int argc, char **argv) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); KleopatraApplication app(argc, argv); KCrash::initialize(); QElapsedTimer timer; timer.start(); KLocalizedString::setApplicationDomain("kleopatra"); + if (Kleo::userIsElevated()) { + /* This is a safeguard against bugreports that something fails because + * of permission problems on windows. Some users still have the Windows + * Vista behavior of running things as Administrator. This can break + * GnuPG in horrible ways for example if a stale lockfile is left that + * can't be removed without another elevation. + * + * Note: This is not the same as running as root on Linux. Elevated means + * that you are temporarily running with the "normal" user environment but + * with elevated permissions. + * */ + KMessageBox::sorry(nullptr, xi18nc("@info", + "Kleopatra cannot be run as adminstrator without " + "breaking file permissions in the GnuPG data folder." + "To manage keys for other users please manage them as a normal user and " + "copy the AppData\\Roaming\\gnupg directory with proper permissions."), + i18nc("@title", "Running as Administrator")); + return EXIT_FAILURE; + } + KUniqueService service; QObject::connect(&service, &KUniqueService::activateRequested, &app, &KleopatraApplication::slotActivateRequested); QObject::connect(&app, &KleopatraApplication::setExitValue, &service, [&service](int i) { service.setExitValue(i); }); // Delay init after KUniqueservice call as this might already // have terminated us and so we can avoid overhead (e.g. keycache // setup / systray icon). qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: Service created"; app.init(); qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: Application initialized"; AboutData aboutData; KAboutData::setApplicationData(aboutData); QCommandLineParser parser; aboutData.setupCommandLine(&parser); kleopatra_options(&parser); parser.process(QApplication::arguments()); aboutData.processCommandLine(&parser); Kdelibs4ConfigMigrator migrate(QStringLiteral("kleopatra")); migrate.setConfigFiles(QStringList() << QStringLiteral("kleopatrarc") << QStringLiteral("libkleopatrarc")); migrate.setUiFiles(QStringList() << QStringLiteral("kleopatra.rc")); migrate.migrate(); qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: Application created"; // Initialize GpgME const GpgME::Error gpgmeInitError = GpgME::initializeLibrary(0); { const unsigned int threads = QThreadPool::globalInstance()->maxThreadCount(); QThreadPool::globalInstance()->setMaxThreadCount(qMax(2U, threads)); } if (gpgmeInitError) { KMessageBox::sorry(nullptr, xi18nc("@info", "The version of the GpgME library you are running against " "is older than the one that the GpgME++ library was built against." "Kleopatra will not function in this setting." "Please ask your administrator for help in resolving this issue."), i18nc("@title", "GpgME Too Old")); return EXIT_FAILURE; } qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: GPGME Initialized"; Kleo::ChecksumDefinition::setInstallPath(Kleo::gpg4winInstallPath()); Kleo::ArchiveDefinition::setInstallPath(Kleo::gnupgInstallPath()); int rc; Kleo::UiServer server(parser.value(QStringLiteral("uiserver-socket"))); try { qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: UiServer created"; QObject::connect(&server, &Kleo::UiServer::startKeyManagerRequested, &app, &KleopatraApplication::openOrRaiseMainWindow); QObject::connect(&server, &Kleo::UiServer::startConfigDialogRequested, &app, &KleopatraApplication::openOrRaiseConfigDialog); #define REGISTER( Command ) server.registerCommandFactory( std::shared_ptr( new Kleo::GenericAssuanCommandFactory ) ) REGISTER(CreateChecksumsCommand); REGISTER(DecryptCommand); REGISTER(DecryptFilesCommand); REGISTER(DecryptVerifyFilesCommand); REGISTER(EchoCommand); REGISTER(EncryptCommand); REGISTER(EncryptFilesCommand); REGISTER(EncryptSignFilesCommand); REGISTER(ImportFilesCommand); REGISTER(PrepEncryptCommand); REGISTER(PrepSignCommand); REGISTER(SelectCertificateCommand); REGISTER(SignCommand); REGISTER(SignEncryptFilesCommand); REGISTER(SignFilesCommand); REGISTER(VerifyChecksumsCommand); REGISTER(VerifyCommand); REGISTER(VerifyFilesCommand); #undef REGISTER server.start(); qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: UiServer started"; } catch (const std::exception &e) { qCDebug(KLEOPATRA_LOG) << "Failed to start UI Server: " << e.what(); #ifdef Q_OS_WIN // Once there actually is a plugin for other systems then Windows this // error should probably be shown, too. But currently only Windows users need // to care. QMessageBox::information(nullptr, i18n("GPG UI Server Error"), i18n("The Kleopatra GPG UI Server Module could not be initialized.
" "The error given was: %1
" "You can use Kleopatra as a certificate manager, but cryptographic plugins that " "rely on a GPG UI Server being present might not work correctly, or at all.
", QString::fromUtf8(e.what()).toHtmlEscaped())); #endif } const bool daemon = parser.isSet(QStringLiteral("daemon")); if (!daemon && app.isSessionRestored()) { app.restoreMainWindow(); } if (!selfCheck()) { return EXIT_FAILURE; } qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: SelfCheck completed"; fillKeyCache(&server); #ifndef QT_NO_SYSTEMTRAYICON app.startMonitoringSmartCard(); #endif app.setIgnoreNewInstance(false); if (!daemon) { const QString err = app.newInstance(parser); if (!err.isEmpty()) { std::cerr << i18n("Invalid arguments: %1", err).toLocal8Bit().constData() << "\n"; return EXIT_FAILURE; } qCDebug(KLEOPATRA_LOG) << "Startup timing:" << timer.elapsed() << "ms elapsed: new instance created"; } rc = app.exec(); app.setIgnoreNewInstance(true); QObject::disconnect(&server, &Kleo::UiServer::startKeyManagerRequested, &app, &KleopatraApplication::openOrRaiseMainWindow); QObject::disconnect(&server, &Kleo::UiServer::startConfigDialogRequested, &app, &KleopatraApplication::openOrRaiseConfigDialog); server.stop(); server.waitForStopped(); return rc; } diff --git a/src/utils/userinfo.cpp b/src/utils/userinfo.cpp index 54e4a3813..5693b6dfa 100644 --- a/src/utils/userinfo.cpp +++ b/src/utils/userinfo.cpp @@ -1,74 +1,84 @@ /* utils/userinfo.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "userinfo.h" #include #ifdef Q_OS_WIN #include "userinfo_win_p.h" #endif #include #include namespace { enum UserInfoDetail { UserInfoName, UserInfoEmailAddress }; static QString env_get_user_name(UserInfoDetail detail) { const auto var = qEnvironmentVariable("EMAIL"); if (!var.isEmpty()) { QString name, addrspec, comment; const auto result = KEmailAddress::splitAddress (var, name, addrspec, comment); if (result == KEmailAddress::AddressOk) { return (detail == UserInfoEmailAddress ? addrspec : name); } } return QString (); } } QString Kleo::userFullName() { const KEMailSettings e; auto name = e.getSetting(KEMailSettings::RealName); #ifdef Q_OS_WIN if (name.isEmpty()) { name = win_get_user_name(NameDisplay); } if (name.isEmpty()) { name = win_get_user_name(NameUnknown); } #endif if (name.isEmpty()) { name = env_get_user_name(UserInfoName); } return name; } QString Kleo::userEmailAddress() { const KEMailSettings e; auto mbox = e.getSetting(KEMailSettings::EmailAddress); #ifdef Q_OS_WIN if (mbox.isEmpty()) { mbox = win_get_user_name(NameUserPrincipal); } #endif if (mbox.isEmpty()) { mbox = env_get_user_name(UserInfoEmailAddress); } return mbox; } + +bool Kleo::userIsElevated() +{ +#ifdef Q_OS_WIN + static bool ret = win_user_is_elevated(); + return ret; +#else + return false; +#endif +} diff --git a/src/utils/userinfo.h b/src/utils/userinfo.h index 5e0b66320..976b08063 100644 --- a/src/utils/userinfo.h +++ b/src/utils/userinfo.h @@ -1,23 +1,32 @@ /* utils/userinfo.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef __KLEOPATRA_UTILS_USERINFO_H__ #define __KLEOPATRA_UTILS_USERINFO_H__ class QString; namespace Kleo { + /* Tries to obtain the users full name from the + * operating system to be useable for Key creation. */ QString userFullName(); + /* Tries to obtain the users email from the + * operating system to be useable for Key creation. */ QString userEmailAddress(); + + /* Checks if the user is running with an elevated security + * token. This is only a concept of Windows and returns + * false on other platforms. */ + bool userIsElevated(); } #endif // __KLEOPATRA_UTILS_USERINFO_H__ diff --git a/src/utils/userinfo_win.cpp b/src/utils/userinfo_win.cpp index 08a7f413c..da47cee03 100644 --- a/src/utils/userinfo_win.cpp +++ b/src/utils/userinfo_win.cpp @@ -1,45 +1,114 @@ /* utils/userinfo_win.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include "userinfo_win_p.h" #include "kleopatra_debug.h" /* Use Windows API to query the user name and email. EXTENDED_NAME_FORMAT is documented in MSDN */ QString win_get_user_name(EXTENDED_NAME_FORMAT what) { QString ret; wchar_t tmp[1]; ULONG nSize = 1; if (what == NameUnknown) { if (GetUserNameW (tmp, &nSize)) { qCWarning (KLEOPATRA_LOG) << "Got empty username"; return ret; } } else if (GetUserNameExW (what, tmp, &nSize)) { return ret; } /* nSize now contains the required size of the buffer */ wchar_t *buf = new wchar_t[nSize]; if (what == NameUnknown) { if (!GetUserNameW (buf, &nSize)) { qCWarning (KLEOPATRA_LOG) << "Failed to get username"; delete[] buf; return ret; } } else if (!GetUserNameExW (what, buf, &nSize)) { delete[] buf; return ret; } ret = QString::fromWCharArray (buf, nSize); delete[] buf; return ret.trimmed(); } + +static bool has_high_integrity(HANDLE hToken) +{ + PTOKEN_MANDATORY_LABEL integrity_label = NULL; + DWORD integrity_level = 0, + size = 0; + + if (hToken == NULL || hToken == INVALID_HANDLE_VALUE) { + qCWarning(KLEOPATRA_LOG) << "Invalid parameters."; + return false; + } + + /* Get the required size */ + if (GetTokenInformation(hToken, TokenIntegrityLevel, + NULL, 0, &size) || + GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + qCDebug(KLEOPATRA_LOG) << "Failed to get required size."; + return false; + } + integrity_label = (PTOKEN_MANDATORY_LABEL) LocalAlloc(0, size); + if (integrity_label == NULL) { + qCDebug(KLEOPATRA_LOG) << "Failed to allocate label."; + return false; + } + + if (!GetTokenInformation(hToken, TokenIntegrityLevel, + integrity_label, size, &size)) { + qCDebug(KLEOPATRA_LOG) << "Failed to get integrity level."; + LocalFree(integrity_label); + return false; + } + + /* Get the last integrity level */ + integrity_level = *GetSidSubAuthority(integrity_label->Label.Sid, + (DWORD)(UCHAR)(*GetSidSubAuthorityCount( + integrity_label->Label.Sid) - 1)); + + LocalFree (integrity_label); + + return integrity_level >= SECURITY_MANDATORY_HIGH_RID; +} + +bool win_user_is_elevated() +{ + HANDLE hToken = NULL; + bool ret = false; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { + DWORD elevation; + DWORD cbSize = sizeof(DWORD); + /* First get the elevation token and then check if that + * token has high integrity afterwards. */ + if (GetTokenInformation (hToken, TokenElevation, &elevation, + sizeof(TokenElevation), &cbSize)) { + qCDebug(KLEOPATRA_LOG) << "Got ElevationToken " << elevation; + ret = elevation; + } + } + /* Elevation will be true and ElevationType TokenElevationTypeFull even + if the token is a user token created by SAFER so we additionally + check the integrity level of the token which will only be high in + the real elevated process and medium otherwise. */ + + ret = ret && has_high_integrity(hToken); + + if (hToken) { + CloseHandle(hToken); + } + return ret; +} diff --git a/src/utils/userinfo_win_p.h b/src/utils/userinfo_win_p.h index a6d7242fa..09f7a86e3 100644 --- a/src/utils/userinfo_win_p.h +++ b/src/utils/userinfo_win_p.h @@ -1,21 +1,23 @@ /* utils/userinfo_win_p.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef __KLEOPATRA_UTILS_USERINFO_WIN_P_H__ #define __KLEOPATRA_UTILS_USERINFO_WIN_P_H__ #include #define SECURITY_WIN32 #include // For GetUserNameEx #include QString win_get_user_name(EXTENDED_NAME_FORMAT what); +bool win_user_is_elevated(); + #endif // __KLEOPATRA_UTILS_USERINFO_WIN_P_H__