Merge branch 'release/2.2.2' into develop

This commit is contained in:
Janek Bevendorff 2017-10-12 14:10:20 +02:00
commit ab21f718ba
14 changed files with 133 additions and 40 deletions

View File

@ -52,6 +52,9 @@ set(KEEPASSXC_VERSION_MINOR "2")
set(KEEPASSXC_VERSION_PATCH "1") set(KEEPASSXC_VERSION_PATCH "1")
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}") set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
# Special flag for snap builds
set(KEEPASSXC_SNAP_BUILD OFF CACHE BOOL "Set whether this is a build for snap or not")
if("${CMAKE_C_COMPILER}" MATCHES "clang$" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") if("${CMAKE_C_COMPILER}" MATCHES "clang$" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_COMPILER_IS_CLANG 1) set(CMAKE_COMPILER_IS_CLANG 1)
endif() endif()

View File

@ -12,7 +12,7 @@ apps:
keepassxc: keepassxc:
command: desktop-launch keepassxc command: desktop-launch keepassxc
plugs: [unity7, x11, opengl, gsettings, home, network, network-bind, removable-media, raw-usb] plugs: [unity7, x11, opengl, gsettings, home, network, network-bind, removable-media, raw-usb]
desktop: share/applications/keepassxc.desktop desktop: usr/share/applications/keepassxc.desktop
cli: cli:
command: keepassxc-cli command: keepassxc-cli
plugs: [gsettings, home, removable-media, raw-usb] plugs: [gsettings, home, removable-media, raw-usb]
@ -23,6 +23,8 @@ parts:
plugin: cmake plugin: cmake
configflags: configflags:
- -DCMAKE_BUILD_TYPE=Release - -DCMAKE_BUILD_TYPE=Release
- -DCMAKE_INSTALL_PREFIX=/usr
- -DKEEPASSXC_SNAP_BUILD=ON
- -DWITH_TESTS=OFF - -DWITH_TESTS=OFF
- -DWITH_XC_AUTOTYPE=ON - -DWITH_XC_AUTOTYPE=ON
- -DWITH_XC_HTTP=ON - -DWITH_XC_HTTP=ON
@ -39,6 +41,8 @@ parts:
- libxtst-dev - libxtst-dev
- libyubikey-dev - libyubikey-dev
- libykpers-1-dev - libykpers-1-dev
install: |
sed -i 's|Icon=keepassxc|Icon=${SNAP}/usr/share/icons/hicolor/256x256/apps/keepassxc.png|g' $SNAPCRAFT_PART_INSTALL/usr/share/applications/keepassxc.desktop
after: [desktop-qt5] after: [desktop-qt5]
# Redefine desktop-qt5 stage packages to work with Ubuntu 17.04 # Redefine desktop-qt5 stage packages to work with Ubuntu 17.04
@ -55,10 +59,12 @@ parts:
- locales-all - locales-all
# Overcome limitation in snapd to support URL loading (CTRL+U) # Overcome limitation in snapd to support URL loading (CTRL+U)
# client needs to install "snapd-xdg-open" on their system
snapd-xdg-open: snapd-xdg-open:
source: https://github.com/ubuntu-core/snapd-xdg-open.git source: https://github.com/ubuntu-core/snapd-xdg-open.git
plugin: dump source-depth: 1
organize: plugin: nil
data/xdg-open: bin/xdg-open install: |
install -D -t $SNAPCRAFT_PART_INSTALL/usr/bin/ data/xdg-open
stage-packages: stage-packages:
- dbus - dbus

View File

@ -16,6 +16,8 @@
#cmakedefine WITH_XC_AUTOTYPE #cmakedefine WITH_XC_AUTOTYPE
#cmakedefine WITH_XC_YUBIKEY #cmakedefine WITH_XC_YUBIKEY
#cmakedefine KEEPASSXC_SNAP_BUILD
#cmakedefine HAVE_PR_SET_DUMPABLE 1 #cmakedefine HAVE_PR_SET_DUMPABLE 1
#cmakedefine HAVE_RLIMIT_CORE 1 #cmakedefine HAVE_RLIMIT_CORE 1
#cmakedefine HAVE_PT_DENY_ATTACH 1 #cmakedefine HAVE_PT_DENY_ATTACH 1

View File

@ -801,28 +801,31 @@ QString Entry::resolvePlaceholder(const QString& str) const
QString Entry::resolveUrl(const QString& url) const QString Entry::resolveUrl(const QString& url) const
{ {
#ifdef WITH_XC_HTTP
QString newUrl = url; QString newUrl = url;
if (!url.contains("://")) { if (!url.isEmpty() && !url.contains("://")) {
// URL doesn't have a protocol, add https by default // URL doesn't have a protocol, add https by default
newUrl.prepend("https://"); newUrl.prepend("https://");
} }
QUrl tempUrl = QUrl(newUrl);
if (tempUrl.isValid()) { if (newUrl.startsWith("cmd://")) {
if (tempUrl.scheme() == "cmd") { QStringList cmdList = newUrl.split(" ");
// URL is a cmd, hopefully the second argument is an URL for (int i=1; i < cmdList.size(); ++i) {
QStringList cmd = newUrl.split(" "); // Don't pass arguments to the resolveUrl function (they look like URL's)
if (cmd.size() > 1) { if (!cmdList[i].startsWith("-") && !cmdList[i].startsWith("/")) {
return resolveUrl(cmd[1].remove("'").remove("\"")); return resolveUrl(cmdList[i].remove(QRegExp("'|\"")));
} }
} else if (tempUrl.scheme() == "http" || tempUrl.scheme() == "https") {
// URL is nice
return tempUrl.url();
} }
// No URL in this command
return QString("");
} }
#else
Q_UNUSED(url); // Validate the URL
#endif QUrl tempUrl = QUrl(newUrl);
if (tempUrl.isValid() && (tempUrl.scheme() == "http" || tempUrl.scheme() == "https")) {
return tempUrl.url();
}
// No valid http URL's found
return QString(""); return QString("");
} }

