SSH Agent: Add support for OpenSSH 8.2 FIDO/U2F keys

Closes #4334
This commit is contained in:
Toni Spets 2020-02-16 14:44:18 +02:00 committed by Jonathan White
parent c07a57d141
commit 860fcfd78d
10 changed files with 183 additions and 19 deletions

View file

@ -55,6 +55,11 @@ void AgentSettingsWidget::loadSettings()
auto sshAuthSockOverride = sshAgent()->authSockOverride();
m_ui->sshAuthSockLabel->setText(sshAuthSock.isEmpty() ? tr("(empty)") : sshAuthSock);
m_ui->sshAuthSockOverrideEdit->setText(sshAuthSockOverride);
auto sshSecurityKeyProvider = sshAgent()->securityKeyProvider(false);
auto sshSecurityKeyProviderOverride = sshAgent()->securityKeyProviderOverride();
m_ui->sshSecurityKeyProviderLabel->setText(sshSecurityKeyProvider.isEmpty() ? tr("(empty)")
: sshSecurityKeyProvider);
m_ui->sshSecurityKeyProviderOverrideEdit->setText(sshSecurityKeyProviderOverride);
#endif
m_ui->sshAuthSockMessageWidget->setVisible(sshAgentEnabled);
@ -85,6 +90,8 @@ void AgentSettingsWidget::saveSettings()
{
auto sshAuthSockOverride = m_ui->sshAuthSockOverrideEdit->text();
sshAgent()->setAuthSockOverride(sshAuthSockOverride);
auto sshSecurityKeyProviderOverride = m_ui->sshSecurityKeyProviderOverrideEdit->text();
sshAgent()->setSecurityKeyProviderOverride(sshSecurityKeyProviderOverride);
#ifdef Q_OS_WIN
sshAgent()->setUsePageant(m_ui->usePageantCheckBox->isChecked());
sshAgent()->setUseOpenSSH(m_ui->useOpenSSHCheckBox->isChecked());

View file

@ -100,6 +100,16 @@
<property name="verticalSpacing">
<number>8</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="sshAuthSockOverrideLabel">
<property name="text">
<string>SSH_AUTH_SOCK override</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="sshAuthSockValueLabel">
<property name="text">
@ -110,15 +120,18 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="sshAuthSockOverrideLabel">
<property name="text">
<string>SSH_AUTH_SOCK override</string>
<item row="4" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</widget>
</spacer>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="sshAuthSockOverrideEdit"/>
@ -139,17 +152,42 @@
</widget>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<widget class="QLabel" name="sshSecurityKeyProviderValueLabel">
<property name="text">
<string>SSH_SK_PROVIDER value</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</spacer>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="sshSecurityKeyProviderLabel">
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
<property name="text">
<string>(empty)</string>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="sshSecurityKeyProviderOverrideLabel">
<property name="text">
<string>SSH_SK_PROVIDER override</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="sshSecurityKeyProviderOverrideEdit"/>
</item>
</layout>
</item>

View file

@ -473,6 +473,8 @@ bool OpenSSHKey::readPublic(BinaryStream& stream)
{ "ecdsa-sha2-nistp384", {STR_PART, STR_PART} },
{ "ecdsa-sha2-nistp521", {STR_PART, STR_PART} },
{ "ssh-ed25519", {STR_PART} },
{ "sk-ecdsa-sha2-nistp256@openssh.com", {STR_PART, STR_PART, STR_PART} },
{ "sk-ssh-ed25519@openssh.com", {STR_PART, STR_PART} },
};
// clang-format on
@ -502,6 +504,8 @@ bool OpenSSHKey::readPrivate(BinaryStream& stream)
{ "ecdsa-sha2-nistp384", {STR_PART, STR_PART, STR_PART} },
{ "ecdsa-sha2-nistp521", {STR_PART, STR_PART, STR_PART} },
{ "ssh-ed25519", {STR_PART, STR_PART} },
{ "sk-ecdsa-sha2-nistp256@openssh.com", {STR_PART, STR_PART, STR_PART, UINT8_PART, STR_PART, STR_PART} },
{ "sk-ssh-ed25519@openssh.com", {STR_PART, STR_PART, UINT8_PART, STR_PART, STR_PART} },
};
// clang-format on

