Forward ssh-agent data between ssh-agent and RDP
authorBen Cohen <ben-cohen@users.noreply.github.com>
Mon, 26 Jun 2017 20:16:22 +0000 (21:16 +0100)
committerBen Cohen <ben-cohen@users.noreply.github.com>
Fri, 10 Nov 2017 20:16:00 +0000 (20:16 +0000)
Add the sshagent plugin to forward the ssh-agent protocol over an RDP
dynamic virtual channel, just as the normal ssh-agent forwards it over
an SSH channel.  Add the "/ssh-agent" command line option to enable it.
Usage:

Run FreeRDP with the ssh-agent plugin enabled:

   xfreerdp /ssh-agent ...

In the remote desktop session run xrdp-ssh-agent and evaluate the output
in the shell as for ssh-agent to set the required environment variables
(specifically $SSH_AUTH_SOCK):

   eval "$(xrdp-ssh-agent -s)"

This is the same as for the normal ssh-agent.  You would typically do
this in your Xsession or /etc/xrdp/startwm.sh.

Limitations:

1. Error checking and handling could be improved.

2. This is only tested on Linux and will only work on systems where
clients talk to the ssh-agent via Unix domain sockets.  It won't
currently work on Windows but it could be ported.

channels/sshagent/CMakeLists.txt [new file with mode: 0644]
channels/sshagent/ChannelOptions.cmake [new file with mode: 0644]
channels/sshagent/client/CMakeLists.txt [new file with mode: 0644]
channels/sshagent/client/sshagent_main.c [new file with mode: 0644]
channels/sshagent/client/sshagent_main.h [new file with mode: 0644]
client/common/cmdline.c
include/freerdp/client/sshagent.h [new file with mode: 0644]
include/freerdp/settings.h

diff --git a/channels/sshagent/CMakeLists.txt b/channels/sshagent/CMakeLists.txt
new file mode 100644 (file)
index 0000000..bc6cb2f
--- /dev/null
@@ -0,0 +1,26 @@
+# FreeRDP: A Remote Desktop Protocol Implementation
+# FreeRDP cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+define_channel("sshagent")
+
+if(WITH_CLIENT_CHANNELS)
+       add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
+endif()
+
+if(WITH_SERVER_CHANNELS)
+       add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
+endif()
diff --git a/channels/sshagent/ChannelOptions.cmake b/channels/sshagent/ChannelOptions.cmake
new file mode 100644 (file)
index 0000000..083d8d5
--- /dev/null
@@ -0,0 +1,13 @@
+
+set(OPTION_DEFAULT OFF)
+set(OPTION_CLIENT_DEFAULT OFF)
+set(OPTION_SERVER_DEFAULT OFF)
+
+define_channel_options(NAME "sshagent" TYPE "dynamic"
+       DESCRIPTION "SSH Agent Forwarding Extension"
+       SPECIFICATIONS ""
+       DEFAULT ${OPTION_DEFAULT})
+
+define_channel_client_options(${OPTION_CLIENT_DEFAULT})
+define_channel_server_options(${OPTION_SERVER_DEFAULT})
+
diff --git a/channels/sshagent/client/CMakeLists.txt b/channels/sshagent/client/CMakeLists.txt
new file mode 100644 (file)
index 0000000..bf8b1b8
--- /dev/null
@@ -0,0 +1,33 @@
+# FreeRDP: A Remote Desktop Protocol Implementation
+# FreeRDP cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+define_channel_client("sshagent")
+
+set(${MODULE_PREFIX}_SRCS
+       sshagent_main.c
+       sshagent_main.h)
+
+include_directories(..)
+
+add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
+
+if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS)
+       install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
+endif()
+
+target_link_libraries(${MODULE_NAME} winpr)
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
diff --git a/channels/sshagent/client/sshagent_main.c b/channels/sshagent/client/sshagent_main.c
new file mode 100644 (file)
index 0000000..61e6aaa
--- /dev/null
@@ -0,0 +1,398 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Echo Virtual Channel Extension
+ *
+ * Copyright 2013 Christian Hofstaedtler
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
+ * Copyright 2017 Ben Cohen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * sshagent_main.c: DVC plugin to forward queries from RDP to the ssh-agent
+ *
+ * This relays data to and from an ssh-agent program equivalent running on the
+ * RDP server to an ssh-agent running locally.  Unlike the normal ssh-agent,
+ * which sends data over an SSH channel, the data is send over an RDP dynamic
+ * virtual channel.
+ *
+ * protocol specification:
+ *     Forward data verbatim over RDP dynamic virtual channel named "sshagent"
+ *     between a ssh client on the xrdp server and the real ssh-agent where
+ *     the RDP client is running.  Each connection by a separate client to
+ *     xrdp-ssh-agent gets a separate DVC invocation.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+#include <winpr/thread.h>
+#include <winpr/stream.h>
+
+#include "sshagent_main.h"
+#include <freerdp/channels/log.h>
+
+#define TAG CHANNELS_TAG("sshagent.client")
+
+typedef struct _SSHAGENT_LISTENER_CALLBACK SSHAGENT_LISTENER_CALLBACK;
+struct _SSHAGENT_LISTENER_CALLBACK
+{
+       IWTSListenerCallback iface;
+
+       IWTSPlugin* plugin;
+       IWTSVirtualChannelManager* channel_mgr;
+
+        const char *agent_uds_path;
+};
+
+typedef struct _SSHAGENT_CHANNEL_CALLBACK SSHAGENT_CHANNEL_CALLBACK;
+struct _SSHAGENT_CHANNEL_CALLBACK
+{
+       IWTSVirtualChannelCallback iface;
+
+       IWTSPlugin* plugin;
+       IWTSVirtualChannelManager* channel_mgr;
+       IWTSVirtualChannel* channel;
+
+        int agent_fd;
+       HANDLE thread;
+       CRITICAL_SECTION lock;
+};
+
+typedef struct _SSHAGENT_PLUGIN SSHAGENT_PLUGIN;
+struct _SSHAGENT_PLUGIN
+{
+       IWTSPlugin iface;
+
+       SSHAGENT_LISTENER_CALLBACK* listener_callback;
+};
+
+
+/**
+ * Function to open the connection to the sshagent
+ *
+ * @return The fd on success, otherwise -1
+ */
+static int connect_to_sshagent(const char *udspath)
+{
+        int agent_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+        if (agent_fd == -1)
+        {
+                WLog_ERR(TAG, "Can't open Unix domain socket!");
+                return -1;
+        }
+
+        struct sockaddr_un addr;
+        memset(&addr, 0, sizeof(addr));
+        addr.sun_family = AF_UNIX;
+        strncpy(addr.sun_path, udspath, sizeof(addr.sun_path) - 1);
+        int rc = connect(agent_fd, (struct sockaddr*)&addr, sizeof(addr));
+        if (rc != 0)
+        {
+                WLog_ERR(TAG, "Can't connect to Unix domain socket \"%s\"!",
+                         udspath);
+                return -1;
+        }
+
+        return agent_fd;
+}
+
+
+/**
+ * Entry point for thread to read from the ssh-agent socket and forward
+ * the data to RDP
+ *
+ * @return NULL
+ */
+static void *sshagent_read_thread(void *data)
+{
+        SSHAGENT_CHANNEL_CALLBACK *callback = (SSHAGENT_CHANNEL_CALLBACK *)data;
+        BYTE buffer[4096];
+        int going = 1;
+        UINT status = CHANNEL_RC_OK;
+
+        while (going)
+        {
+                int bytes_read = read(callback->agent_fd,
+                                      buffer,
+                                      sizeof(buffer));
+
+                if (bytes_read == 0)
+                {
+                        /* Socket closed cleanly at other end */
+                        going = 0;
+                }
+                else if (bytes_read < 0)
+                {
+                        if (errno != EAGAIN
+                            && errno != EWOULDBLOCK
+                            && errno != EINTR)
+                        {
+                                WLog_ERR(TAG,
+                                         "Error reading from sshagent, errno=%d",
+                                         errno);
+                                status = ERROR_READ_FAULT;
+                                going = 0;
+                        }
+                }
+                else
+                {
+                        /* Something read: forward to virtual channel */
+                        status = callback->channel->Write(callback->channel,
+                                                          bytes_read,
+                                                          buffer,
+                                                          NULL);
+                        if (status != CHANNEL_RC_OK)
+                        {
+                                going = 0;
+                        }
+                }
+        }
+
+        close(callback->agent_fd);
+
+        //if (status != CHANNEL_RC_OK)
+        //        setChannelError(rdpei->rdpcontext, status,
+        //                        "sshagent_read_thread reported an error");
+
+       ExitThread(0);
+        return NULL;
+}
+
+/**
+ * Callback for data received from the RDP server; forward this to ssh-agent
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT sshagent_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data)
+{
+       SSHAGENT_CHANNEL_CALLBACK* callback = (SSHAGENT_CHANNEL_CALLBACK*) pChannelCallback;
+       BYTE* pBuffer = Stream_Pointer(data);
+       UINT32 cbSize = Stream_GetRemainingLength(data);
+        BYTE *pos = pBuffer;
+
+        /* Forward what we have received to the ssh agent */
+        UINT32 bytes_to_write = cbSize;
+        errno = 0;
+        while (bytes_to_write > 0)
+        {
+                int bytes_written = write(callback->agent_fd, pos,
+                                          bytes_to_write);
+                if (bytes_written < 0)
+                {
+                        if (errno != EAGAIN
+                            && errno != EWOULDBLOCK
+                            && errno != EINTR)
+                        {
+                                WLog_ERR(TAG,
+                                         "Error writing to sshagent, errno=%d",
+                                         errno);
+                                return ERROR_WRITE_FAULT;
+                        }
+                }
+                else
+                {
+                        bytes_to_write -= bytes_written;
+                        pos += bytes_written;
+                }
+        }
+
+        /* Consume stream */
+        Stream_Seek(data, cbSize);
+
+        return CHANNEL_RC_OK;
+}
+
+/**
+ * Callback for when the virtual channel is closed
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT sshagent_on_close(IWTSVirtualChannelCallback* pChannelCallback)
+{
+       SSHAGENT_CHANNEL_CALLBACK* callback = (SSHAGENT_CHANNEL_CALLBACK*) pChannelCallback;
+
+        /* Call shutdown() to wake up the read() in sshagent_read_thread(). */
+        shutdown(callback->agent_fd, SHUT_RDWR);
+
+       EnterCriticalSection(&callback->lock);
+       if (WaitForSingleObject(callback->thread, INFINITE) == WAIT_FAILED)
+       {
+               UINT error = GetLastError();
+               WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
+               return error;
+       }
+
+        CloseHandle(callback->thread);
+       DeleteCriticalSection(&callback->lock);
+
+       free(callback);
+
+       return CHANNEL_RC_OK;
+}
+
+
+/**
+ * Callback for when a new virtual channel is opened
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT sshagent_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
+       IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
+       IWTSVirtualChannelCallback** ppCallback)
+{
+       SSHAGENT_CHANNEL_CALLBACK* callback;
+       SSHAGENT_LISTENER_CALLBACK* listener_callback = (SSHAGENT_LISTENER_CALLBACK*) pListenerCallback;
+
+       callback = (SSHAGENT_CHANNEL_CALLBACK*) calloc(1, sizeof(SSHAGENT_CHANNEL_CALLBACK));
+
+       if (!callback)
+       {
+               WLog_ERR(TAG, "calloc failed!");
+               return CHANNEL_RC_NO_MEMORY;
+       }
+
+        /* Now open a connection to the local ssh-agent.  Do this for each
+         * connection to the plugin in case we mess up the agent session. */
+        callback->agent_fd
+                = connect_to_sshagent(listener_callback->agent_uds_path);
+        if (callback->agent_fd == -1)
+        {
+                return CHANNEL_RC_INITIALIZATION_ERROR;
+        }
+
+       InitializeCriticalSection(&callback->lock);
+
+       callback->iface.OnDataReceived = sshagent_on_data_received;
+       callback->iface.OnClose = sshagent_on_close;
+       callback->plugin = listener_callback->plugin;
+       callback->channel_mgr = listener_callback->channel_mgr;
+       callback->channel = pChannel;
+
+       if (!(callback->thread
+              = CreateThread(NULL,
+                             0,
+                             (LPTHREAD_START_ROUTINE) sshagent_read_thread,
+                             (void*) callback,
+                             0,
+                             NULL)))
+       {
+               WLog_ERR(TAG, "CreateThread failed!");
+                return CHANNEL_RC_INITIALIZATION_ERROR;
+       }
+
+       *ppCallback = (IWTSVirtualChannelCallback*) callback;
+
+       return CHANNEL_RC_OK;
+}
+
+/**
+ * Callback for when the plugin is initialised
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT sshagent_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
+{
+       SSHAGENT_PLUGIN* sshagent = (SSHAGENT_PLUGIN*) pPlugin;
+
+       sshagent->listener_callback = (SSHAGENT_LISTENER_CALLBACK*) calloc(1, sizeof(SSHAGENT_LISTENER_CALLBACK));
+
+       if (!sshagent->listener_callback)
+       {
+               WLog_ERR(TAG, "calloc failed!");
+               return CHANNEL_RC_NO_MEMORY;
+       }
+
+       sshagent->listener_callback->iface.OnNewChannelConnection = sshagent_on_new_channel_connection;
+       sshagent->listener_callback->plugin = pPlugin;
+       sshagent->listener_callback->channel_mgr = pChannelMgr;
+
+        sshagent->listener_callback->agent_uds_path = getenv("SSH_AUTH_SOCK");
+        if (sshagent->listener_callback->agent_uds_path == NULL)
+        {
+               WLog_ERR(TAG, "Environment variable $SSH_AUTH_SOCK undefined!");
+                return CHANNEL_RC_INITIALIZATION_ERROR;
+        }
+
+       return pChannelMgr->CreateListener(pChannelMgr, "SSHAGENT", 0,
+               (IWTSListenerCallback*) sshagent->listener_callback, NULL);
+}
+
+/**
+ * Callback for when the plugin is terminated
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT sshagent_plugin_terminated(IWTSPlugin* pPlugin)
+{
+       SSHAGENT_PLUGIN* sshagent = (SSHAGENT_PLUGIN*) pPlugin;
+
+       free(sshagent);
+
+       return CHANNEL_RC_OK;
+}
+
+#ifdef BUILTIN_CHANNELS
+#define DVCPluginEntry         sshagent_DVCPluginEntry
+#else
+#define DVCPluginEntry         FREERDP_API DVCPluginEntry
+#endif
+
+/**
+ * Main entry point for sshagent DVC plugin
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
+{
+       UINT status = CHANNEL_RC_OK;
+       SSHAGENT_PLUGIN* sshagent;
+
+       sshagent = (SSHAGENT_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "sshagent");
+
+       if (!sshagent)
+       {
+               sshagent = (SSHAGENT_PLUGIN*) calloc(1, sizeof(SSHAGENT_PLUGIN));
+
+               if (!sshagent)
+               {
+                       WLog_ERR(TAG, "calloc failed!");
+                       return CHANNEL_RC_NO_MEMORY;
+               }
+
+               sshagent->iface.Initialize = sshagent_plugin_initialize;
+               sshagent->iface.Connected = NULL;
+               sshagent->iface.Disconnected = NULL;
+               sshagent->iface.Terminated = sshagent_plugin_terminated;
+
+               status = pEntryPoints->RegisterPlugin(pEntryPoints, "sshagent", (IWTSPlugin*) sshagent);
+       }
+
+       return status;
+}
+
+/* vim: set sw=8:ts=8:noet: */
diff --git a/channels/sshagent/client/sshagent_main.h b/channels/sshagent/client/sshagent_main.h
new file mode 100644 (file)
index 0000000..135a758
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Echo Virtual Channel Extension
+ *
+ * Copyright 2013 Christian Hofstaedtler
+ * Copyright 2017 Ben Cohen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SSHAGENT_MAIN_H
+#define __SSHAGENT_MAIN_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <winpr/stream.h>
+
+#include <freerdp/svc.h>
+#include <freerdp/addin.h>
+#include <freerdp/channels/log.h>
+
+#define DVC_TAG CHANNELS_TAG("sshagent.client")
+#ifdef WITH_DEBUG_SSHAGENT
+#define DEBUG_SSHAGENT(...) WLog_DBG(DVC_TAG, __VA_ARGS__)
+#else
+#define DEBUG_SSHAGENT(...) do { } while (0)
+#endif
+
+#endif /* __SSHAGENT_MAIN_H */
+
index 4b328b6..9501628 100644 (file)
@@ -179,6 +179,7 @@ static COMMAND_LINE_ARGUMENT_A args[] =
        { "sound", COMMAND_LINE_VALUE_OPTIONAL, "[sys:<sys>,][dev:<dev>,][format:<format>,][rate:<rate>,][channel:<channel>,][latency:<latency>,][quality:<quality>]", NULL, NULL, -1, "audio", "Audio output (sound)" },
        { "span", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "Span screen over multiple monitors" },
        { "spn-class", COMMAND_LINE_VALUE_REQUIRED, "<service-class>", NULL, NULL, -1, NULL, "SPN authentication service class" },
