2017-10-29 15:17:24 +00:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2017 Toni Spets <toni.spets@iki.fi>
|
|
|
|
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
|
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 2 or (at your option)
|
|
|
|
|
* version 3 of the License.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "KeeAgentSettings.h"
|
2019-11-02 09:25:13 +00:00
|
|
|
#include "core/Tools.h"
|
2017-10-29 15:17:24 +00:00
|
|
|
|
2019-11-09 18:45:08 +00:00
|
|
|
bool KeeAgentSettings::operator==(const KeeAgentSettings& other) const
|
2017-10-29 15:17:24 +00:00
|
|
|
{
|
2018-11-01 03:27:38 +00:00
|
|
|
// clang-format off
|
2018-03-31 20:01:30 +00:00
|
|
|
return (m_allowUseOfSshKey == other.m_allowUseOfSshKey && m_addAtDatabaseOpen == other.m_addAtDatabaseOpen
|
2017-10-29 15:17:24 +00:00
|
|
|
&& m_removeAtDatabaseClose == other.m_removeAtDatabaseClose
|
|
|
|
|
&& m_useConfirmConstraintWhenAdding == other.m_useConfirmConstraintWhenAdding
|
|
|
|
|
&& m_useLifetimeConstraintWhenAdding == other.m_useLifetimeConstraintWhenAdding
|
|
|
|
|
&& m_lifetimeConstraintDuration == other.m_lifetimeConstraintDuration
|
|
|
|
|
&& m_selectedType == other.m_selectedType
|
|
|
|
|
&& m_attachmentName == other.m_attachmentName
|
|
|
|
|
&& m_saveAttachmentToTempFile == other.m_saveAttachmentToTempFile
|
|
|
|
|
&& m_fileName == other.m_fileName);
|
2018-11-01 03:27:38 +00:00
|
|
|
// clang-format on
|
2017-10-29 15:17:24 +00:00
|
|
|
}
|
|
|
|
|
|
2019-11-09 18:45:08 +00:00
|
|
|
bool KeeAgentSettings::operator!=(const KeeAgentSettings& other) const
|
2017-10-29 15:17:24 +00:00
|
|
|
{
|
|
|
|
|
return !(*this == other);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-09 18:45:08 +00:00
|
|
|
/**
|
|
|
|
|
* Test if this instance is at default settings.
|
|
|
|
|
*
|
|
|
|
|
* @return true if is at default settings
|
|
|
|
|
*/
|
|
|
|
|
bool KeeAgentSettings::isDefault() const
|
2017-10-29 15:17:24 +00:00
|
|
|
{
|
|
|
|
|
KeeAgentSettings defaultSettings;
|
|
|
|
|
return (*this == defaultSettings);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-09 18:45:08 +00:00
|
|
|
/**
|
|
|
|
|
* Get last error as a QString.
|
|
|
|
|
*
|
|
|
|
|
* @return translated error message
|
|
|
|
|
*/
|
|
|
|
|
const QString KeeAgentSettings::errorString() const
|
|
|
|
|
{
|
|
|
|
|
return m_error;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-29 15:17:24 +00:00
|
|
|
bool KeeAgentSettings::allowUseOfSshKey() const
|
|
|
|
|
{
|
|
|
|
|
return m_allowUseOfSshKey;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool KeeAgentSettings::addAtDatabaseOpen() const
|
|
|
|
|
{
|
|
|
|
|
return m_addAtDatabaseOpen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool KeeAgentSettings::removeAtDatabaseClose() const
|
|
|
|
|
{
|
|
|
|
|
return m_removeAtDatabaseClose;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool KeeAgentSettings::useConfirmConstraintWhenAdding() const
|
|
|
|
|
{
|
|
|
|
|
return m_useConfirmConstraintWhenAdding;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool KeeAgentSettings::useLifetimeConstraintWhenAdding() const
|
|
|
|
|
{
|
|
|
|
|
return m_useLifetimeConstraintWhenAdding;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int KeeAgentSettings::lifetimeConstraintDuration() const
|
|
|
|
|
{
|
|
|
|
|
return m_lifetimeConstraintDuration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString KeeAgentSettings::selectedType() const
|
|
|
|
|
{
|
|
|
|
|
return m_selectedType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString KeeAgentSettings::attachmentName() const
|
|
|
|
|
{
|
|
|
|
|
return m_attachmentName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool KeeAgentSettings::saveAttachmentToTempFile() const
|
|
|
|
|
{
|
|
|
|
|
return m_saveAttachmentToTempFile;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString KeeAgentSettings::fileName() const
|
|
|
|
|
{
|
|
|
|
|
return m_fileName;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-02 09:25:13 +00:00
|
|
|
const QString KeeAgentSettings::fileNameEnvSubst(QProcessEnvironment environment) const
|
|
|
|
|
{
|
|
|
|
|
return Tools::envSubstitute(m_fileName, environment);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-29 15:17:24 +00:00
|
|
|
void KeeAgentSettings::setAllowUseOfSshKey(bool allowUseOfSshKey)
|
|
|
|
|
{
|
|
|
|
|
m_allowUseOfSshKey = allowUseOfSshKey;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void KeeAgentSettings::setAddAtDatabaseOpen(bool addAtDatabaseOpen)
|
|
|
|
|
{
|
|
|
|
|
m_addAtDatabaseOpen = addAtDatabaseOpen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void KeeAgentSettings::setRemoveAtDatabaseClose(bool removeAtDatabaseClose)
|
|
|
|
|
{
|
|
|
|
|
m_removeAtDatabaseClose = removeAtDatabaseClose;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void KeeAgentSettings::setUseConfirmConstraintWhenAdding(bool useConfirmConstraintWhenAdding)
|
|
|
|
|
{
|
|
|
|
|
m_useConfirmConstraintWhenAdding = useConfirmConstraintWhenAdding;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void KeeAgentSettings::setUseLifetimeConstraintWhenAdding(bool useLifetimeConstraintWhenAdding)
|
|
|
|
|
{
|
|
|
|
|
m_useLifetimeConstraintWhenAdding = useLifetimeConstraintWhenAdding;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void KeeAgentSettings::setLifetimeConstraintDuration(int lifetimeConstraintDuration)
|
|
|
|
|
{
|
|
|
|
|
m_lifetimeConstraintDuration = lifetimeConstraintDuration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void KeeAgentSettings::setSelectedType(const QString& selectedType)
|
|
|
|
|
{
|
|
|
|
|
m_selectedType = selectedType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void KeeAgentSettings::setAttachmentName(const QString& attachmentName)
|
|
|
|
|
{
|
|
|
|
|
m_attachmentName = attachmentName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void KeeAgentSettings::setSaveAttachmentToTempFile(bool saveAttachmentToTempFile)
|
|
|
|
|
{
|
|
|
|
|
m_saveAttachmentToTempFile = saveAttachmentToTempFile;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void KeeAgentSettings::setFileName(const QString& fileName)
|
|
|
|
|
{
|
|
|
|
|
m_fileName = fileName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool KeeAgentSettings::readBool(QXmlStreamReader& reader)
|
|
|
|
|
{
|
|
|
|
|
reader.readNext();
|
|
|
|
|
bool ret = (reader.text().startsWith("t", Qt::CaseInsensitive));
|
|
|
|
|
reader.readNext(); // tag end
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int KeeAgentSettings::readInt(QXmlStreamReader& reader)
|
|
|
|
|
{
|
|
|
|
|
reader.readNext();
|
|
|
|
|
int ret = reader.text().toInt();
|
|
|
|
|
reader.readNext(); // tag end
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-09 18:45:08 +00:00
|
|
|
/**
|
|
|
|
|
* Read settings from an XML document.
|
|
|
|
|
*
|
|
|
|
|
* Sets error string on error.
|
|
|
|
|
*
|
|
|
|
|
* @param ba XML document
|
|
|
|
|
* @return success
|
|
|
|
|
*/
|
2017-10-29 15:17:24 +00:00
|
|
|
bool KeeAgentSettings::fromXml(const QByteArray& ba)
|
|
|
|
|
{
|
|
|
|
|
QXmlStreamReader reader;
|
|
|
|
|
reader.addData(ba);
|
|
|
|
|
|
|
|
|
|
if (reader.error() || !reader.readNextStartElement()) {
|
2019-11-09 18:45:08 +00:00
|
|
|
m_error = reader.errorString();
|
2017-10-29 15:17:24 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (reader.qualifiedName() != "EntrySettings") {
|
2019-11-09 18:45:08 +00:00
|
|
|
m_error = QCoreApplication::translate("KeeAgentSettings", "Invalid KeeAgent settings file structure.");
|
2017-10-29 15:17:24 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (!reader.error() && reader.readNextStartElement()) {
|
|
|
|
|
if (reader.name() == "AllowUseOfSshKey") {
|
|
|
|
|
m_allowUseOfSshKey = readBool(reader);
|
|
|
|
|
} else if (reader.name() == "AddAtDatabaseOpen") {
|
|
|
|
|
m_addAtDatabaseOpen = readBool(reader);
|
|
|
|
|
} else if (reader.name() == "RemoveAtDatabaseClose") {
|
|
|
|
|
m_removeAtDatabaseClose = readBool(reader);
|
|
|
|
|
} else if (reader.name() == "UseConfirmConstraintWhenAdding") {
|
|
|
|
|
m_useConfirmConstraintWhenAdding = readBool(reader);
|
|
|
|
|
} else if (reader.name() == "UseLifetimeConstraintWhenAdding") {
|
|
|
|
|
m_useLifetimeConstraintWhenAdding = readBool(reader);
|
|
|
|
|
} else if (reader.name() == "LifetimeConstraintDuration") {
|
|
|
|
|
m_lifetimeConstraintDuration = readInt(reader);
|
|
|
|
|
} else if (reader.name() == "Location") {
|
|
|
|
|
while (!reader.error() && reader.readNextStartElement()) {
|
|
|
|
|
if (reader.name() == "SelectedType") {
|
|
|
|
|
reader.readNext();
|
|
|
|
|
m_selectedType = reader.text().toString();
|
|
|
|
|
reader.readNext();
|
|
|
|
|
} else if (reader.name() == "AttachmentName") {
|
|
|
|
|
reader.readNext();
|
|
|
|
|
m_attachmentName = reader.text().toString();
|
|
|
|
|
reader.readNext();
|
|
|
|
|
} else if (reader.name() == "SaveAttachmentToTempFile") {
|
|
|
|
|
m_saveAttachmentToTempFile = readBool(reader);
|
|
|
|
|
} else if (reader.name() == "FileName") {
|
|
|
|
|
reader.readNext();
|
|
|
|
|
m_fileName = reader.text().toString();
|
|
|
|
|
reader.readNext();
|
|
|
|
|
} else {
|
|
|
|
|
qWarning() << "Skipping location element" << reader.name();
|
|
|
|
|
reader.skipCurrentElement();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
qWarning() << "Skipping element" << reader.name();
|
|
|
|
|
reader.skipCurrentElement();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-09 18:45:08 +00:00
|
|
|
/**
|
|
|
|
|
* Write settings to an XML document.
|
|
|
|
|
*
|
|
|
|
|
* @return XML document
|
|
|
|
|
*/
|
|
|
|
|
QByteArray KeeAgentSettings::toXml() const
|
2017-10-29 15:17:24 +00:00
|
|
|
{
|
|
|
|
|
QByteArray ba;
|
|
|
|
|
QXmlStreamWriter writer(&ba);
|
|
|
|
|
|
|
|
|
|
// real KeeAgent can only read UTF-16
|
|
|
|
|
writer.setCodec(QTextCodec::codecForName("UTF-16"));
|
|
|
|
|
writer.setAutoFormatting(true);
|
|
|
|
|
writer.setAutoFormattingIndent(2);
|
|
|
|
|
|
|
|
|
|
writer.writeStartDocument();
|
|
|
|
|
|
|
|
|
|
writer.writeStartElement("EntrySettings");
|
|
|
|
|
writer.writeAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
|
|
|
|
|
writer.writeAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
|
|
|
|
|
|
|
|
|
|
writer.writeTextElement("AllowUseOfSshKey", m_allowUseOfSshKey ? "true" : "false");
|
|
|
|
|
writer.writeTextElement("AddAtDatabaseOpen", m_addAtDatabaseOpen ? "true" : "false");
|
|
|
|
|
writer.writeTextElement("RemoveAtDatabaseClose", m_removeAtDatabaseClose ? "true" : "false");
|
|
|
|
|
writer.writeTextElement("UseConfirmConstraintWhenAdding", m_useConfirmConstraintWhenAdding ? "true" : "false");
|
|
|
|
|
writer.writeTextElement("UseLifetimeConstraintWhenAdding", m_useLifetimeConstraintWhenAdding ? "true" : "false");
|
|
|
|
|
writer.writeTextElement("LifetimeConstraintDuration", QString::number(m_lifetimeConstraintDuration));
|
|
|
|
|
|
|
|
|
|
writer.writeStartElement("Location");
|
|
|
|
|
writer.writeTextElement("SelectedType", m_selectedType);
|
|
|
|
|
|
|
|
|
|
if (!m_attachmentName.isEmpty()) {
|
|
|
|
|
writer.writeTextElement("AttachmentName", m_attachmentName);
|
|
|
|
|
} else {
|
|
|
|
|
writer.writeEmptyElement("AttachmentName");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.writeTextElement("SaveAttachmentToTempFile", m_saveAttachmentToTempFile ? "true" : "false");
|
|
|
|
|
|
|
|
|
|
if (!m_fileName.isEmpty()) {
|
|
|
|
|
writer.writeTextElement("FileName", m_fileName);
|
|
|
|
|
} else {
|
|
|
|
|
writer.writeEmptyElement("FileName");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.writeEndElement(); // Location
|
|
|
|
|
writer.writeEndElement(); // EntrySettings
|
|
|
|
|
writer.writeEndDocument();
|
|
|
|
|
|
|
|
|
|
return ba;
|
|
|
|
|
}
|
2019-11-09 18:45:08 +00:00
|
|
|
|
2018-05-10 13:12:36 +00:00
|
|
|
/**
|
|
|
|
|
* Check if an entry has KeeAgent settings configured
|
|
|
|
|
*
|
|
|
|
|
* @param entry Entry to check the attachment
|
|
|
|
|
* @return true if XML document exists
|
|
|
|
|
*/
|
|
|
|
|
bool KeeAgentSettings::inEntry(const Entry* entry)
|
|
|
|
|
{
|
|
|
|
|
return entry->attachments()->hasKey("KeeAgent.settings");
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-09 18:45:08 +00:00
|
|
|
/**
|
|
|
|
|
* Read settings from an entry as an XML attachment.
|
|
|
|
|
*
|
|
|
|
|
* Sets error string on error.
|
|
|
|
|
*
|
|
|
|
|
* @param entry Entry to read the attachment from
|
|
|
|
|
* @return true if XML document was loaded
|
|
|
|
|
*/
|
|
|
|
|
bool KeeAgentSettings::fromEntry(const Entry* entry)
|
|
|
|
|
{
|
|
|
|
|
return fromXml(entry->attachments()->value("KeeAgent.settings"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Write settings to an entry as an XML attachment.
|
|
|
|
|
*
|
|
|
|
|
* @param entry Entry to create the attachment to
|
|
|
|
|
*/
|
|
|
|
|
void KeeAgentSettings::toEntry(Entry* entry) const
|
|
|
|
|
{
|
|
|
|
|
if (isDefault()) {
|
|
|
|
|
if (entry->attachments()->hasKey("KeeAgent.settings")) {
|
|
|
|
|
entry->attachments()->remove("KeeAgent.settings");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
entry->attachments()->set("KeeAgent.settings", toXml());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test if a SSH key is currently set to be used
|
|
|
|
|
*
|
|
|
|
|
* @return true if key is configured
|
|
|
|
|
*/
|
|
|
|
|
bool KeeAgentSettings::keyConfigured() const
|
|
|
|
|
{
|
|
|
|
|
if (m_selectedType == "attachment") {
|
|
|
|
|
return !m_attachmentName.isEmpty();
|
|
|
|
|
} else {
|
|
|
|
|
return !m_fileName.isEmpty();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read a SSH key based on settings from entry to key.
|
|
|
|
|
*
|
|
|
|
|
* Sets error string on error.
|
|
|
|
|
*
|
|
|
|
|
* @param entry input entry to read attachment and decryption key
|
|
|
|
|
* @param key output key object
|
|
|
|
|
* @param decrypt avoid private key decryption if possible (old RSA keys are always decrypted)
|
|
|
|
|
* @return true if key was properly opened
|
|
|
|
|
*/
|
|
|
|
|
bool KeeAgentSettings::toOpenSSHKey(const Entry* entry, OpenSSHKey& key, bool decrypt)
|
|
|
|
|
{
|
|
|
|
|
QString fileName;
|
|
|
|
|
QByteArray privateKeyData;
|
|
|
|
|
|
|
|
|
|
if (m_selectedType == "attachment") {
|
|
|
|
|
fileName = m_attachmentName;
|
|
|
|
|
privateKeyData = entry->attachments()->value(fileName);
|
|
|
|
|
} else {
|
2019-11-02 09:25:13 +00:00
|
|
|
QFile localFile(fileNameEnvSubst());
|
2019-11-09 18:45:08 +00:00
|
|
|
QFileInfo localFileInfo(localFile);
|
|
|
|
|
fileName = localFileInfo.fileName();
|
|
|
|
|
|
|
|
|
|
if (localFile.fileName().isEmpty()) {
|
|
|
|
|
m_error = QCoreApplication::translate("KeeAgentSettings", "Private key is empty");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (localFile.size() > 1024 * 1024) {
|
|
|
|
|
m_error = QCoreApplication::translate("KeeAgentSettings", "File too large to be a private key");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!localFile.open(QIODevice::ReadOnly)) {
|
|
|
|
|
m_error = QCoreApplication::translate("KeeAgentSettings", "Failed to open private key");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
privateKeyData = localFile.readAll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (privateKeyData.isEmpty()) {
|
|
|
|
|
m_error = QCoreApplication::translate("KeeAgentSettings", "Private key is empty");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!key.parsePKCS1PEM(privateKeyData)) {
|
|
|
|
|
m_error = key.errorString();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (key.encrypted() && (decrypt || key.publicParts().isEmpty())) {
|
|
|
|
|
if (!key.openKey(entry->password())) {
|
|
|
|
|
m_error = key.errorString();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (key.comment().isEmpty()) {
|
|
|
|
|
key.setComment(entry->username());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (key.comment().isEmpty()) {
|
|
|
|
|
key.setComment(fileName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|