View file

@ -61,11 +61,21 @@ QString SSHAgent::authSockOverride() const
return config()->get(Config::SSHAgent_AuthSockOverride).toString();
}
QString SSHAgent::securityKeyProviderOverride() const
{
return config()->get(Config::SSHAgent_SecurityKeyProviderOverride).toString();
}
void SSHAgent::setAuthSockOverride(QString& authSockOverride)
{
config()->set(Config::SSHAgent_AuthSockOverride, authSockOverride);
}
void SSHAgent::setSecurityKeyProviderOverride(QString& securityKeyProviderOverride)
{
config()->set(Config::SSHAgent_SecurityKeyProviderOverride, securityKeyProviderOverride);
}
#ifdef Q_OS_WIN
bool SSHAgent::useOpenSSH() const
{
@ -109,6 +119,21 @@ QString SSHAgent::socketPath(bool allowOverride) const
return socketPath;
}
QString SSHAgent::securityKeyProvider(bool allowOverride) const
{
QString skProvider;
if (allowOverride) {
skProvider = securityKeyProviderOverride();
}
if (skProvider.isEmpty()) {
skProvider = QProcessEnvironment::systemEnvironment().value("SSH_SK_PROVIDER", "internal");
}
return skProvider;
}
const QString SSHAgent::errorString() const
{
return m_error;
@ -257,10 +282,12 @@ bool SSHAgent::addIdentity(OpenSSHKey& key, const KeeAgentSettings& settings, co
QByteArray requestData;
BinaryStream request(&requestData);
bool isSecurityKey = key.type().startsWith("sk-");
request.write((settings.useLifetimeConstraintWhenAdding() || settings.useConfirmConstraintWhenAdding())
? SSH_AGENTC_ADD_ID_CONSTRAINED
: SSH_AGENTC_ADD_IDENTITY);
request.write(
(settings.useLifetimeConstraintWhenAdding() || settings.useConfirmConstraintWhenAdding() || isSecurityKey)
? SSH_AGENTC_ADD_ID_CONSTRAINED
: SSH_AGENTC_ADD_IDENTITY);
key.writePrivate(request);
if (settings.useLifetimeConstraintWhenAdding()) {
@ -272,6 +299,12 @@ bool SSHAgent::addIdentity(OpenSSHKey& key, const KeeAgentSettings& settings, co
request.write(SSH_AGENT_CONSTRAIN_CONFIRM);
}
if (isSecurityKey) {
request.write(SSH_AGENT_CONSTRAIN_EXTENSION);
request.writeString(QString("sk-provider@openssh.com"));
request.writeString(securityKeyProvider());
}
QByteArray responseData;
if (!sendMessage(requestData, responseData)) {
return false;
@ -289,6 +322,11 @@ bool SSHAgent::addIdentity(OpenSSHKey& key, const KeeAgentSettings& settings, co
m_error += "\n" + tr("A confirmation request is not supported by the agent (check options).");
}
if (isSecurityKey) {
m_error +=
"\n" + tr("Security keys are not supported by the agent or the security key provider is unavailable.");
}
return false;
}

View file

@ -37,8 +37,11 @@ public:
bool isEnabled() const;
void setEnabled(bool enabled);
QString socketPath(bool allowOverride = true) const;
QString securityKeyProvider(bool allowOverride = true) const;
QString authSockOverride() const;
QString securityKeyProviderOverride() const;
void setAuthSockOverride(QString& authSockOverride);
void setSecurityKeyProviderOverride(QString& securityKeyProviderOverride);
#ifdef Q_OS_WIN
bool useOpenSSH() const;
bool usePageant() const;
@ -74,6 +77,7 @@ private:
const quint8 SSH_AGENT_CONSTRAIN_LIFETIME = 1;
const quint8 SSH_AGENT_CONSTRAIN_CONFIRM = 2;
const quint8 SSH_AGENT_CONSTRAIN_EXTENSION = 255;
bool sendMessage(const QByteArray& in, QByteArray& out);
bool sendMessageOpenSSH(const QByteArray& in, QByteArray& out);