+       { "ssh-agent", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "ssh-agent", "SSH Agent forwarding channel" },
        { "t", COMMAND_LINE_VALUE_REQUIRED, "<title>", NULL, NULL, -1, "title", "Window title" },
        { "themes", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Enable themes" },
        { "tls-ciphers", COMMAND_LINE_VALUE_REQUIRED, "netmon|ma|ciphers", NULL, NULL, -1, NULL, "Allowed TLS ciphers" },
@@ -928,6 +929,10 @@ static int freerdp_client_command_line_post_filter(void* context,
        {
                settings->SupportEchoChannel = TRUE;
        }
+       CommandLineSwitchCase(arg, "ssh-agent")
+       {
+               settings->SupportSSHAgentChannel = TRUE;
+       }
        CommandLineSwitchCase(arg, "disp")
        {
                settings->SupportDisplayControl = TRUE;
@@ -2904,6 +2909,17 @@ BOOL freerdp_client_load_addins(rdpChannels* channels, rdpSettings* settings)
                        return FALSE;
        }
 
+       if (settings->SupportSSHAgentChannel)
+       {
+               char* p[1];
+               int count;
+               count = 1;
+               p[0] = "sshagent";
+
+               if (!freerdp_client_add_dynamic_channel(settings, count, p))
+                       return FALSE;
+       }
+
        if (settings->SupportDisplayControl)
        {
                char* p[1];
diff --git a/include/freerdp/client/sshagent.h b/include/freerdp/client/sshagent.h
new file mode 100644 (file)
index 0000000..536408d
--- /dev/null
@@ -0,0 +1,90 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Clipboard Virtual Channel Extension
+ *
+ * Copyright 2011 Vic Lee
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
+ * Copyright 2017 Ben Cohen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_CHANNEL_CLIENT_SSHAGENT_H
+#define FREERDP_CHANNEL_CLIENT_SSHAGENT_H
+
+#include <freerdp/types.h>
+
+#include <freerdp/message.h>
+#include <freerdp/channels/cliprdr.h>
+#include <freerdp/freerdp.h>
+
+typedef struct _sshagent_client_context
+{
+  int ProtocolVersion;
+  int MaxConnections;
+} SSHAgentClientContext;
+
+
+/*
+ * The channel is defined by the sshagent channel in xrdp as follows.
+ *
+ * Server to client commands
+ * -------------------------
+ *
+ * Capabilities (at start of channel stream):
+ *
+ *    INT32  SA_TAG_CAPABILITY
+ *    INT32  SSHAGENT_CHAN_PROT_VERSION := 1
+ *    INT32  SSHAGENT_MAX_CONNECTIONS
+ *
+ * Open connection:
+ *
+ *    INT32  SA_TAG_OPEN
+ *    INT32  Connection id (0, ..., SSHAGENT_MAX_CONNECTIONS - 1)
+ *
+ * Send data:
+ * 
+ *    INT32  SA_TAG_WRITE
+ *    INT32  Connection id (0, ..., SSHAGENT_MAX_CONNECTIONS - 1)
+ *    INT32  Data length
+ *    DATA   ...
+ *
+ * Close connection:
+ *
+ *    INT32  SA_TAG_CLOSE
+ *    INT32  Connection id (0, ..., SSHAGENT_MAX_CONNECTIONS - 1)
+ *      
+ * Client to server commands
+ * -------------------------
+ *
+ * Capabilities (in reply to server capabilities):
+ *
+ *    INT32  SA_TAG_CAPABILITY
+ *    INT32  SSHAGENT_CHAN_PROT_VERSION := 1
+ *    INT32  SSHAGENT_MAX_CONNECTIONS
+ *
+ * Send data:
+ * 
+ *    INT32  SA_TAG_WRITE
+ *    INT32  Connection id (0, ..., SSHAGENT_MAX_CONNECTIONS - 1)
+ *    INT32  Data length
+ *    DATA   ...
+ *
+ * Close connection (abnormal):
+ *
+ *    INT32  SA_TAG_CLOSE
+ *    INT32  Connection id (0, ..., SSHAGENT_MAX_CONNECTIONS - 1)
+ */
+
+#endif /* FREERDP_CHANNEL_CLIENT_SSHAGENT_H */
index d6af94a..2f378de 100644 (file)
@@ -809,6 +809,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL;
 #define FreeRDP_SupportEchoChannel                             5184
 #define FreeRDP_SupportDisplayControl                          5185
 #define FreeRDP_SupportGeometryTracking                                5186
+#define FreeRDP_SupportSSHAgentChannel                         5187
 
 /**
  * FreeRDP Settings Data Structure
@@ -1422,7 +1423,8 @@ struct rdp_settings
        ALIGN64 BOOL SupportEchoChannel; /* 5184 */
        ALIGN64 BOOL SupportDisplayControl; /* 5185 */
        ALIGN64 BOOL SupportGeometryTracking; /* 5186 */
-       UINT64 padding5312[5312 - 5187]; /* 5187 */
+       ALIGN64 BOOL SupportSSHAgentChannel; /* 5187 */
+       UINT64 padding5312[5312 - 5188]; /* 5188 */
 
        /**
         * WARNING: End of ABI stable zone!