SSH agent client support (KeeAgent compatible)#1098
SSH agent client support (KeeAgent compatible)#1098phoerious merged 3 commits intokeepassxreboot:developfrom
Conversation
1caf560 to
d0ea74f
Compare
|
Hi hifi, there's a conflict after #1124 (https://github.com/keepassxreboot/keepassxc/pull/1124/files#diff-7b98a79aea3db9f4e15f2f31fe24c087R103), could you fix that? I always apply useful patches on top of latest git. |
168757a to
cce6d71
Compare
|
@yan12125 Rebased. |
ab208b4 to
5577ed6
Compare
|
Ok, so I'm going to apologise for this in advance, but: You sir, are a fucking legend. |
b5fd350 to
9fc45cf
Compare
|
Trying to use that but I've just always the comment "unknown cipher aes256-ctr" and can't add keys to agent. Can you help me? |
|
@mfulz: Support for aes256-ctr is currently missing from this PR. That shouldn't be too long to come :) As references for developers:
|
|
One Bug: I'f auto adding key when db is unlocked the user confirmation is not working. |
|
@yan12125 Great thanks for the help, this undocumented option was the missing link. Perfect feature. Just the bug with user confirmation on db unlock is not working. |
src/sshagent/DatabaseAgent.cpp
Outdated
| || mode == DatabaseWidget::EditMode) | ||
| && !m_sentKeys) { | ||
| for (QSharedPointer<OpenSSHKey> e : keys) { | ||
| AgentClient::instance()->addIdentity(*e.data()); |
There was a problem hiding this comment.
Here lifetime and confirm should be passed, too.
There was a problem hiding this comment.
Need to do a bit of refactoring there, it was written before constraints existed.
9fc45cf to
d6ca747
Compare
|
@yan12125 AES-256-CTR should work with the latest version. |
|
@mfulz Constraints should now work if using automatic add on unlock. Thank you for testing. |
2a7b1d8 to
bdad109
Compare
|
@hifi Thank you very much, I can confirm that everything is working now absolutely perfect: aes256-ctr, confirmation on auto unlock. |
0752358 to
9581dba
Compare
There was a problem hiding this comment.
Needs a little bit of work. Overall great job! Instead of rolling your own BinaryStream you need to use the QDataStream which will be inherently platform agnostic.
Visually, on the SSH Agent settings page, align the checkboxes with the second column (ie not the label text) to give a cleaner appearance.
src/gui/entry/EditEntryWidget.cpp
Outdated
| #include <QMenu> | ||
| #include <QSortFilterProxyModel> | ||
| #include <QTemporaryFile> | ||
| #include <QtWidgets> |
There was a problem hiding this comment.
Remove this include line to prevent compile errors on Windows.
There was a problem hiding this comment.
I think removing it will make Travis fail with older Qt 5.
There was a problem hiding this comment.
Perhaps you are missing a more specific include then?
There was a problem hiding this comment.
One of you told me on IRC to include QtWidgets instead of the specific ones when I had them in the early incarnations. Please decide.
There was a problem hiding this comment.
Personally, I don't care. But if including QWidgets produces errors on Windows, this needs to be addressed.
There was a problem hiding this comment.
That was throw away advice to get you going. You always want to scope down if possible, and especially if it causes issues. The ui_XXXX.h files bring in all the widget headers you need. It was likely a false positive that Travis was barfing on a widget file.
src/gui/entry/EditEntryWidget.h
Outdated
| bool m_create; | ||
| bool m_history; | ||
| #ifdef WITH_XC_SSHAGENT | ||
| bool m_sshAgent; |
There was a problem hiding this comment.
Suggest renaming this to m_sshAgentEnabled as it is named now it implies holding an object not a boolean.
| #include <QIODevice> | ||
| #include <QBuffer> | ||
|
|
||
| class BinaryStream : QObject |
There was a problem hiding this comment.
Recommend removing this implementation entirely in favor of QDataStream (you can set Endianness!) http://doc.qt.io/qt-5/qdatastream.html
There was a problem hiding this comment.
OpenSSH has its own buffer format, which is not compatible with Qt's. Maybe renaming BinaryStream to something like SSHBufferStream is better.
| @@ -0,0 +1,57 @@ | |||
| /* | |||
| * Copyright (C) 2017 Toni Spets <toni.spets@iki.fi> | |||
There was a problem hiding this comment.
All new files need to include the standard KeePassXC team copyright (in can be in addition to your own):
Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
|
||
| void AgentSettingsWidget::saveSettings() | ||
| { | ||
| config()->set("SSHAgent", m_ui->enableSSHAgentCheckBox->isChecked()); |
There was a problem hiding this comment.
At this point you should emit a signal that the SSH Agent settings have changed which would alert the system to either start or stop the ssh agent. We should never have to restart the application to enact a setting.
There was a problem hiding this comment.
This will require some refactoring as yeah, it needs to send the keys out to an agent if databases are unlocked when you enable it and emulate a lock if you disable while keys have been sent that are supposed to be removed on lock.
There was a problem hiding this comment.
We can leave that to a future enhancement after this merge. I'll create an issue to track.
| <item> | ||
| <widget class="QCheckBox" name="lifetimeCheckBox"> | ||
| <property name="text"> | ||
| <string>Remove key from agent after</string> |
There was a problem hiding this comment.
It's not entirely clear when this timeout kicks in. Is it on database open? Is it on last use? Is it a timeout between uses? I think it needs to be reworded to be clear.
There was a problem hiding this comment.
I disagree on the ambiguity but if someone has a good rewording that works, sure.
There was a problem hiding this comment.
What's the answer though... I didn't parse the code to find out
There was a problem hiding this comment.
Regardless how the key is added to the agent, the agent will auto-remove the key after said timeout. It's an agent feature and we just tell it how long it should keep the key.
| } | ||
|
|
||
| if (beginEx.cap(1) != "OPENSSH PRIVATE KEY") { | ||
| m_error = tr("This is not an OpenSSH key, only modern keys are supported"); |
There was a problem hiding this comment.
This error is obtuse. What is meant by "modern keys"?? None of my keys loaded up and I have absolutely no idea how to fix the problem. Why isn't RSA PRIVATE KEY accepted?
There was a problem hiding this comment.
I don't know what to call the new type keys. Suggestions welcome.
There was a problem hiding this comment.
After researching it they are elliptic curve generated keys (ie, ed25519). Why don't you allow loading RSA keys? RSA 4096-bits is perfectly secure...
http://blog.siphos.be/2015/08/switching-openssh-to-ed25519-keys/
Do note that not all SSH servers support the new ed25519. Dropbear is a noted hold out.
There was a problem hiding this comment.
It means the new "OpenSSH file format", it supports all key types and conversions are possible as stated in OP with instructions.
There was a problem hiding this comment.
Gotcha, I guess I should read your description first. Either way the link to the documentation (could even be within the GUI layout, not the error string) needs to be settled for the lay user. We will get SO MANY bug reports as it is implemented now.
There was a problem hiding this comment.
Would it be a link to the wiki? Is there any other reasonable way to document things like this?
There was a problem hiding this comment.
Sadly, we miss a lot of documentation, it's an open issue #696
|
May I also suggest at some point we have some documentation on how to configure this to work? |
|
@CRCinAU It most likely is needed because it's not obvious what the new key format means and what would be the troubleshooting steps to get your agent visible on Linux/BSD. The key format error really needs to point to some documentation how you convert your key the very least. As @droidmonkey said for the live enable/disable configuration option it could also be tracked as a separate issue to improve the UX before 2.3.0 lands. |
src/sshagent/OpenSSHKey.cpp
Outdated
| QString begin = rows.first(); | ||
| QString end = rows.last(); | ||
|
|
||
| QRegExp beginEx("-----BEGIN (.+)-----"); |
There was a problem hiding this comment.
More thorough review pending, but I just saw this while reading my notifications. This should be
"^-----BEGIN ([^\\-]+)-----$"
Also use QRegularExpression instead of QRegExp. Or maybe even split after -----BEGIN and before ----- using QString::indexOf().
There was a problem hiding this comment.
It's using exactMatch which implies ^...$.
There was a problem hiding this comment.
Still leaves the thing in the middle.
There was a problem hiding this comment.
I had it like that first but opted to use the shorter version as it doesn't matter much with a single line exact match. Sure, it bleeds extra dashes into the capture. I'll change it back.
2a2e12d to
8fe507b
Compare
phoerious
left a comment
There was a problem hiding this comment.
I found a few things that need to be changed. After that I think this is ready for merging. Most of the changes are minor nitpicks, but I also found a few potential gotchas.
I would also prefer some doc comments for the new methods, so we can generate an API documentation in the future, but that can be done in a separate PR.
src/gui/entry/EditEntryWidget.cpp
Outdated
| continue; | ||
| } | ||
|
|
||
| m_sshAgentUi->privateKeyComboBox->addItem("Attachment: " + fileName); |
There was a problem hiding this comment.
Should be a translatable string.
src/gui/entry/EditEntryWidget.cpp
Outdated
| } | ||
|
|
||
| if (settings.selectedType() == "attachment") { | ||
| m_sshAgentUi->privateKeyComboBox->setCurrentText("Attachment: " + settings.attachmentName()); |
src/sshagent/SSHAgent.cpp
Outdated
| m_instance = new SSHAgent(parent); | ||
| } | ||
|
|
||
| bool SSHAgent::agentRunning() const |
There was a problem hiding this comment.
Should be named isAgentRunning()
src/gui/entry/EditEntryWidget.cpp
Outdated
| settings.setUseLifetimeConstraintWhenAdding(m_sshAgentUi->lifetimeCheckBox->isChecked()); | ||
| settings.setLifetimeConstraintDuration(m_sshAgentUi->lifetimeSpinBox->value()); | ||
|
|
||
| QString prefix = "Attachment: "; |
There was a problem hiding this comment.
I think this is the prefix format for KeeAgent so shoudn't be translatable
There was a problem hiding this comment.
It's my own convention unrelated to KeeAgent so no worries there.
There was a problem hiding this comment.
If it's an hardcoded prefix you should consider something like _ATTACHMENT: , like this it really seems a translatable string
| settings.setLifetimeConstraintDuration(m_sshAgentUi->lifetimeSpinBox->value()); | ||
|
|
||
| QString prefix = "Attachment: "; | ||
| if (privateKeyPath.startsWith(prefix)) { |
There was a problem hiding this comment.
startsWith() may not work any more, once the string has been translated.
src/sshagent/OpenSSHKey.cpp
Outdated
|
|
||
| // load private if no encryption | ||
| if (!encrypted()) { | ||
| return decrypt(); |
There was a problem hiding this comment.
Am I missing something? Why do you decrypt if it is not encrypted?
There was a problem hiding this comment.
Decryption also implies parsing the private portion regardless if there's actual encryption used. Function naming issue.
src/sshagent/OpenSSHKey.cpp
Outdated
|
|
||
| QByteArray phraseData = passphrase.toLatin1(); | ||
| if (bcrypt_pbkdf(phraseData, salt, decryptKey, rounds) < 0) { | ||
| m_error = tr("Decryption failed, wrong passphrase?"); |
There was a problem hiding this comment.
This error message is confusing. You are deriving the decryption key here, not decrypting the data.
There was a problem hiding this comment.
It happens because of an empty passphrase. Will catch that special case earlier and have a proper error message for both.
| keyData.setRawData(decryptKey.data(), cipher->keySize()); | ||
| ivData.setRawData(decryptKey.data() + cipher->keySize(), cipher->blockSize()); | ||
|
|
||
| cipher->init(keyData, ivData); |
There was a problem hiding this comment.
I still find it peculiar, that the OpenSSH format has no HMAC for the cipher text. PuTTY format has that.
There was a problem hiding this comment.
You want me to file an issue with OpenSSH developers? 😕
There was a problem hiding this comment.
No, that was just a comment on the sidelines.
src/sshagent/OpenSSHKey.cpp
Outdated
| return false; | ||
| } | ||
|
|
||
| for (int i = 0; i < keyParts; i++) { |
src/sshagent/OpenSSHKey.cpp
Outdated
| return false; | ||
| } | ||
|
|
||
| for (int i = 0; i < keyParts; i++) { |
Includes AES-256-CTR non-stream tests
This additionally makes keySize() and blockSize() work before setting the key and IV. Required for SSH agent decryption.
7ea79d2 to
4840c2c
Compare
|
Congratulations, you got merged with the Matrix! |
|
🎉 |
|
So urrrm... When are we looking at doing a new release? I'm super keen to look at this plus the yubikey fixes of a while ago... |
|
Great news! Waiting for release. |
|
@CRCinAU Official daily builds are being discussed as the 2.3.0 cycle will bring in many new features that will require heavy testing before release. |
|
@hifi Does that include the new browser plugin I keep hearing about? |
|
Yes absolutely |
|
Oh man, thanks a lot for this! Keeagent was the only reason keeping me on Keepass (even though that crashed for me when pressing the windows key on linux). I've tried this out and it is works great so far (had to convert my key though)! |
- Add support for KDBX 4.0, Argon2 and ChaCha20 [#148, #1179, #1230, #1494] - Add SSH Agent feature [#1098, #1450, #1463] - Add preview panel with details of the selected entry [#879, #1338] - Add more and configurable columns to entry table and allow copying of values by double click [#1305] - Add KeePassXC-Browser API as a replacement for KeePassHTTP [#608] - Deprecate KeePassHTTP [#1392] - Add support for Steam one-time passwords [#1206] - Add support for multiple Auto-Type sequences for a single entry [#1390] - Adjust YubiKey HMAC-SHA1 challenge-response key generation for KDBX 4.0 [#1060] - Replace qHttp with cURL for website icon downloads [#1460] - Remove lock file [#1231] - Add option to create backup file before saving [#1385] - Ask to save a generated password before closing the entry password generator [#1499] - Resolve placeholders recursively [#1078] - Add Auto-Type button to the toolbar [#1056] - Improve window focus handling for Auto-Type dialogs [#1204, #1490] - Auto-Type dialog and password generator can now be exited with ESC [#1252, #1412] - Add optional dark tray icon [#1154] - Add new "Unsafe saving" option to work around saving problems with file sync services [#1385] - Add IBus support to AppImage and additional image formats to Windows builds [#1534, #1537] - Add diceware password generator to CLI [#1406] - Add --key-file option to CLI [#816, #824] - Add DBus interface for opening and closing KeePassXC databases [#283] - Add KDBX compression options to database settings [#1419] - Discourage use of old fixed-length key files in favor of arbitrary files [#1326, #1327] - Correct reference resolution in entry fields [#1486] - Fix window state and recent databases not being remembered on exit [#1453] - Correct history item generation when configuring TOTP for an entry [#1446] - Correct multiple TOTP bugs [#1414] - Automatic saving after every change is now a default [#279] - Allow creation of new entries during search [#1398] - Correct menu issues on macOS [#1335] - Allow compilation on OpenBSD [#1328] - Improve entry attachments view [#1139, #1298] - Fix auto lock for Gnome and Xfce [#910, #1249] - Don't remember key files in file dialogs when the setting is disabled [#1188] - Improve database merging and conflict resolution [#807, #1165] - Fix macOS pasteboard issues [#1202] - Improve startup times on some platforms [#1205] - Hide the notes field by default [#1124] - Toggle main window by clicking tray icon with the middle mouse button [#992] - Fix custom icons not copied over when databases are merged [#1008] - Allow use of DEL key to delete entries [#914] - Correct intermittent crash due to stale history items [#1527] - Sanitize newline characters in title, username and URL fields [#1502] - Reopen previously opened databases in correct order [#774] - Use system's zxcvbn library if available [#701] - Implement various i18n improvements [#690, #875, #1436]
This implements a ssh-agent client that sends keys from your KeePassXC database to a running agent when a database is unlocked and removes them when it's locked.
Description
New OpenSSH key types are supported (DSA, RSA, ECDSA and ED25519) including decryption. Old keys can be converted to new format with
ssh-keygenor PuTTYgen without needing to rotate your public key on remote hosts.POSIX and Mac agent access is through the environment
SSH_AUTH_SOCKUNIX socket and the socket is opened on-demand when keys are added or removed from the agent. Communication is blocking through BinaryStream class that wraps around QIODevice and thus QLocalSocket.Windows (Pageant) agent access is using shared memory and it shares the same protocol. This has been inlined and
#ifdefseparated from the socket version as it is quite small.TODO
KeeAgent.settingsreading/writing for compatibility*and&in arguments/variablestr()Non-goals (updated)
Motivation and context
I didn't know I need KeeAgent. Now I definitely need KeeAgent. Issue #278 is tracking the wishlist item.
How has this been tested?
In daily use by author. If you want to test this please build with
-DWITH_XC_SSHAGENT=ONand enable SSH Agent from Settings.Not working for you?
If the entry agent control buttons are greyed out on Linux/Mac when a key is loaded the environment does not have
SSH_AUTH_SOCKvariable set. You need to make sure your session has an agent running so that the environment is populated before you start KeePassXC.On Windows, you can start Pageant at any time. The buttons will light up after you re-open entry edit.
Key conversion tips
New compatible keys can be generated via the following command:
Old keys can be converted into the new format like this (make a copy of the key first just in case):
PuTTYgen can import any key (OpenSSH or PuTTY key) and export them in the new format via the Conversions menu and selecting Export OpenSSH key (force new file format).
Screenshots (if appropriate):
Types of changes
Checklist: