Page MenuHome GnuPG

Add Win32-OpenSSH support to gpg-agent's ssh-agent
Closed, ResolvedPublic

Assigned To
Authored By
ccharabaruk
Apr 7 2018, 12:59 AM
Tokens
"Like" token, awarded by Zymlex."Like" token, awarded by mfilippov."Like" token, awarded by grv87."Mountain of Wealth" token, awarded by gpguser123."Love" token, awarded by avemilia."Love" token, awarded by nicolaslegland."Love" token, awarded by emmedve1.

Description

The OpenSSH port shipping with new versions of Windows uses Windows named pipes for interprocess communication. The enable-ssh-support option for gpg-agent should be extended on Windows to support named pipes in addition to Cygwin/MSYS emulation of Unix sockets, or a new option should be added to Windows for this, so that "native" ssh client can be used instead of requiring Cygwin ssh client or PuTTY.

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
werner triaged this task as Normal priority.Apr 9 2018, 10:25 AM
werner added a subscriber: werner.

Thanks for the pointer. But as long as the Windows ssh server is that instable I see no urgent need to add this to GnuPG.

BTW, can you confirm that gpg-agent works with the Cygwin SSH? IIRC, I once added a hack to allow for this but never thoroughly tested it.

I would argue that the Windows port of OpenSSH is not unstable at this point, especially given that Microsoft is even providing it as an installable feature in the next regular Windows 10 release. The fact that the port is now using actual OpenSSH version numbers instead of their own 0.x versions lends credence to this as well.

As for Cygwin, it's been so long since I needed to use the Cygwin or MSYS builds of the OpenSSH client that I didn't even realize I still had it installed. However, after checking with both latest version of Cygwin and latest 64-bit MSYS2, gpg-agent doesn't work with their ssh client packages. Trying to use the S.gpg-agent.ssh "socket" results in this error message when attempting to access the agent via ssh, ssh-add, etc.:

Error connecting to agent: Bad file descriptor

Rhat's for the client, right. I never used it. We used to run a Windows 8 instance in a VM to run tests via ssh on it. That worked most not really stable. For obvious reasons I am more interested in the server part ;-)

The Cygwin thing was a quick and untested hack. I don't think it makes much sense to follow up on this. Better to support the native ssh thing.

I've been working with one of Microsoft's developers on a temporary tool that should bridge the connection between named pipes and the Unix sockets emulation used by gpg-agent but things appear to trip up with sending the nonce. From the position of the tool, the nonce value is successfully sent (send returns 16), but never seems to be picked up by gpg-agent. Instead both gpg-agent and the bridge sit there until whatever tool is using them (I test using ssh-add -l) is terminated, at which point gpg-agent immediately spits up the message

gpg-agent[16064]: error reading nonce on fd 660: Input/output error

(PID and FD variable, of course.)

@werner After sending the nonce value from the socket file, does anything need to be read back before ssh-agent commands can be sent? Are there any byte ordering requirements for sending the nonce or can they be sent in the same order as they are in the file?

The nonce is a string of octets thus it needs to be passed verbatim. I would need to study the code in libassun/src/assuan-socket.c to tell more.

I just took a look through assuan-socket.c and it appears that we just need to send the nonce and don't need to read anything back. We also found a bug on our side that was preventing the nonce from being sent, which has been fixed. The error message logged above no longer happens.

Now however, as soon as the nonce is sent across, we get log messages like

gpg-agent[32944]: ssh handler 0x3 for fd 844 started
gpg-agent[32944]: ssh handler 0x3 for fd 844 terminated

It appears that the actual ssh-agent message coming across the bridge is never read or acted upon, but the socket connection isn't closed despite the log message stating that the handler is terminated.

@werner I was hoping to make a modified gpg-agent build that would let me walk through what's going on after the nonce is sent but it looks like the gpg4win process only takes in a package of pre-built gpg binaries which rules that out. As far as I can figure out, after the nonce is read and accepted, libassuan creates a stream object out of the socket and then finding nothing in the stream terminates the ssh handler. We send the actual client request immediately after the nonce but in a separate call to send() so I now wonder if by not having anything read in at the same time as the nonce gpg-agent or libassuan thinks that it's a 0-length stream.