View File

@ -91,17 +91,29 @@ QString FilePath::pluginPath(const QString& name)
QIcon FilePath::applicationIcon() QIcon FilePath::applicationIcon()
{ {
#ifdef KEEPASSXC_SNAP_BUILD
return icon("apps", "keepassxc", false);
#else
return icon("apps", "keepassxc"); return icon("apps", "keepassxc");
#endif
} }
QIcon FilePath::trayIconLocked() QIcon FilePath::trayIconLocked()
{ {
#ifdef KEEPASSXC_SNAP_BUILD
return icon("apps", "keepassxc-locked", false);
#else
return icon("apps", "keepassxc-locked"); return icon("apps", "keepassxc-locked");
#endif
} }
QIcon FilePath::trayIconUnlocked() QIcon FilePath::trayIconUnlocked()
{ {
#ifdef KEEPASSXC_SNAP_BUILD
return icon("apps", "keepassxc-unlocked", false);
#else
return icon("apps", "keepassxc-unlocked"); return icon("apps", "keepassxc-unlocked");
#endif
} }
QIcon FilePath::icon(const QString& category, const QString& name, bool fromTheme) QIcon FilePath::icon(const QString& category, const QString& name, bool fromTheme)

View File

@ -443,7 +443,7 @@ void Metadata::copyCustomIcons(const QSet<Uuid>& iconList, const Metadata* other
QByteArray Metadata::hashImage(const QImage& image) QByteArray Metadata::hashImage(const QImage& image)
{ {
auto data = QByteArray((char*)image.bits(), image.byteCount()); auto data = QByteArray(reinterpret_cast<const char*>(image.bits()), image.byteCount());
return QCryptographicHash::hash(data, QCryptographicHash::Md5); return QCryptographicHash::hash(data, QCryptographicHash::Md5);
} }

View File

@ -70,9 +70,6 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
connect(m_ui->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey())); connect(m_ui->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey()));
connect(m_ui->comboChallengeResponse, SIGNAL(activated(int)), SLOT(activateChallengeResponse())); connect(m_ui->comboChallengeResponse, SIGNAL(activated(int)), SLOT(activateChallengeResponse()));
connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection);
connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection);
#else #else
m_ui->checkChallengeResponse->setVisible(false); m_ui->checkChallengeResponse->setVisible(false);
m_ui->buttonRedetectYubikey->setVisible(false); m_ui->buttonRedetectYubikey->setVisible(false);
@ -98,10 +95,24 @@ void DatabaseOpenWidget::showEvent(QShowEvent* event)
m_ui->editPassword->setFocus(); m_ui->editPassword->setFocus();
#ifdef WITH_XC_YUBIKEY #ifdef WITH_XC_YUBIKEY
connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection);
connect(YubiKey::instance(), SIGNAL(detectComplete()), SLOT(yubikeyDetectComplete()), Qt::QueuedConnection);
connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection);
pollYubikey(); pollYubikey();
#endif #endif
} }
void DatabaseOpenWidget::hideEvent(QHideEvent* event)
{
DialogyWidget::hideEvent(event);
#ifdef WITH_XC_YUBIKEY
// Don't listen to any Yubikey events if we are hidden
disconnect(YubiKey::instance(), 0, this, 0);
#endif
}
void DatabaseOpenWidget::load(const QString& filename) void DatabaseOpenWidget::load(const QString& filename)
{ {
m_filename = filename; m_filename = filename;
@ -283,10 +294,6 @@ void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking)
YkChallengeResponseKey yk(slot, blocking); YkChallengeResponseKey yk(slot, blocking);
// add detected YubiKey to combo box and encode blocking mode in LSB, slot number in second LSB // add detected YubiKey to combo box and encode blocking mode in LSB, slot number in second LSB
m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant((slot << 1) | blocking)); m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant((slot << 1) | blocking));
m_ui->comboChallengeResponse->setEnabled(true);
m_ui->checkChallengeResponse->setEnabled(true);
m_ui->buttonRedetectYubikey->setEnabled(true);
m_ui->yubikeyProgress->setVisible(false);
if (config()->get("RememberLastKeyFiles").toBool()) { if (config()->get("RememberLastKeyFiles").toBool()) {
QHash<QString, QVariant> lastChallengeResponse = config()->get("LastChallengeResponse").toHash(); QHash<QString, QVariant> lastChallengeResponse = config()->get("LastChallengeResponse").toHash();
@ -296,6 +303,14 @@ void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking)
} }
} }
void DatabaseOpenWidget::yubikeyDetectComplete()
{
m_ui->comboChallengeResponse->setEnabled(true);
m_ui->checkChallengeResponse->setEnabled(true);
m_ui->buttonRedetectYubikey->setEnabled(true);
m_ui->yubikeyProgress->setVisible(false);
}
void DatabaseOpenWidget::noYubikeyFound() void DatabaseOpenWidget::noYubikeyFound()
{ {
m_ui->buttonRedetectYubikey->setEnabled(true); m_ui->buttonRedetectYubikey->setEnabled(true);

View File

@ -51,6 +51,7 @@ signals:
protected: protected:
void showEvent(QShowEvent* event) override; void showEvent(QShowEvent* event) override;
void hideEvent(QHideEvent* event) override;
CompositeKey databaseKey(); CompositeKey databaseKey();
protected slots: protected slots:
@ -63,6 +64,7 @@ private slots:
void activateChallengeResponse(); void activateChallengeResponse();
void browseKeyFile(); void browseKeyFile();
void yubikeyDetected(int slot, bool blocking); void yubikeyDetected(int slot, bool blocking);
void yubikeyDetectComplete();
void noYubikeyFound(); void noYubikeyFound();
protected: protected:

View File

@ -302,6 +302,7 @@ void EditWidgetIcons::addCustomIcon(const QImage &icon)
} }
// Select the new or existing icon // Select the new or existing icon
updateRadioButtonCustomIcons();
QModelIndex index = m_customIconModel->indexFromUuid(uuid); QModelIndex index = m_customIconModel->indexFromUuid(uuid);
m_ui->customIconsView->setCurrentIndex(index); m_ui->customIconsView->setCurrentIndex(index);
} }

View File

@ -157,7 +157,7 @@ void SettingsWidget::loadSettings()
m_secUi->lockDatabaseIdleSpinBox->setValue(config()->get("security/lockdatabaseidlesec").toInt()); m_secUi->lockDatabaseIdleSpinBox->setValue(config()->get("security/lockdatabaseidlesec").toInt());
m_secUi->lockDatabaseMinimizeCheckBox->setChecked(config()->get("security/lockdatabaseminimize").toBool()); m_secUi->lockDatabaseMinimizeCheckBox->setChecked(config()->get("security/lockdatabaseminimize").toBool());
m_secUi->lockDatabaseOnScreenLockCheckBox->setChecked(config()->get("security/lockdatabasescreenlock").toBool()); m_secUi->lockDatabaseOnScreenLockCheckBox->setChecked(config()->get("security/lockdatabasescreenlock").toBool());
m_secUi->lockDatabaseOnScreenLockCheckBox->setChecked(config()->get("security/IconDownloadFallbackToGoogle").toBool()); m_secUi->fallbackToGoogle->setChecked(config()->get("security/IconDownloadFallbackToGoogle").toBool());
m_secUi->passwordCleartextCheckBox->setChecked(config()->get("security/passwordscleartext").toBool()); m_secUi->passwordCleartextCheckBox->setChecked(config()->get("security/passwordscleartext").toBool());
m_secUi->passwordRepeatCheckBox->setChecked(config()->get("security/passwordsrepeat").toBool()); m_secUi->passwordRepeatCheckBox->setChecked(config()->get("security/passwordsrepeat").toBool());