Although I don't use the ssh client on Windows I had to integrate the Windows ssh server into our release process (GlobalSign sent us a Windows-only token, for the new cert and so we can't anymore use osslsigncode). The ssh server is really stable and so it makes a lot of sense to better integrate our ssh-agent into Windows.

I need to look into their named pipes IPC - I once ruled that out for Windows because it is not possible to limit connections to the local machine.

In the meantime you can use [0]. I have tested with ssh key on yubikey and AuthenticationMethods publickey, win32-ssh (or ssh-portable, which is the new repository name) correctly works with gpg and pinentry is called. Despite it being called wsl, wsl environment is not required.

[0] https://github.com/benpye/wsl-ssh-pageant

I'm actually trying to do the following:

I have 2 Windows PCs at home: Laptop and Desktop.

I'm using Windows' native OpenSSH client and server so I can connect to my desktop using my laptop.

I want to edit a file while on SSH that's placed on my desktop, and then run git commit so I can save my work.

The problem is my Git is configured to ask for a GPG key, that has a password. Since I'm over SSH, the GnuPG window asking for such password can't be rendered and my Desktop gets stuck at this step.


What I'm looking for is a way to forward this password prompt to my laptop, where I can then sign my commit through SSH.

Is such a thing possible? And does WSL SSH Pageant work for the process I mentioned?

P.S.: local commit signing (without SSH connections) is working on both machines, they both have the same GPG keys and config for Git and GnuPG.

P.S.²: I never used any graphical GPG client (Pageant, gpg4win, etc), I just use GPG to sign Git Commits through the CLI.

@bvieira You need to set pinentry-mode=loopback for gpg program used in git.

On unix systems GPG_TTY=$(tty) makes it work. But not sure on windows how this logic is handled.

So you might have to use git --config global gpg.Program and try passing pinentry-mode. Not sure if you can pass arguments to it directly, otherwise might have to create a wrapper batch file.

Unfortunately you can't pass extra arguments.

loopback mode won't help; it is simply that we don't support the integrated OpenSSH yet. The Putty support uses a different IPC mechanism than our inter-GnuPG-IPC which is again different from OpenSSH for Windows.

So, if there's no support for native OpenSSH yet, I'll wait for it. After it's supported, I should be able to get the scenery I described working, right?

@werner can you confirm if the environment I provided will work with OpenSSH support fully implemented?

I wrote https://github.com/rupor-github/win-gpg-agent to simplify usage on Windows until this issue is resolved - it handles various edge cases on Windows.

There is a user report that got things to work with https://github.com/rupor-github/win-gpg-agent
on https://wald.intevation.org/forum/forum.php?thread_id=2359&forum_id=21&group_id=11

Though this took some time to find out for the user. So if this is not implemented soon, we should improve the documentation about works and what does not.

Lots of detailed documentation but frankly, after a brief read I have not yet figured out what it really does. We won't support Cygwin stuff - this is all obsolete and awe also removed starting gpg-agent as a service for good reasons. Instead of starting gpg-agent with lot of command line args it would be better to put this into a per user or system wide config file.

Since Windows user naively could expect multiple methods of accessing certificates from different programs (or sometimes from the same program but different supported environments, like Git4Win and git in WSL) to work together transparently, win-gpg-agent covers translation of one accidentally supported method (32 bit putty shared memory) to multiple unsupported ones (named pipe, cygwin, etc). It also takes care of managing gpg-agent.exe lifetime tying it to user login session for convenience. It uses command line parameters to only to overwrite staff critical to its functionality and does not prevent user from having configuration file(s). Optionally it provides pinentry which is integrated with Windows native Crypto Vault and UX rather than using wonderful QT or GTK. As specified in documentation when developers of gpg and WIndows will get their act together and figure out what they want and how they want it - most of functionality would not be needed. I would like to point out that simply claiming superiority and not supporting cygwin (Git4Win) or working Assuan ssh socket or putty shared memory in 64 bits Windows build does not help with user experience a single bit.

@rupor-github thanks for your explanations and the contribution to the GnuPG and crypto Free Software code base!

My experience with @werner is that there are usually good reason why he prefers one over the other thing. And sometimes things to not get work upon for a while, because resources are limited. Any questions or suggestions are always made under these preconditions of the current understanding. As most people in Free Software work as good as they can (and wth the resources they have available), I think suggesting to anybody to get their act together is not helpful. Also I saw nobody claiming superiority. Please ask or propose something more specific to improve understanding and the situation.

@bernhard thank you for explaining, did not mean to offend anybody. Before creating win-gpg-agent I tried to read as much as I could on a history and obviously had to study source a bit. Be it as it may - I decided to have separate wrapper, rather then contributing directly to gpg code base. There is noticable number of use cases on Windows which presently not addressed, some I believe are sitting it the queue already.

Fix non working assuan ssh socket?
Provide working 64 bit build including putty shared memory protocol?
Start communicating with other developers on clean future path (so cygwin solutions could be adjusted)?
Support windows named pipes so whatever comes with Windows works out of the box rather than explaining why name pipes are bad?

Not sure if that is helpful.

@rupor-github no problem! :)

As to your suggestions. The status of this issue, as far as I understood from reading it is:

Werner has accepted that using the native Windows ssh (an OpenSSH port) with the names pipes they use, is a valid and useful use-case. He now needs time to read up on the named pipes, their properties and come up with the code.
(If anybody else comes up with well documented code or links to very good technical documentation, it may help the process.)