View File

@ -25,6 +25,7 @@
#include <ykdef.h> #include <ykdef.h>
#include <ykstatus.h> #include <ykstatus.h>
#include "core/Tools.h"
#include "core/Global.h" #include "core/Global.h"
#include "crypto/Random.h" #include "crypto/Random.h"
@ -112,23 +113,36 @@ bool YubiKey::deinit()
void YubiKey::detect() void YubiKey::detect()
{ {
if (init()) { bool found = false;
for (int i = 1; i < 3; i++) {
YubiKey::ChallengeResult result;
QByteArray rand = randomGen()->randomArray(1);
QByteArray resp;
if (init()) {
YubiKey::ChallengeResult result;
QByteArray rand = randomGen()->randomArray(1);
QByteArray resp;
// Check slot 1 and 2 for Challenge-Response HMAC capability
for (int i = 1; i <= 2; ++i) {
result = challenge(i, false, rand, resp); result = challenge(i, false, rand, resp);
if (result == YubiKey::ALREADY_RUNNING) { if (result == ALREADY_RUNNING) {
emit alreadyRunning(); // Try this slot again after waiting
return; Tools::sleep(300);
} else if (result != YubiKey::ERROR) { result = challenge(i, false, rand, resp);
emit detected(i, result == YubiKey::WOULDBLOCK);
return;
} }
if (result != ALREADY_RUNNING && result != ERROR) {
emit detected(i, result == WOULDBLOCK);
found = true;
}
// Wait between slots to let the yubikey settle
Tools::sleep(150);
} }
} }
emit notFound();
if (!found) {
emit notFound();
} else {
emit detectComplete();
}
} }
bool YubiKey::getSerial(unsigned int& serial) bool YubiKey::getSerial(unsigned int& serial)
@ -160,6 +174,7 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte
} }
// yk_challenge_response() insists on 64 byte response buffer */ // yk_challenge_response() insists on 64 byte response buffer */
response.clear();
response.resize(64); response.resize(64);
/* The challenge sent to the yubikey should always be 64 bytes for /* The challenge sent to the yubikey should always be 64 bytes for

View File

@ -87,6 +87,11 @@ signals:
*/ */
void detected(int slot, bool blocking); void detected(int slot, bool blocking);
/**
* Emitted when detection is complete
*/
void detectComplete();
/** /**
* Emitted when the YubiKey was challenged and has returned a response. * Emitted when the YubiKey was challenged and has returned a response.
*/ */

View File

@ -16,6 +16,7 @@
*/ */
#include "TestEntry.h" #include "TestEntry.h"
#include "config-keepassx-tests.h"
#include <QTest> #include <QTest>
@ -130,3 +131,30 @@ void TestEntry::testClone()
delete entryOrg; delete entryOrg;
} }
void TestEntry::testResolveUrl()
{
Entry* entry = new Entry();
QString testUrl("www.google.com");
QString testCmd("cmd://firefox " + testUrl);
QString testComplexCmd("cmd://firefox --start-now --url 'http://" + testUrl + "' --quit");
QString nonHttpUrl("ftp://google.com");
QString noUrl("random text inserted here");
// Test standard URL's
QCOMPARE(entry->resolveUrl(""), QString(""));
QCOMPARE(entry->resolveUrl(testUrl), "https://" + testUrl);
QCOMPARE(entry->resolveUrl("http://" + testUrl), "http://" + testUrl);
// Test cmd:// with no URL
QCOMPARE(entry->resolveUrl("cmd://firefox"), QString(""));
QCOMPARE(entry->resolveUrl("cmd://firefox --no-url"), QString(""));
// Test cmd:// with URL's
QCOMPARE(entry->resolveUrl(testCmd), "https://" + testUrl);
QCOMPARE(entry->resolveUrl(testComplexCmd), "http://" + testUrl);
// Test non-http URL
QCOMPARE(entry->resolveUrl(nonHttpUrl), QString(""));
// Test no URL
QCOMPARE(entry->resolveUrl(noUrl), QString(""));
delete entry;
}

View File

@ -31,6 +31,7 @@ private slots:
void testHistoryItemDeletion(); void testHistoryItemDeletion();
void testCopyDataFrom(); void testCopyDataFrom();
void testClone(); void testClone();
void testResolveUrl();
}; };
#endif // KEEPASSX_TESTENTRY_H #endif // KEEPASSX_TESTENTRY_H