This seems to be the focus of this issue. (And the remark made at laast by @werner seem to be part explanation of the history, stating his status that he could not yet fully read and understand the https://github.com/rupor-github/win-gpg-agent documentation and some minor suggestions/thoughs.)

As for the two remaining suggestions:

  1. working 64 bit build for putty shared memory protocol
  2. cygwin solutions improvement.

The next step forward for those would be to have separate new issues and get them to be understood well.

(Re 1 my understanding was that putty is working in principle, but I'm not sure about what it is about the 64 bit build.)

(Re 2: As for cygwin maybe an email to gnupg-devel or some cygwin mailinglist explaining the challenges can be helpful?)

@bernhard Sorry for the delayed answer, was on sabbatical.

There are 2 putty versions 32 bits and 64 bits, both are working. Now there is 32 bit version of GnuPG tools which successfully pretends to be putty (implements shared memory protocol). I could build 64 bits GnuPG without a problem, however it does a bad job pretending to Putty - when I try to access shared memory it does not work (crashes the other side).

Also last time I looked for whatever reasons Assuan S.gpg-agent.ssh support in GnuPG code was broken under Windows (there was a comment in the code on that)

I am not exactly sure how to improve cygwin solutions support and have no intention messing with egos on cygwin list. My understanding is that presently (WSL and everything) cygwin is not really needed. Unfortunately most widely used Git4Windows is built on cygwin platform so any "successful" and user friendly solution should consider this for time being...

@rupor-github no problem for the delay. Thanks for explaining!

Now I can see that there is at least the Putty 64bit support, which should be put in a new issue.

With Assuan S.gpg-agent.ssh support in GnuPG code was broken under Windows, this maybe part of this issue already or not, I technically do not know enough to guess about this. So it is probably good to wait until werner gets a go at this issue. (Which take a while.)

Here is a patch to implement the functionality with --enable-win32-openssh-support.

Note that it requires forthcoming new libgpg-error release, as current version has a bug for T6112.

It works for me.

I think that the last argument of CreateNamedPipeA can limit the access to the named pipe.

The descriptor I use (in my patch) is:

"D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;0x12019b;;;AU)"

I don't know this semantics well (I just copied from ssh-agent implementation of Win32-OpenSSH).

References:
https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format
https://docs.microsoft.com/en-us/windows/win32/secauthz/ace-strings
https://docs.microsoft.com/en-us/windows/win32/secauthz/sid-strings

https://github.com/mtth-bfft/winsddl

Here is the parser output:

$ python3 sd.py --type=pipe "D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;0x12019b;;;AU)"
D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;0x12019b;;;AU)
    Discretionary ACL: P(A;;GA;;;SY)(A;;GA;;;BA)(A;;0x12019b;;;AU)
        Flags: P: SE_DACL_PROTECTED (Blocks inheritance of parent's ACEs)
    
        ACE 1: A;;GA;;;SY
            Type: A (ACCESS_ALLOWED_ACE_TYPE)
            Access rights: GA	(0x10000000)
                0x10000000 GENERIC_ALL	(Generic right mapped to every other existing right)
            Trustee: SY (S-1-5-18) (Local System)
    
        ACE 2: A;;GA;;;BA
            Type: A (ACCESS_ALLOWED_ACE_TYPE)
            Access rights: GA	(0x10000000)
                0x10000000 GENERIC_ALL	(Generic right mapped to every other existing right)
            Trustee: BA (S-1-5-32-544) (Administrators)
    
        ACE 3: A;;0x12019b;;;AU
            Type: A (ACCESS_ALLOWED_ACE_TYPE)
            Access rights: 0x12019b
                0x100000   SYNCHRONIZE	(Wait for a change in the object, standard to all types)
                0x20000    READ_CONTROL	(Read the object's security descriptor except its SACL, standard to all types)
                0x100      FILE_WRITE_ATTRIBUTES	(Write the pipe's attributes)
                0x80       FILE_READ_ATTRIBUTES	(Read the pipe's attributes)
                0x10       FILE_WRITE_EA	(Write the pipe's extended attributes, if any)
                0x8        FILE_READ_EA	(Read the pipe's extended attributes, if any)
                0x2        FILE_WRITE_DATA	(Write contents into the pipe)
                0x1        FILE_READ_DATA	(Read contents from the pipe)
            Trustee: AU (S-1-5-11) (Authenticated Users)

Probably, PIPE_REJECT_REMOTE_CLIENTS mode and lpSecurityAttributes=NULL is OK.

Probably, PIPE_REJECT_REMOTE_CLIENTS mode and lpSecurityAttributes=NULL is OK.

I tried it and this way looks good. I can use keys from the agent with SSH_AUTH_SOCK='\\.\pipe\openssh-ssh-agent', and ssh reports error 5 (ERROR_ACCESS_DENIED) with SSH_AUTH_SOCK='\\127.0.0.1\pipe\openssh-ssh-agent'. Never mind. I didn't notice this change is already implemented as mentioned in E936.

As a side note, to access named pipes using the IP address, I need to run net start server, otherwise ssh reports error 53 (ERROR_BAD_NETPATH).

Here is the modified patch:

I was looking for this when writing the update NEWS for the latest release and noticed that this has not been pushed yet. I really think that it would be nice to have that. Especially for Smartcard use cases.

It's not yet pushed, because it requires new release of libgpg-error (for T6112: libgpg-error,w32: bidirectional Pipe support for estream).

I hacked configure.ac of gnupg to force it build with libgpg-error 1.45, and OpenSSH works with the created pipe. Maybe the libgpg-error fix is only necessary in some certain circumstances?

gniibe changed the task status from Open to Testing.Oct 26 2022, 9:24 AM

I try WinGPG 4.1.0, and I receive an error:
ssh git@github.com
PTY allocation request failed on channel 0

Authentication succeed if I pressed enter after:PTY allocation request failed on channel 0

This does not look like a problem in GnuPG/gpg4win because gnupg implements the ssh-agent protocol and not the ssh server or client functionality. ssh tells sshd whether it shall allocate a PTY (Pseudo TTY). I don't use ssh with github but it is likely that you may only run commands (which don't require a PTY). Usually you would invoke a "git" command cia ssh.

Thanks all. It is a bug in Win32 OpenSSH. https://github.com/PowerShell/Win32-OpenSSH/issues/1953 it is already fixed. I think the issue will be resolved after the update is shipped. I could use ssh -T git@github.com as a workaround.

@gniibe Am I misunderstanding something? I thought that with this change one is able to connect from a Windows box to a Linux box and have GPG agent forwarding work. I am still hitting pretty much the same issue described here: https://github.com/PowerShell/Win32-OpenSSH/issues/1564
On my Windows endpoint I'm running gpg.exe version 2.4.0.49237 and in C:\Users\mate\AppData\Roaming\gnupg\gpg-agent.conf I have a single line enable-win32-openssh-support. Running gpg-connect-agent.exe reloadagent /bye I have a gpg-agent running. Get-Process gpg-agent shows that it's running. In my Windows env I have SSH_AUTH_SOCK set to \\.\pipe\openssh-ssh-agent and my Linux endpoint is configured in SSH config with

ForwardAgent yes
AddKeysToAgent yes
RemoteForward /run/user/1015/gnupg/S.gpg-agent C\:/Users/mate/AppData/Local/gnupg/S.gpg-agent.extra

As the remote end reports /run/user/1015/gnupg/S.gpg-agent that socket for agent-socket when issuing gpgconf --list-dirs and my local gpgconfg.exe --list-dirs reports C%3a\Users\mate\AppData\Local\gnupg\S.gpg-agent.extra where I transform %3a to \: manually. SSH authentication works perfectly, when connecting pinentry-qt pops up to unlock my key and when connecting to yet another machine, my SSH agent is forwarded again. However, gpg fails to use my agent. Issuing gpg --list-secret-keys --verbose prints the following to the console:

gpg --list-secret-keys --verbose
gpg: using pgp trust model
getsockopt SO_ERROR failed
connect_to C:/Users/mate/AppData/Local/gnupg/S.gpg-agent.extra port -2: failed.
gpg: no running gpg-agent - starting '/usr/bin/gpg-agent'
getsockopt SO_ERROR failed
connect_to C:/Users/mate/AppData/Local/gnupg/S.gpg-agent.extra port -2: failed.
gpg: waiting for the agent to come up ... (5s)
getsockopt SO_ERROR failed
connect_to C:/Users/mate/AppData/Local/gnupg/S.gpg-agent.extra port -2: failed.
getsockopt SO_ERROR failed
connect_to C:/Users/mate/AppData/Local/gnupg/S.gpg-agent.extra port -2: failed.
getsockopt SO_ERROR failed
connect_to C:/Users/mate/AppData/Local/gnupg/S.gpg-agent.extra port -2: failed.
getsockopt SO_ERROR failed
connect_to C:/Users/mate/AppData/Local/gnupg/S.gpg-agent.extra port -2: failed.
getsockopt SO_ERROR failed
connect_to C:/Users/mate/AppData/Local/gnupg/S.gpg-agent.extra port -2: failed.
getsockopt SO_ERROR failed
connect_to C:/Users/mate/AppData/Local/gnupg/S.gpg-agent.extra port -2: failed.
gpg: waiting for the agent to come up ... (4s)
gpg: waiting for the agent to come up ... (3s)
gpg: waiting for the agent to come up ... (2s)
gpg: waiting for the agent to come up ... (1s)
gpg: can't connect to the agent: End of file

What is missing to tie the knot on both ends without having to resort to 3rd party tools like @rupor-github 's agent-gui? The remote gpg version is 2.2.19, is that the issue? Must that also be 2.3.9+?

@MathiasMagnus This change is to support Win32-OpenSSH by gpg-agent emulation of ssh-agent; You can use gpg-agent emulation of ssh-agent when you use Win32-OpenSSH. That is, you can use GPG auth subkey for Win32-OpenSSH.

Sorry, It's not related to gpg-agent's Unix domain socket emulation on Windows.

In case if someone finds it through a search:

After adding enable-win32-openssh-support to gpg-agent.conf, need to specify the path to connect gpg-agent
just add/check lines in OpenSSH config file:

IdentityAgent "\\\\.\\pipe\\openssh-ssh-agent"
IdentityFile none

or set env:

set SSH_AUTH_SOCK=\\.\pipe\openssh-ssh-agent

in Git config:

[core]
	sshCommand = "'C:/OpenSSH/SSH.exe' -F C:/OpenSSH/config -v"

MinGit requires the full path to the OpenSSH executable, otherwise 'SSH' evokes bundled in MinGit, but it can't work with any method to connect gpg-agent.