gnome-remote-desktop/add-smartcard-redirection-support.patch
Joan Torres Lopez 88023d834a
Add smartcard redirection support
Resolves: RHEL-219073
2026-08-19 17:16:12 +02:00

7534 lines
259 KiB
Diff

From 7f1620a727e698a3b73c01d13063b53e7473ef65 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 8 Jul 2026 00:33:21 +0200
Subject: [PATCH 01/11] session-rdp: Add device redirection channel support
Add GrdRdpDeviceRedirection and GrdRdpSmartcard to handle the RDPDR
static virtual channel. When the client joins the channel, grd-session-rdp
creates the device redirection context and starts it.
GrdRdpDeviceRedirection wraps FreeRDP's RdpdrServerContext and owns
GrdRdpSmartcard, which registers the RDPDR smartcard callbacks. This
is the entry point for forwarding local requests to the remote RDP
client through the RDPDR channel.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/merge_requests/406>
---
src/grd-rdp-device-redirection.c | 112 ++++++++++++++++++++
src/grd-rdp-device-redirection.h | 37 +++++++
src/grd-rdp-private.h | 1 +
src/grd-rdp-smartcard.c | 171 +++++++++++++++++++++++++++++++
src/grd-rdp-smartcard.h | 37 +++++++
src/grd-session-rdp.c | 8 ++
src/grd-types.h | 2 +
src/meson.build | 4 +
8 files changed, 372 insertions(+)
create mode 100644 src/grd-rdp-device-redirection.c
create mode 100644 src/grd-rdp-device-redirection.h
create mode 100644 src/grd-rdp-smartcard.c
create mode 100644 src/grd-rdp-smartcard.h
diff --git a/src/grd-rdp-device-redirection.c b/src/grd-rdp-device-redirection.c
new file mode 100644
index 0000000..7032d8a
--- /dev/null
+++ b/src/grd-rdp-device-redirection.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2026 Red Hat Inc.
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ * Joan Torres Lopez <joantolo@redhat.com>
+ */
+
+#include "config.h"
+
+#include "grd-rdp-device-redirection.h"
+
+#include <freerdp/server/rdpdr.h>
+
+#include "grd-rdp-smartcard.h"
+
+struct _GrdRdpDeviceRedirection
+{
+ GObject parent;
+
+ RdpdrServerContext *rdpdr_context;
+
+ GrdRdpSmartcard *smartcard;
+};
+
+G_DEFINE_TYPE (GrdRdpDeviceRedirection, grd_rdp_device_redirection,
+ G_TYPE_OBJECT)
+
+GrdRdpSmartcard *
+grd_rdp_device_redirection_get_smartcard (GrdRdpDeviceRedirection *device_redirection)
+{
+ return device_redirection->smartcard;
+}
+
+GrdRdpDeviceRedirection *
+grd_rdp_device_redirection_new (GrdSessionRdp *session_rdp,
+ HANDLE vcm)
+{
+ g_autoptr (GrdRdpDeviceRedirection) device_redirection = NULL;
+ RdpdrServerContext *rdpdr_context;
+
+ device_redirection = g_object_new (GRD_TYPE_RDP_DEVICE_REDIRECTION, NULL);
+
+ rdpdr_context = rdpdr_server_context_new (vcm);
+ if (!rdpdr_context)
+ g_error ("[RDP.RDPDR] Failed to create server context (OOM)");
+
+ device_redirection->rdpdr_context = rdpdr_context;
+ device_redirection->smartcard = grd_rdp_smartcard_new (session_rdp,
+ rdpdr_context);
+
+ rdpdr_context->data = device_redirection;
+
+ if (rdpdr_context->Start (rdpdr_context) != CHANNEL_RC_OK)
+ {
+ g_warning ("[RDP.RDPDR] Failed to start RDPDR channel");
+ g_clear_pointer (&device_redirection->rdpdr_context,
+ rdpdr_server_context_free);
+ return NULL;
+ }
+
+ return g_steal_pointer (&device_redirection);
+}
+
+static void
+grd_rdp_device_redirection_dispose (GObject *object)
+{
+ GrdRdpDeviceRedirection *device_redirection =
+ GRD_RDP_DEVICE_REDIRECTION (object);
+
+ if (device_redirection->smartcard)
+ grd_rdp_smartcard_invoke_shutdown (device_redirection->smartcard);
+
+ if (device_redirection->rdpdr_context)
+ {
+ device_redirection->rdpdr_context->Stop (
+ device_redirection->rdpdr_context);
+ g_clear_pointer (&device_redirection->rdpdr_context,
+ rdpdr_server_context_free);
+ }
+
+ g_clear_object (&device_redirection->smartcard);
+
+ G_OBJECT_CLASS (grd_rdp_device_redirection_parent_class)->dispose (object);
+}
+
+static void
+grd_rdp_device_redirection_init (GrdRdpDeviceRedirection *device_redirection)
+{
+}
+
+static void
+grd_rdp_device_redirection_class_init (GrdRdpDeviceRedirectionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = grd_rdp_device_redirection_dispose;
+}
diff --git a/src/grd-rdp-device-redirection.h b/src/grd-rdp-device-redirection.h
new file mode 100644
index 0000000..2f8e9f2
--- /dev/null
+++ b/src/grd-rdp-device-redirection.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2026 Red Hat Inc.
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ * Joan Torres Lopez <joantolo@redhat.com>
+ */
+
+#pragma once
+
+#include <freerdp/channels/wtsvc.h>
+#include <glib-object.h>
+
+#include "grd-types.h"
+
+#define GRD_TYPE_RDP_DEVICE_REDIRECTION (grd_rdp_device_redirection_get_type ())
+G_DECLARE_FINAL_TYPE (GrdRdpDeviceRedirection, grd_rdp_device_redirection,
+ GRD, RDP_DEVICE_REDIRECTION, GObject)
+
+GrdRdpDeviceRedirection *grd_rdp_device_redirection_new (GrdSessionRdp *session_rdp,
+ HANDLE vcm);
+
+GrdRdpSmartcard *grd_rdp_device_redirection_get_smartcard (GrdRdpDeviceRedirection *device_redirection);
diff --git a/src/grd-rdp-private.h b/src/grd-rdp-private.h
index 47057f1..1c280a8 100644
--- a/src/grd-rdp-private.h
+++ b/src/grd-rdp-private.h
@@ -50,4 +50,5 @@ typedef struct _RdpPeerContext
GrdRdpDvcInput *input;
#endif
GrdRdpDvcTelemetry *telemetry;
+ GrdRdpDeviceRedirection *device_redirection;
} RdpPeerContext;
diff --git a/src/grd-rdp-smartcard.c b/src/grd-rdp-smartcard.c
new file mode 100644
index 0000000..0cee1fe
--- /dev/null
+++ b/src/grd-rdp-smartcard.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2026 Red Hat Inc.
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ * Joan Torres Lopez <joantolo@redhat.com>
+ */
+
+#include "config.h"
+
+#include "grd-rdp-smartcard.h"
+
+#include "grd-context.h"
+#include "grd-rdp-device-redirection.h"
+#include "grd-session-rdp.h"
+
+struct _GrdRdpSmartcard
+{
+ GObject parent;
+
+ GMutex shutdown_mutex;
+ gboolean in_shutdown;
+
+ RdpdrServerContext *rdpdr_context;
+ GHashTable *smartcard_device_ids;
+};
+
+G_DEFINE_TYPE (GrdRdpSmartcard, grd_rdp_smartcard, G_TYPE_OBJECT)
+
+static UINT
+on_smartcard_create (RdpdrServerContext *rdpdr_context,
+ const RdpdrDevice *device)
+{
+ GrdRdpDeviceRedirection *device_redirection = rdpdr_context->data;
+ GrdRdpSmartcard *smartcard;
+
+ smartcard = grd_rdp_device_redirection_get_smartcard (device_redirection);
+ if (!smartcard)
+ return CHANNEL_RC_OK;
+
+ g_mutex_lock (&smartcard->shutdown_mutex);
+ if (smartcard->in_shutdown)
+ {
+ g_mutex_unlock (&smartcard->shutdown_mutex);
+ return CHANNEL_RC_OK;
+ }
+ g_mutex_unlock (&smartcard->shutdown_mutex);
+
+ if (g_hash_table_contains (smartcard->smartcard_device_ids,
+ GUINT_TO_POINTER (device->DeviceId)))
+ {
+ g_warning ("[RDP.SMARTCARD] Smartcard device already exists: id=%u",
+ device->DeviceId);
+ return CHANNEL_RC_OK;
+ }
+
+ g_debug ("[RDP.SMARTCARD] Smartcard device created: id=%u", device->DeviceId);
+
+ g_hash_table_add (smartcard->smartcard_device_ids,
+ GUINT_TO_POINTER (device->DeviceId));
+
+ return CHANNEL_RC_OK;
+}
+
+static UINT
+on_smartcard_delete (RdpdrServerContext *rdpdr_context,
+ uint32_t device_id)
+{
+ GrdRdpDeviceRedirection *device_redirection = rdpdr_context->data;
+ GrdRdpSmartcard *smartcard;
+
+ smartcard = grd_rdp_device_redirection_get_smartcard (device_redirection);
+ if (!smartcard)
+ return CHANNEL_RC_OK;
+
+ g_mutex_lock (&smartcard->shutdown_mutex);
+ if (smartcard->in_shutdown)
+ {
+ g_mutex_unlock (&smartcard->shutdown_mutex);
+ return CHANNEL_RC_OK;
+ }
+ g_mutex_unlock (&smartcard->shutdown_mutex);
+
+ if (!g_hash_table_remove (smartcard->smartcard_device_ids,
+ GUINT_TO_POINTER (device_id)))
+ return CHANNEL_RC_OK;
+
+ g_debug ("[RDP.SMARTCARD] Smartcard device deleted: id=%u", device_id);
+
+ return CHANNEL_RC_OK;
+}
+
+void
+grd_rdp_smartcard_invoke_shutdown (GrdRdpSmartcard *smartcard)
+{
+ g_mutex_lock (&smartcard->shutdown_mutex);
+ smartcard->in_shutdown = TRUE;
+ g_mutex_unlock (&smartcard->shutdown_mutex);
+}
+
+GrdRdpSmartcard *
+grd_rdp_smartcard_new (GrdSessionRdp *session_rdp,
+ RdpdrServerContext *rdpdr_context)
+{
+ GrdRdpSmartcard *smartcard;
+ GrdContext *context;
+
+ context = grd_session_get_context (GRD_SESSION (session_rdp));
+ g_assert (grd_context_get_runtime_mode (context) != GRD_RUNTIME_MODE_SYSTEM);
+
+ smartcard = g_object_new (GRD_TYPE_RDP_SMARTCARD, NULL);
+
+ smartcard->rdpdr_context = rdpdr_context;
+
+ rdpdr_context->OnSmartcardCreate = on_smartcard_create;
+ rdpdr_context->OnSmartcardDelete = on_smartcard_delete;
+
+ rdpdr_context->supported |= RDPDR_DTYP_SMARTCARD;
+
+ return smartcard;
+}
+static void
+grd_rdp_smartcard_dispose (GObject *object)
+{
+ GrdRdpSmartcard *smartcard = GRD_RDP_SMARTCARD (object);
+
+ g_clear_pointer (&smartcard->smartcard_device_ids, g_hash_table_unref);
+
+ G_OBJECT_CLASS (grd_rdp_smartcard_parent_class)->dispose (object);
+}
+
+static void
+grd_rdp_smartcard_finalize (GObject *object)
+{
+ GrdRdpSmartcard *smartcard = GRD_RDP_SMARTCARD (object);
+
+ g_mutex_clear (&smartcard->shutdown_mutex);
+
+ G_OBJECT_CLASS (grd_rdp_smartcard_parent_class)->finalize (object);
+}
+
+static void
+grd_rdp_smartcard_init (GrdRdpSmartcard *smartcard)
+{
+ g_mutex_init (&smartcard->shutdown_mutex);
+
+ smartcard->smartcard_device_ids = g_hash_table_new (NULL, NULL);
+}
+
+static void
+grd_rdp_smartcard_class_init (GrdRdpSmartcardClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = grd_rdp_smartcard_dispose;
+ object_class->finalize = grd_rdp_smartcard_finalize;
+}
diff --git a/src/grd-rdp-smartcard.h b/src/grd-rdp-smartcard.h
new file mode 100644
index 0000000..cca248f
--- /dev/null
+++ b/src/grd-rdp-smartcard.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2026 Red Hat Inc.
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ * Joan Torres Lopez <joantolo@redhat.com>
+ */
+
+#pragma once
+
+#include <freerdp/server/rdpdr.h>
+#include <glib-object.h>
+
+#include "grd-types.h"
+
+#define GRD_TYPE_RDP_SMARTCARD (grd_rdp_smartcard_get_type ())
+G_DECLARE_FINAL_TYPE (GrdRdpSmartcard, grd_rdp_smartcard,
+ GRD, RDP_SMARTCARD, GObject)
+
+GrdRdpSmartcard *grd_rdp_smartcard_new (GrdSessionRdp *session_rdp,
+ RdpdrServerContext *rdpdr_context);
+
+void grd_rdp_smartcard_invoke_shutdown (GrdRdpSmartcard *smartcard);
diff --git a/src/grd-session-rdp.c b/src/grd-session-rdp.c
index 72125a9..6c963da 100644
--- a/src/grd-session-rdp.c
+++ b/src/grd-session-rdp.c
@@ -22,6 +22,7 @@
#include "grd-session-rdp.h"
#include <freerdp/channels/drdynvc.h>
+#include <freerdp/channels/rdpdr.h>
#include <freerdp/crypto/crypto.h>
#include <freerdp/freerdp.h>
#include <freerdp/peer.h>
@@ -32,6 +33,7 @@
#include "grd-clipboard-rdp.h"
#include "grd-context.h"
#include "grd-rdp-cursor-renderer.h"
+#include "grd-rdp-device-redirection.h"
#include "grd-rdp-dvc-audio-input.h"
#include "grd-rdp-dvc-audio-playback.h"
#include "grd-rdp-dvc-display-control.h"
@@ -1631,6 +1633,7 @@ grd_session_rdp_stop (GrdSession *session)
g_mutex_lock (&rdp_peer_context->channel_mutex);
g_clear_object (&rdp_peer_context->audio_input);
g_clear_object (&rdp_peer_context->clipboard_rdp);
+ g_clear_object (&rdp_peer_context->device_redirection);
g_clear_object (&rdp_peer_context->audio_playback);
g_clear_object (&rdp_peer_context->display_control);
#if 0
@@ -1736,6 +1739,11 @@ initialize_remaining_virtual_channels (GrdSessionRdp *session_rdp)
rdp_peer_context->vcm,
get_max_monitor_count (session_rdp));
}
+ if (WTSVirtualChannelManagerIsChannelJoined (vcm, RDPDR_SVC_CHANNEL_NAME))
+ {
+ rdp_peer_context->device_redirection =
+ grd_rdp_device_redirection_new (session_rdp, vcm);
+ }
if (WTSVirtualChannelManagerIsChannelJoined (vcm, CLIPRDR_SVC_CHANNEL_NAME))
{
uint32_t os_major_type =
diff --git a/src/grd-types.h b/src/grd-types.h
index f392c13..84af5f8 100644
--- a/src/grd-types.h
+++ b/src/grd-types.h
@@ -47,6 +47,7 @@ typedef struct _GrdRdpBufferPool GrdRdpBufferPool;
typedef struct _GrdRdpConnectTimeAutodetection GrdRdpConnectTimeAutodetection;
typedef struct _GrdRdpCursorRenderer GrdRdpCursorRenderer;
typedef struct _GrdRdpDamageDetector GrdRdpDamageDetector;
+typedef struct _GrdRdpDeviceRedirection GrdRdpDeviceRedirection;
typedef struct _GrdRdpDvcAudioInput GrdRdpDvcAudioInput;
typedef struct _GrdRdpDvcAudioPlayback GrdRdpDvcAudioPlayback;
typedef struct _GrdRdpDvcDisplayControl GrdRdpDvcDisplayControl;
@@ -73,6 +74,7 @@ typedef struct _GrdRdpRenderer GrdRdpRenderer;
typedef struct _GrdRdpSAMFile GrdRdpSAMFile;
typedef struct _GrdRdpServer GrdRdpServer;
typedef struct _GrdRdpSessionMetrics GrdRdpSessionMetrics;
+typedef struct _GrdRdpSmartcard GrdRdpSmartcard;
typedef struct _GrdRdpStreamOwner GrdRdpStreamOwner;
typedef struct _GrdRdpSurface GrdRdpSurface;
typedef struct _GrdRdpSurfaceRenderer GrdRdpSurfaceRenderer;
diff --git a/src/meson.build b/src/meson.build
index 15a33a8..b2f6fd6 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -136,6 +136,8 @@ if have_rdp
'grd-rdp-damage-detector-cuda.h',
'grd-rdp-damage-detector-memcmp.c',
'grd-rdp-damage-detector-memcmp.h',
+ 'grd-rdp-device-redirection.c',
+ 'grd-rdp-device-redirection.h',
'grd-rdp-dsp.c',
'grd-rdp-dsp.h',
'grd-rdp-dvc.c',
@@ -198,6 +200,8 @@ if have_rdp
'grd-rdp-server.h',
'grd-rdp-session-metrics.c',
'grd-rdp-session-metrics.h',
+ 'grd-rdp-smartcard.c',
+ 'grd-rdp-smartcard.h',
'grd-rdp-stream-owner.c',
'grd-rdp-stream-owner.h',
'grd-rdp-surface.c',
From d1d5bee04dfd36cb44698a4d72e78b17f38fc25c Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 29 Jul 2026 17:49:07 +0200
Subject: [PATCH 02/11] daemon-utils: Move session ID lookup from D-Bus sender
to shared utility
This will be used later by grd-pcscd to identify the session
associated with a D-Bus caller.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/merge_requests/406>
---
src/grd-daemon-system.c | 52 ++++-------------------------------------
src/grd-daemon-utils.c | 44 ++++++++++++++++++++++++++++++++++
src/grd-daemon-utils.h | 5 ++++
3 files changed, 53 insertions(+), 48 deletions(-)
diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c
index 67e94b58..527a677a 100644
--- a/src/grd-daemon-system.c
+++ b/src/grd-daemon-system.c
@@ -231,50 +231,6 @@ get_routing_token_from_id (const char *id)
return g_strdup (id + strlen (REMOTE_DESKTOP_CLIENT_OBJECT_PATH "/"));
}
-static char *
-get_session_id_of_sender (GDBusConnection *connection,
- const char *name,
- GCancellable *cancellable,
- GError **error)
-{
- char *session_id = NULL;
- gboolean success;
- pid_t pid = 0;
- uid_t uid = 0;
-
- success = grd_get_pid_of_sender_sync (connection,
- name,
- &pid,
- cancellable,
- error);
- if (!success)
- return NULL;
-
- session_id = grd_get_session_id_from_pid (pid);
- if (session_id)
- return session_id;
-
- success = grd_get_uid_of_sender_sync (connection,
- name,
- &uid,
- cancellable,
- error);
- if (!success)
- return NULL;
-
- session_id = grd_get_session_id_from_uid (uid);
- if (!session_id)
- {
- g_set_error (error,
- G_IO_ERROR,
- G_IO_ERROR_NOT_FOUND,
- "Could not find a session for user %d",
- (int) uid);
- }
-
- return session_id;
-}
-
static char *
get_handover_object_path_for_call (GrdDaemonSystem *daemon_system,
GDBusMethodInvocation *invocation,
@@ -291,10 +247,10 @@ get_handover_object_path_for_call (GrdDaemonSystem *daemon_system,
sender = g_dbus_method_invocation_get_sender (invocation);
cancellable = grd_daemon_get_cancellable (GRD_DAEMON (daemon_system));
- session_id = get_session_id_of_sender (connection,
- sender,
- cancellable,
- error);
+ session_id = grd_get_session_id_of_sender (connection,
+ sender,
+ cancellable,
+ error);
if (!session_id)
return NULL;
diff --git a/src/grd-daemon-utils.c b/src/grd-daemon-utils.c
index 87c95cdf..06c4fde2 100644
--- a/src/grd-daemon-utils.c
+++ b/src/grd-daemon-utils.c
@@ -169,6 +169,50 @@ grd_get_session_id_from_uid (uid_t uid)
return g_strdup (session_id);
}
+char *
+grd_get_session_id_of_sender (GDBusConnection *connection,
+ const char *name,
+ GCancellable *cancellable,
+ GError **error)
+{
+ char *session_id = NULL;
+ gboolean success;
+ pid_t pid = 0;
+ uid_t uid = 0;
+
+ success = grd_get_pid_of_sender_sync (connection,
+ name,
+ &pid,
+ cancellable,
+ error);
+ if (!success)
+ return NULL;
+
+ session_id = grd_get_session_id_from_pid (pid);
+ if (session_id)
+ return session_id;
+
+ success = grd_get_uid_of_sender_sync (connection,
+ name,
+ &uid,
+ cancellable,
+ error);
+ if (!success)
+ return NULL;
+
+ session_id = grd_get_session_id_from_uid (uid);
+ if (!session_id)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_FOUND,
+ "Could not find a session for user %d",
+ (int) uid);
+ }
+
+ return session_id;
+}
+
gboolean
grd_is_remote_login (void)
{
diff --git a/src/grd-daemon-utils.h b/src/grd-daemon-utils.h
index 174ef309..19fe3d66 100644
--- a/src/grd-daemon-utils.h
+++ b/src/grd-daemon-utils.h
@@ -37,6 +37,11 @@ char *grd_get_session_id_from_pid (pid_t pid);
char *grd_get_session_id_from_uid (uid_t uid);
+char *grd_get_session_id_of_sender (GDBusConnection *connection,
+ const char *name,
+ GCancellable *cancellable,
+ GError **error);
+
gboolean grd_is_remote_login (void);
void grd_session_manager_call_logout_sync (void);
--
2.51.0
From ea08390a1c79e9afc0231a4ee76611b06fb3e10b Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 8 Jul 2026 00:57:37 +0200
Subject: [PATCH 03/11] rdp-smartcard: Add Pcscd D-Bus proxy
Add the org.gnome.RemoteDesktop.Pcscd D-Bus interface definition and
generate bindings for it. GrdRdpSmartcard now retrieves this proxy
when is redirecting a smartcard device and will use it to connect local PC/SC
requests to the remote RDP client through grd-pcscd.
In the next commit there'll be a dbus proxy to communicate with
grd-pcscd through a private dbus connection.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/merge_requests/406>
---
src/grd-private.h | 2 +
src/grd-rdp-smartcard.c | 75 +++++++++++++++++++++++++++
src/meson.build | 6 +++
src/org.gnome.RemoteDesktop.Pcscd.xml | 19 +++++++
4 files changed, 102 insertions(+)
create mode 100644 src/org.gnome.RemoteDesktop.Pcscd.xml
diff --git a/src/grd-private.h b/src/grd-private.h
index 70b236b9..5b1c91c0 100644
--- a/src/grd-private.h
+++ b/src/grd-private.h
@@ -39,6 +39,8 @@
#define MUTTER_REMOTE_DESKTOP_OBJECT_PATH "/org/gnome/Mutter/RemoteDesktop"
#define MUTTER_SCREEN_CAST_BUS_NAME "org.gnome.Mutter.ScreenCast"
#define MUTTER_SCREEN_CAST_OBJECT_PATH "/org/gnome/Mutter/ScreenCast"
+#define REMOTE_DESKTOP_PCSCD_BUS_NAME "org.gnome.RemoteDesktop.Pcscd"
+#define REMOTE_DESKTOP_PCSCD_OBJECT_PATH "/org/gnome/RemoteDesktop/Pcscd"
#define GDM_BUS_NAME "org.gnome.DisplayManager"
#define GDM_REMOTE_DISPLAY_FACTORY_OBJECT_PATH "/org/gnome/DisplayManager/RemoteDisplayFactory"
#define GDM_OBJECT_MANAGER_OBJECT_PATH "/org/gnome/DisplayManager/Displays"
diff --git a/src/grd-rdp-smartcard.c b/src/grd-rdp-smartcard.c
index 0cee1fea..e418ef54 100644
--- a/src/grd-rdp-smartcard.c
+++ b/src/grd-rdp-smartcard.c
@@ -25,6 +25,8 @@
#include "grd-rdp-smartcard.h"
#include "grd-context.h"
+#include "grd-dbus-pcscd.h"
+#include "grd-private.h"
#include "grd-rdp-device-redirection.h"
#include "grd-session-rdp.h"
@@ -37,10 +39,75 @@ struct _GrdRdpSmartcard
RdpdrServerContext *rdpdr_context;
GHashTable *smartcard_device_ids;
+
+ /* System bus proxy to grd-pcscd */
+ GCancellable *pcscd_proxy_cancellable;
+ GrdDBusPcscd *pcscd_proxy;
};
G_DEFINE_TYPE (GrdRdpSmartcard, grd_rdp_smartcard, G_TYPE_OBJECT)
+static void
+on_pcscd_proxy_acquired (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ g_autoptr (GError) error = NULL;
+ GrdDBusPcscd *pcscd_proxy;
+ g_autofree char *name_owner = NULL;
+
+ pcscd_proxy = grd_dbus_pcscd_proxy_new_for_bus_finish (result, &error);
+ if (!pcscd_proxy)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_warning ("[RDP.SMARTCARD] Failed to create pcscd proxy: %s",
+ error->message);
+ }
+ return;
+ }
+
+ smartcard->pcscd_proxy = pcscd_proxy;
+}
+
+static void
+setup_private_proxy (GrdRdpSmartcard *smartcard)
+{
+ g_assert (!smartcard->pcscd_proxy_cancellable);
+
+ smartcard->pcscd_proxy_cancellable = g_cancellable_new ();
+
+ grd_dbus_pcscd_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ REMOTE_DESKTOP_PCSCD_BUS_NAME,
+ REMOTE_DESKTOP_PCSCD_OBJECT_PATH,
+ smartcard->pcscd_proxy_cancellable,
+ on_pcscd_proxy_acquired,
+ smartcard);
+}
+
+static void
+teardown_private_proxy (GrdRdpSmartcard *smartcard)
+{
+ g_cancellable_cancel (smartcard->pcscd_proxy_cancellable);
+ g_clear_object (&smartcard->pcscd_proxy_cancellable);
+
+ g_clear_object (&smartcard->pcscd_proxy);
+}
+
+static void
+setup_pcscd_proxies (GrdRdpSmartcard *smartcard)
+{
+ setup_private_proxy (smartcard);
+}
+
+static void
+teardown_pcscd_proxies (GrdRdpSmartcard *smartcard)
+{
+ teardown_private_proxy (smartcard);
+}
+
static UINT
on_smartcard_create (RdpdrServerContext *rdpdr_context,
const RdpdrDevice *device)
@@ -70,6 +137,9 @@ on_smartcard_create (RdpdrServerContext *rdpdr_context,
g_debug ("[RDP.SMARTCARD] Smartcard device created: id=%u", device->DeviceId);
+ if (g_hash_table_size (smartcard->smartcard_device_ids) == 0)
+ setup_pcscd_proxies (smartcard);
+
g_hash_table_add (smartcard->smartcard_device_ids,
GUINT_TO_POINTER (device->DeviceId));
@@ -101,6 +171,9 @@ on_smartcard_delete (RdpdrServerContext *rdpdr_context,
g_debug ("[RDP.SMARTCARD] Smartcard device deleted: id=%u", device_id);
+ if (g_hash_table_size (smartcard->smartcard_device_ids) == 0)
+ teardown_pcscd_proxies (smartcard);
+
return CHANNEL_RC_OK;
}
@@ -138,6 +211,8 @@ grd_rdp_smartcard_dispose (GObject *object)
{
GrdRdpSmartcard *smartcard = GRD_RDP_SMARTCARD (object);
+ teardown_pcscd_proxies (smartcard);
+
g_clear_pointer (&smartcard->smartcard_device_ids, g_hash_table_unref);
G_OBJECT_CLASS (grd_rdp_smartcard_parent_class)->dispose (object);
diff --git a/src/meson.build b/src/meson.build
index 3343f6dc..1f1acda1 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -327,6 +327,12 @@ dbus_gen_remote_desktop = gnome.gdbus_codegen('grd-dbus-remote-desktop',
namespace: 'GrdDBusRemoteDesktop')
gen_daemon_sources += dbus_gen_remote_desktop
+dbus_gen_pcscd = gnome.gdbus_codegen('grd-dbus-pcscd',
+ 'org.gnome.RemoteDesktop.Pcscd.xml',
+ interface_prefix: 'org.gnome.RemoteDesktop.',
+ namespace: 'GrdDBus')
+gen_daemon_sources += dbus_gen_pcscd
+
if have_rdp
gen_daemon_sources += gnome.gdbus_codegen('grd-dbus-gdm',
'org.gnome.DisplayManager.xml',
diff --git a/src/org.gnome.RemoteDesktop.Pcscd.xml b/src/org.gnome.RemoteDesktop.Pcscd.xml
new file mode 100644
index 00000000..34c25302
--- /dev/null
+++ b/src/org.gnome.RemoteDesktop.Pcscd.xml
@@ -0,0 +1,19 @@
+<!DOCTYPE node PUBLIC
+ "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/dtds/introspection-1.0.dtd">
+<node>
+
+ <!--
+ org.gnome.RemoteDesktop.Pcscd:
+ @short_description: PC/SC daemon interface for smartcard redirection
+
+ This API is exposed by grd-pcscd on the system bus.
+ gnome-remote-desktop-daemons use it to establish a private D-Bus
+ connection with grd-pcscd for smartcard operations.
+ No compatibility between versions is promised.
+ -->
+ <interface name="org.gnome.RemoteDesktop.Pcscd">
+
+ </interface>
+
+</node>
--
2.51.0
From a9c9b959f24fde92f24b3e2c0e24cad5b95e5cc8 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 8 Jul 2026 01:40:51 +0200
Subject: [PATCH 04/11] rdp-smartcard: Add grd-pcscd daemon and private D-Bus
connection
Add the grd-pcscd daemon, a system-level service that bridges local
PC/SC clients with RDP smartcard sessions.
When a smartcard device is announced by the RDP client, GrdRdpSmartcard
calls Connect() on grd-pcscd, passing a socketpair fd to establish a
private D-Bus connection shared between both sides. grd-pcscd creates
a GrdPcscdSession for each connection, which is the bridge between
local PC/SC requests and the remote smartcard. GrdRdpSmartcard exposes
the Pcscd.Session interface on the private D-Bus connection, and
GrdPcscdSession uses it to forward calls from local clients.
GrdPcscdSession will export Pcscd.Session on the system bus in the
next commit. The Pcscd.Session interface is empty for now; actual
PC/SC method handlers will be added in subsequent commits.
Also add the systemd service unit and its D-Bus bus policy.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/merge_requests/406>
---
data/gnome-remote-desktop-pcscd.service.in | 42 +++
data/meson.build | 23 ++
data/org.gnome.RemoteDesktop.Pcscd.conf.in | 23 ++
data/org.gnome.RemoteDesktop.Pcscd.service.in | 5 +
src/grd-pcscd-session.c | 220 ++++++++++++++++
src/grd-pcscd-session.h | 35 +++
src/grd-pcscd.c | 246 ++++++++++++++++++
src/grd-rdp-smartcard.c | 194 ++++++++++++++
src/meson.build | 15 ++
src/org.gnome.RemoteDesktop.Pcscd.xml | 40 +++
10 files changed, 843 insertions(+)
create mode 100644 data/gnome-remote-desktop-pcscd.service.in
create mode 100644 data/org.gnome.RemoteDesktop.Pcscd.conf.in
create mode 100644 data/org.gnome.RemoteDesktop.Pcscd.service.in
create mode 100644 src/grd-pcscd-session.c
create mode 100644 src/grd-pcscd-session.h
create mode 100644 src/grd-pcscd.c
diff --git a/data/gnome-remote-desktop-pcscd.service.in b/data/gnome-remote-desktop-pcscd.service.in
new file mode 100644
index 00000000..5f6c71a2
--- /dev/null
+++ b/data/gnome-remote-desktop-pcscd.service.in
@@ -0,0 +1,42 @@
+[Unit]
+Description=GNOME Remote Desktop PC/SC Daemon
+
+[Service]
+Type=dbus
+BusName=org.gnome.RemoteDesktop.Pcscd
+User=@GRD_USERNAME@
+ExecStart=@libexecdir@/grd-pcscd
+RuntimeDirectory=grd-pcscd
+
+# Filesystem
+ProtectSystem=strict
+ProtectHome=yes
+PrivateTmp=yes
+PrivateDevices=yes
+
+# Kernel
+ProtectKernelTunables=yes
+ProtectKernelModules=yes
+ProtectKernelLogs=yes
+ProtectControlGroups=yes
+ProtectClock=yes
+ProtectHostname=yes
+
+# Privileges
+NoNewPrivileges=yes
+CapabilityBoundingSet=
+RestrictSUIDSGID=yes
+LockPersonality=yes
+
+# Network
+RestrictAddressFamilies=AF_UNIX
+IPAddressDeny=any
+
+# Misc
+RestrictRealtime=yes
+RestrictNamespaces=yes
+MemoryDenyWriteExecute=yes
+RemoveIPC=yes
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+SystemCallFilter=~@privileged @resources
diff --git a/data/meson.build b/data/meson.build
index e12c1d51..b81acf5c 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -71,6 +71,21 @@ if get_option('systemd')
},
install_dir: userunitdir)
+ configure_file(input: 'gnome-remote-desktop-pcscd.service.in',
+ output: 'gnome-remote-desktop-pcscd.service',
+ configuration: {
+ 'libexecdir': libexecdir,
+ 'GRD_USERNAME': grd_username,
+ },
+ install_dir: systemunitdir)
+
+ configure_file(input: 'org.gnome.RemoteDesktop.Pcscd.conf.in',
+ output: 'org.gnome.RemoteDesktop.Pcscd.conf',
+ configuration: {
+ 'GRD_USERNAME': grd_username,
+ },
+ install_dir: dbus_sys_dir)
+
configure_file(input: 'gnome-remote-desktop-configuration.service.in',
output: 'gnome-remote-desktop-configuration.service',
configuration: {
@@ -103,6 +118,14 @@ if get_option('systemd')
},
install_dir: join_paths(datadir, 'polkit-1', 'actions'))
+ configure_file(input: 'org.gnome.RemoteDesktop.Pcscd.service.in',
+ output: 'org.gnome.RemoteDesktop.Pcscd.service',
+ configuration: {
+ 'libexecdir': libexecdir,
+ 'GRD_USERNAME': grd_username,
+ },
+ install_dir: dbus_system_services_dir)
+
configure_file(input: 'org.gnome.RemoteDesktop.Configuration.service.in',
output: 'org.gnome.RemoteDesktop.Configuration.service',
configuration: {
diff --git a/data/org.gnome.RemoteDesktop.Pcscd.conf.in b/data/org.gnome.RemoteDesktop.Pcscd.conf.in
new file mode 100644
index 00000000..410a4eb4
--- /dev/null
+++ b/data/org.gnome.RemoteDesktop.Pcscd.conf.in
@@ -0,0 +1,23 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+ <!-- Only @GRD_USERNAME@ can own the service -->
+ <policy user="@GRD_USERNAME@">
+ <allow own="org.gnome.RemoteDesktop.Pcscd"/>
+ </policy>
+
+ <!-- Allow anyone to introspect and call methods -->
+ <policy context="default">
+ <allow send_destination="org.gnome.RemoteDesktop.Pcscd"
+ send_interface="org.freedesktop.DBus.Introspectable"/>
+ <allow send_destination="org.gnome.RemoteDesktop.Pcscd"
+ send_interface="org.freedesktop.DBus.Properties"/>
+ <allow send_destination="org.gnome.RemoteDesktop.Pcscd"
+ send_interface="org.gnome.RemoteDesktop.Pcscd"/>
+ <allow send_destination="org.gnome.RemoteDesktop.Pcscd"
+ send_interface="org.gnome.RemoteDesktop.Pcscd.Session"/>
+ </policy>
+
+</busconfig>
diff --git a/data/org.gnome.RemoteDesktop.Pcscd.service.in b/data/org.gnome.RemoteDesktop.Pcscd.service.in
new file mode 100644
index 00000000..d1d42139
--- /dev/null
+++ b/data/org.gnome.RemoteDesktop.Pcscd.service.in
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=org.gnome.RemoteDesktop.Pcscd
+Exec=@libexecdir@/grd-pcscd
+User=@GRD_USERNAME@
+SystemdService=gnome-remote-desktop-pcscd.service
diff --git a/src/grd-pcscd-session.c b/src/grd-pcscd-session.c
new file mode 100644
index 00000000..26d76b71
--- /dev/null
+++ b/src/grd-pcscd-session.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2026 Red Hat Inc.
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ * Joan Torres Lopez <joantolo@redhat.com>
+ */
+
+#include "config.h"
+
+#include "grd-pcscd-session.h"
+
+#include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
+#include <glib/gstdio.h>
+
+#include "grd-dbus-pcscd.h"
+
+struct _GrdPcscdSession
+{
+ GObject parent;
+
+ char *session_id;
+
+ GCancellable *cancellable;
+
+ GrdDBusPcscdSession *private_proxy;
+};
+
+enum
+{
+ CLOSED,
+
+ N_SIGNALS,
+};
+
+static guint signals[N_SIGNALS];
+
+G_DEFINE_TYPE (GrdPcscdSession, grd_pcscd_session, G_TYPE_OBJECT)
+
+static void
+on_private_connection_closed (GDBusConnection *connection,
+ gboolean remote_peer_vanished,
+ GError *error,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ g_debug ("[PCSCD.SESSION %s] Private connection closed", session->session_id);
+
+ g_signal_emit (session, signals[CLOSED], 0);
+}
+
+static void
+on_private_proxy_ready (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GrdDBusPcscdSession) private_proxy = NULL;
+ g_autoptr (GError) error = NULL;
+ GrdPcscdSession *session = user_data;
+ GDBusConnection *private_connection;
+
+ private_proxy = grd_dbus_pcscd_session_proxy_new_finish (result, &error);
+ if (!private_proxy)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_warning ("[PCSCD.SESSION %s] Failed to create private proxy: %s",
+ session->session_id, error->message);
+ g_signal_emit (session, signals[CLOSED], 0);
+ }
+ return;
+ }
+
+ private_connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (private_proxy));
+ g_signal_connect_object (private_connection, "closed",
+ G_CALLBACK (on_private_connection_closed),
+ session, G_CONNECT_DEFAULT);
+
+ session->private_proxy = g_steal_pointer (&private_proxy);
+
+ g_debug ("[PCSCD.SESSION %s] Private D-Bus connection established",
+ session->session_id);
+}
+
+static void
+on_private_connection_ready (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+ g_autoptr (GDBusConnection) private_connection = NULL;
+ g_autoptr (GError) error = NULL;
+
+ private_connection = g_dbus_connection_new_finish (result, &error);
+ if (!private_connection)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_warning ("[PCSCD.SESSION %s] Failed to create private connection: %s",
+ session->session_id, error->message);
+ g_signal_emit (session, signals[CLOSED], 0);
+ }
+ return;
+ }
+
+ grd_dbus_pcscd_session_proxy_new (private_connection,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "/",
+ session->cancellable,
+ on_private_proxy_ready,
+ session);
+}
+
+static void
+create_private_proxy (GrdPcscdSession *session,
+ int fd)
+{
+ g_autoptr (GInputStream) input_stream = NULL;
+ g_autoptr (GOutputStream) output_stream = NULL;
+ g_autoptr (GIOStream) io_stream = NULL;
+ g_autofree char *guid = NULL;
+
+ /* fd ownership: fd -> input_stream -> io_stream -> private_connection -> proxy */
+ input_stream = g_unix_input_stream_new (fd, TRUE);
+ output_stream = g_unix_output_stream_new (fd, FALSE);
+ io_stream = g_simple_io_stream_new (input_stream, output_stream);
+
+ guid = g_dbus_generate_guid ();
+ g_dbus_connection_new (io_stream,
+ guid,
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER |
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS,
+ NULL,
+ session->cancellable,
+ on_private_connection_ready,
+ session);
+}
+
+const char *
+grd_pcscd_session_get_session_id (GrdPcscdSession *session)
+{
+ return session->session_id;
+}
+
+GrdPcscdSession *
+grd_pcscd_session_new (const char *session_id,
+ int fd,
+ GDBusConnection *system_connection)
+{
+ g_autoptr (GrdPcscdSession) session = NULL;
+
+ session = g_object_new (GRD_TYPE_PCSCD_SESSION, NULL);
+ session->session_id = g_strdup (session_id);
+ session->cancellable = g_cancellable_new ();
+
+ create_private_proxy (session, fd);
+
+ return g_steal_pointer (&session);
+}
+
+static void
+grd_pcscd_session_dispose (GObject *object)
+{
+ GrdPcscdSession *session = GRD_PCSCD_SESSION (object);
+
+ g_cancellable_cancel (session->cancellable);
+ g_clear_object (&session->cancellable);
+
+ if (session->private_proxy)
+ {
+ GDBusConnection *private_connection =
+ g_dbus_proxy_get_connection (G_DBUS_PROXY (session->private_proxy));
+
+ if (!g_dbus_connection_is_closed (private_connection))
+ g_dbus_connection_close (private_connection, NULL, NULL, NULL);
+
+ g_clear_object (&session->private_proxy);
+ }
+
+ g_clear_pointer (&session->session_id, g_free);
+
+ G_OBJECT_CLASS (grd_pcscd_session_parent_class)->dispose (object);
+}
+
+static void
+grd_pcscd_session_init (GrdPcscdSession *session)
+{
+}
+
+static void
+grd_pcscd_session_class_init (GrdPcscdSessionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = grd_pcscd_session_dispose;
+
+ signals[CLOSED] =
+ g_signal_new ("closed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
diff --git a/src/grd-pcscd-session.h b/src/grd-pcscd-session.h
new file mode 100644
index 00000000..bafcb1ef
--- /dev/null
+++ b/src/grd-pcscd-session.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2026 Red Hat Inc.
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ * Joan Torres Lopez <joantolo@redhat.com>
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+#define GRD_TYPE_PCSCD_SESSION (grd_pcscd_session_get_type ())
+G_DECLARE_FINAL_TYPE (GrdPcscdSession, grd_pcscd_session,
+ GRD, PCSCD_SESSION, GObject)
+
+GrdPcscdSession *grd_pcscd_session_new (const char *session_id,
+ int fd,
+ GDBusConnection *system_connection);
+
+const char *grd_pcscd_session_get_session_id (GrdPcscdSession *session);
diff --git a/src/grd-pcscd.c b/src/grd-pcscd.c
new file mode 100644
index 00000000..a70ce574
--- /dev/null
+++ b/src/grd-pcscd.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2026 Red Hat Inc.
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ * Joan Torres Lopez <joantolo@redhat.com>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <glib-unix.h>
+#include <stdint.h>
+
+#include "grd-daemon-utils.h"
+#include "grd-dbus-pcscd.h"
+#include "grd-pcscd-session.h"
+
+#define PCSCD_BUS_NAME "org.gnome.RemoteDesktop.Pcscd"
+#define PCSCD_OBJECT_PATH "/org/gnome/RemoteDesktop/Pcscd"
+
+typedef struct _GrdPcscd
+{
+ GMainLoop *loop;
+ guint sigint_source_id;
+ guint sigterm_source_id;
+
+ guint own_name_id;
+ GrdDBusPcscd *skeleton;
+ GDBusConnection *connection;
+
+ GHashTable *sessions;
+} GrdPcscd;
+
+static void
+grd_pcscd_free (GrdPcscd *pcscd)
+{
+ g_clear_pointer (&pcscd->sessions, g_hash_table_unref);
+
+ g_clear_handle_id (&pcscd->sigint_source_id, g_source_remove);
+ g_clear_handle_id (&pcscd->sigterm_source_id, g_source_remove);
+
+ g_clear_handle_id (&pcscd->own_name_id, g_bus_unown_name);
+
+ if (pcscd->skeleton)
+ {
+ g_dbus_interface_skeleton_unexport (
+ G_DBUS_INTERFACE_SKELETON (pcscd->skeleton));
+ g_clear_object (&pcscd->skeleton);
+ }
+
+ g_clear_object (&pcscd->connection);
+ g_clear_pointer (&pcscd->loop, g_main_loop_unref);
+}
+
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (GrdPcscd, grd_pcscd_free)
+
+static void
+on_session_closed (GrdPcscdSession *session,
+ gpointer user_data)
+{
+ GrdPcscd *pcscd = user_data;
+ const char *session_id;
+
+ session_id = grd_pcscd_session_get_session_id (session);
+
+ g_message ("Closing smartcard session %s", session_id);
+
+ g_hash_table_remove (pcscd->sessions, session_id);
+}
+
+static gboolean
+on_handle_connect (GrdDBusPcscd *skeleton,
+ GDBusMethodInvocation *invocation,
+ GUnixFDList *fd_list,
+ GVariant *arg_rdp_fd,
+ gpointer user_data)
+{
+ g_autofree char *session_id = NULL;
+ g_autoptr (GError) error = NULL;
+ g_autofd int fd = -1;
+ GrdPcscd *pcscd = user_data;
+ GDBusConnection *connection;
+ GrdPcscdSession *session;
+ const char *sender;
+ int fd_idx;
+
+ if (!G_IS_UNIX_FD_LIST (fd_list))
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "No fd list received");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ fd_idx = g_variant_get_handle (arg_rdp_fd);
+ fd = g_unix_fd_list_get (fd_list, fd_idx, &error);
+ if (fd < 0)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Failed to get fd: %s",
+ error->message);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ connection = g_dbus_method_invocation_get_connection (invocation);
+ sender = g_dbus_method_invocation_get_sender (invocation);
+
+ session_id = grd_get_session_id_of_sender (connection, sender, NULL, &error);
+ if (!session_id)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "Failed to get session of "
+ "sender: %s",
+ error->message);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ if (g_hash_table_contains (pcscd->sessions, session_id))
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "Session %s already exists",
+ session_id);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ session = grd_pcscd_session_new (session_id,
+ g_steal_fd (&fd),
+ pcscd->connection);
+
+ g_signal_connect (session, "closed",
+ G_CALLBACK (on_session_closed),
+ pcscd);
+
+ g_hash_table_insert (pcscd->sessions, g_strdup (session_id), session);
+
+ g_message ("New smartcard session %s", session_id);
+
+ grd_dbus_pcscd_complete_connect (skeleton, invocation, NULL);
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ g_autoptr (GrdDBusPcscd) skeleton = NULL;
+ g_autoptr (GError) error = NULL;
+ GrdPcscd *pcscd = user_data;
+
+ skeleton = grd_dbus_pcscd_skeleton_new ();
+ g_signal_connect (skeleton, "handle-connect",
+ G_CALLBACK (on_handle_connect),
+ pcscd);
+
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
+ connection,
+ PCSCD_OBJECT_PATH,
+ &error))
+ {
+ g_warning ("Failed to export Pcscd interface: %s", error->message);
+ g_main_loop_quit (pcscd->loop);
+ return;
+ }
+
+ g_set_object (&pcscd->skeleton, skeleton);
+ g_set_object (&pcscd->connection, connection);
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ GrdPcscd *pcscd = user_data;
+
+ g_warning ("Lost bus name %s", name);
+
+ g_main_loop_quit (pcscd->loop);
+}
+
+static gboolean
+on_signal_quit (gpointer user_data)
+{
+ GrdPcscd *pcscd = user_data;
+
+ g_message ("Received signal, shutting down");
+
+ g_clear_handle_id (&pcscd->sigint_source_id, g_source_remove);
+ g_clear_handle_id (&pcscd->sigterm_source_id, g_source_remove);
+
+ g_main_loop_quit (pcscd->loop);
+
+ return G_SOURCE_REMOVE;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ g_auto (GrdPcscd) pcscd = {};
+
+ pcscd.loop = g_main_loop_new (NULL, FALSE);
+ pcscd.sessions = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_object_unref);
+
+ pcscd.own_name_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
+ PCSCD_BUS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ on_bus_acquired,
+ NULL,
+ on_name_lost,
+ &pcscd,
+ NULL);
+
+ pcscd.sigint_source_id = g_unix_signal_add (SIGINT, on_signal_quit, &pcscd);
+ pcscd.sigterm_source_id = g_unix_signal_add (SIGTERM, on_signal_quit, &pcscd);
+
+ g_main_loop_run (pcscd.loop);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/grd-rdp-smartcard.c b/src/grd-rdp-smartcard.c
index e418ef54..a674fc0e 100644
--- a/src/grd-rdp-smartcard.c
+++ b/src/grd-rdp-smartcard.c
@@ -24,12 +24,22 @@
#include "grd-rdp-smartcard.h"
+#include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
+#include <glib/gstdio.h>
+
#include "grd-context.h"
#include "grd-dbus-pcscd.h"
#include "grd-private.h"
#include "grd-rdp-device-redirection.h"
#include "grd-session-rdp.h"
+typedef struct
+{
+ GrdRdpSmartcard *smartcard;
+ int fd;
+} PcscdConnectData;
+
struct _GrdRdpSmartcard
{
GObject parent;
@@ -42,11 +52,181 @@ struct _GrdRdpSmartcard
/* System bus proxy to grd-pcscd */
GCancellable *pcscd_proxy_cancellable;
+ unsigned long name_owner_changed_id;
GrdDBusPcscd *pcscd_proxy;
+
+ /* Private peer-to-peer proxy for system-level clients (via grd-pcscd) */
+ GCancellable *private_proxy_cancellable;
+ GrdDBusPcscdSession *private_proxy;
};
G_DEFINE_TYPE (GrdRdpSmartcard, grd_rdp_smartcard, G_TYPE_OBJECT)
+static void
+on_private_connection_ready (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusConnection) private_connection = NULL;
+ g_autoptr (GrdDBusPcscdSession) private_proxy = NULL;
+ g_autoptr (GError) error = NULL;
+ GrdRdpSmartcard *smartcard = user_data;
+
+ private_connection = g_dbus_connection_new_finish (result, &error);
+ if (!private_connection)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_warning ("[RDP.SMARTCARD] Failed to create private connection: %s",
+ error->message);
+ }
+ return;
+ }
+
+ private_proxy = grd_dbus_pcscd_session_skeleton_new ();
+ if (!g_dbus_interface_skeleton_export (
+ G_DBUS_INTERFACE_SKELETON (private_proxy),
+ private_connection,
+ "/",
+ &error))
+ {
+ g_warning ("[RDP.SMARTCARD] Failed to export session skeleton: %s",
+ error->message);
+ return;
+ }
+
+ smartcard->private_proxy = g_steal_pointer (&private_proxy);
+
+ g_debug ("[RDP.SMARTCARD] Private D-Bus connection established");
+}
+
+static void
+create_private_proxy (GrdRdpSmartcard *smartcard,
+ int fd)
+{
+ g_autoptr (GOutputStream) output_stream = NULL;
+ g_autoptr (GInputStream) input_stream = NULL;
+ g_autoptr (GIOStream) io_stream = NULL;
+
+ /* fd ownership: fd -> output_stream -> io_stream -> private_connection -> proxy */
+ output_stream = g_unix_output_stream_new (fd, TRUE);
+ input_stream = g_unix_input_stream_new (fd, FALSE);
+ io_stream = g_simple_io_stream_new (input_stream, output_stream);
+
+ g_dbus_connection_new (io_stream,
+ NULL,
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ NULL,
+ smartcard->private_proxy_cancellable,
+ on_private_connection_ready,
+ smartcard);
+}
+
+static void
+on_connect_to_grd_pcscd_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GrdDBusPcscd *pcscd_proxy = GRD_DBUS_PCSCD (source_object);
+ PcscdConnectData *data = user_data;
+ GrdRdpSmartcard *smartcard = data->smartcard;
+ g_autofd int fd = g_steal_fd (&data->fd);
+ g_autoptr (GError) error = NULL;
+
+ g_free (data);
+
+ if (!grd_dbus_pcscd_call_connect_finish (pcscd_proxy,
+ NULL,
+ result,
+ &error))
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_warning ("[RDP.SMARTCARD] Failed to connect to pcscd: %s",
+ error->message);
+ }
+ return;
+ }
+
+ g_debug ("[RDP.SMARTCARD] Connected to pcscd, setting up private D-Bus");
+
+ create_private_proxy (smartcard, g_steal_fd (&fd));
+}
+
+static void
+connect_to_grd_pcscd (GrdRdpSmartcard *smartcard)
+{
+ g_autoptr (GUnixFDList) fd_list = NULL;
+ g_autoptr (GError) error = NULL;
+ PcscdConnectData *data;
+ g_autofd int local_fd = -1;
+ int fds[2];
+ int fd_idx;
+
+ g_assert (!smartcard->private_proxy_cancellable);
+
+ smartcard->private_proxy_cancellable = g_cancellable_new ();
+
+ if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) < 0)
+ {
+ g_warning ("[RDP.SMARTCARD] Failed to create socketpair: %s",
+ g_strerror (errno));
+ return;
+ }
+
+ local_fd = fds[0];
+
+ fd_list = g_unix_fd_list_new ();
+ fd_idx = g_unix_fd_list_append (fd_list, fds[1], &error);
+ close (fds[1]);
+
+ if (fd_idx < 0)
+ {
+ g_warning ("[RDP.SMARTCARD] Failed to append fd to list: %s",
+ error->message);
+ return;
+ }
+
+ data = g_new0 (PcscdConnectData, 1);
+ data->smartcard = smartcard;
+ data->fd = g_steal_fd (&local_fd);
+
+ grd_dbus_pcscd_call_connect (smartcard->pcscd_proxy,
+ g_variant_new_handle (fd_idx),
+ fd_list,
+ smartcard->private_proxy_cancellable,
+ on_connect_to_grd_pcscd_finished,
+ data);
+}
+
+static void
+disconnect_from_grd_pcscd (GrdRdpSmartcard *smartcard)
+{
+ g_cancellable_cancel (smartcard->private_proxy_cancellable);
+ g_clear_object (&smartcard->private_proxy_cancellable);
+
+ if (smartcard->private_proxy)
+ {
+ g_dbus_interface_skeleton_unexport (
+ G_DBUS_INTERFACE_SKELETON (smartcard->private_proxy));
+ g_clear_object (&smartcard->private_proxy);
+ }
+}
+
+static void
+on_grd_pcscd_name_owner_changed (GrdRdpSmartcard *smartcard)
+{
+ GrdDBusPcscd *pcscd_proxy = smartcard->pcscd_proxy;
+ g_autofree char *name_owner = NULL;
+
+ name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (pcscd_proxy));
+
+ disconnect_from_grd_pcscd (smartcard);
+
+ if (name_owner)
+ connect_to_grd_pcscd (smartcard);
+}
+
static void
on_pcscd_proxy_acquired (GObject *source_object,
GAsyncResult *result,
@@ -69,6 +249,15 @@ on_pcscd_proxy_acquired (GObject *source_object,
}
smartcard->pcscd_proxy = pcscd_proxy;
+
+ smartcard->name_owner_changed_id =
+ g_signal_connect_swapped (pcscd_proxy, "notify::g-name-owner",
+ G_CALLBACK (on_grd_pcscd_name_owner_changed),
+ smartcard);
+
+ name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (pcscd_proxy));
+ if (name_owner)
+ connect_to_grd_pcscd (smartcard);
}
static void
@@ -93,6 +282,11 @@ teardown_private_proxy (GrdRdpSmartcard *smartcard)
g_cancellable_cancel (smartcard->pcscd_proxy_cancellable);
g_clear_object (&smartcard->pcscd_proxy_cancellable);
+ g_clear_signal_handler (&smartcard->name_owner_changed_id,
+ smartcard->pcscd_proxy);
+
+ disconnect_from_grd_pcscd (smartcard);
+
g_clear_object (&smartcard->pcscd_proxy);
}
diff --git a/src/meson.build b/src/meson.build
index 1f1acda1..b6102a9a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -440,6 +440,21 @@ if have_rdp
include_directories: [configinc],
install: true,
install_dir: libexecdir)
+
+ executable('grd-pcscd',
+ 'grd-pcscd.c',
+ 'grd-pcscd-session.c',
+ 'grd-pcscd-session.h',
+ 'grd-daemon-utils.c',
+ 'grd-daemon-utils.h',
+ dbus_gen_pcscd,
+ dependencies: [glib_dep,
+ gio_dep,
+ gio_unix_dep,
+ libsystemd_dep],
+ include_directories: [configinc],
+ install: true,
+ install_dir: libexecdir)
endif
custom_target('gsettings-enums',
diff --git a/src/org.gnome.RemoteDesktop.Pcscd.xml b/src/org.gnome.RemoteDesktop.Pcscd.xml
index 34c25302..34e98a9c 100644
--- a/src/org.gnome.RemoteDesktop.Pcscd.xml
+++ b/src/org.gnome.RemoteDesktop.Pcscd.xml
@@ -14,6 +14,46 @@
-->
<interface name="org.gnome.RemoteDesktop.Pcscd">
+ <!--
+ Connect:
+
+ Register a new smartcard redirection session. The fd is one end of a
+ Unix socketpair. grd-pcscd uses this fd to create a private peer-to-peer
+ D-Bus connection with the calling gnome-remote-desktop-daemon, over
+ which the org.gnome.RemoteDesktop.Pcscd.Session interface is exposed
+ for forwarding PC/SC calls to the remote smartcard.
+ -->
+ <method name="Connect">
+ <annotation name="org.gtk.GDBus.C.UnixFD" value="true"/>
+ <arg name="fd" direction="in" type="h"/>
+ </method>
+
+ </interface>
+
+ <!--
+ org.gnome.RemoteDesktop.Pcscd.Session:
+ @short_description: PC/SC session interface for smartcard operations
+
+ This interface facilitates remote smartcard operations and is utilized in
+ two distinct contexts:
+
+ 1. Exposed on the system bus by `grd-pcscd` (at the object path
+ `/org/gnome/RemoteDesktop/Pcscd/{session_id}`). This allows local
+ clients to communicate with the smartcard.
+
+ 2. Exposed by the `gnome-remote-desktop-daemon` over a private D-Bus
+ connection with `grd-pcscd`.
+
+ The `grd-pcscd` daemon acts as a proxy. It handles method calls on the
+ system bus for a specific `session_id`, and forwards that data over the
+ private D-Bus connection to the `gnome-remote-desktop-daemon` running
+ within that session.
+
+ The methods provided by this interface mirror the standard `winscard.h` API.
+ No compatibility between versions is promised.
+ -->
+ <interface name="org.gnome.RemoteDesktop.Pcscd.Session">
+
</interface>
</node>
--
2.51.0
From 50fb2998a20e63e73b1533836091eb89cdf27807 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 8 Jul 2026 02:02:01 +0200
Subject: [PATCH 05/11] rdp-smartcard: Add PC/SC commands and libgrdpcsc
library
Populate the Pcscd.Session D-Bus interface with all winscard.h PC/SC
methods: EstablishContext, ReleaseContext, Connect, Disconnect,
Transmit, Status, GetStatusChange, ListReaders, and others.
Add GrdPcscdSession handlers that export the Pcscd.Session interface
on the system bus at /org/gnome/RemoteDesktop/Pcscd/{session_id} and
forward incoming calls over the private D-Bus connection to
GrdRdpSmartcard. GrdRdpSmartcard handles them by dispatching through
FreeRDP's RDPDR smartcard callbacks to the remote RDP client, and
returns the results back.
Add libgrdpcsc (libgrdpcsc.so.0), a drop-in replacement for
libpcsclite that redirects PC/SC calls to grd-pcscd instead of the
local pcscd socket. It determines which session to target using
GDM_AUTH_SESSION_ID (set by GDM during PAM authentication) or
XDG_SESSION_ID (for applications inside the remote session), and
connects to the corresponding Pcscd.Session object on the system bus.
This library is intended to be configured as the provider_library in
opensc.conf so that clients like sssd or OpenSC transparently use the
redirected smartcard.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/merge_requests/406>
---
meson.build | 2 +-
src/grd-pcsc-lib-backend-grd-pcscd-proxy.c | 920 ++++++++++++
src/grd-pcsc-lib-backend.c | 57 +
src/grd-pcsc-lib-backend.h | 137 ++
src/grd-pcsc-lib.c | 297 ++++
src/grd-pcscd-session.c | 917 +++++++++++-
src/grd-pcscd-session.h | 7 +-
src/grd-pcscd.c | 13 +-
src/grd-rdp-smartcard.c | 1565 +++++++++++++++++++-
src/meson.build | 17 +
src/org.gnome.RemoteDesktop.Pcscd.xml | 204 +++
11 files changed, 4122 insertions(+), 14 deletions(-)
create mode 100644 src/grd-pcsc-lib-backend-grd-pcscd-proxy.c
create mode 100644 src/grd-pcsc-lib-backend.c
create mode 100644 src/grd-pcsc-lib-backend.h
create mode 100644 src/grd-pcsc-lib.c
diff --git a/meson.build b/meson.build
index 2419627..5e91d25 100644
--- a/meson.build
+++ b/meson.build
@@ -6,7 +6,7 @@ project('gnome-remote-desktop', 'c',
cuda_req = '>= 11.1.5.0'
epoxy_req = '>= 1.4'
-freerdp_req = '>= 3.10.0'
+freerdp_req = '>= 3.28.0'
fuse_req = '>= 3.9.1'
polkit_req = '>= 122'
vulkan_req = '>= 1.2.0'
diff --git a/src/grd-pcsc-lib-backend-grd-pcscd-proxy.c b/src/grd-pcsc-lib-backend-grd-pcscd-proxy.c
new file mode 100644
index 0000000..7c8b144
--- /dev/null
+++ b/src/grd-pcsc-lib-backend-grd-pcscd-proxy.c
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2026 Red Hat Inc.
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ * Joan Torres Lopez <joantolo@redhat.com>
+ */
+
+#include "config.h"
+
+#include "grd-pcsc-lib-backend.h"
+
+#include <stdint.h>
+#include <systemd/sd-login.h>
+
+#include "grd-dbus-pcscd.h"
+
+#define PCSCD_BUS_NAME "org.gnome.RemoteDesktop.Pcscd"
+
+static GrdDBusPcscdSession *session_proxy;
+
+static LONG
+grd_pcscd_proxy_establish_context (DWORD dwScope,
+ LPCVOID pvReserved1,
+ LPCVOID pvReserved2,
+ LPSCARDCONTEXT phContext)
+{
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ int64_t context = 0;
+ GVariant *call;
+
+ call = g_variant_new ("(t)", (uint64_t) dwScope);
+
+ if (!grd_dbus_pcscd_session_call_establish_context_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] EstablishContext D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(xx)",
+ &return_value,
+ &context);
+
+ if (phContext)
+ *phContext = context;
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_release_context (SCARDCONTEXT hContext)
+{
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ GVariant *call;
+
+ call = g_variant_new ("(x)", (int64_t) hContext);
+
+ if (!grd_dbus_pcscd_session_call_release_context_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] ReleaseContext D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(x)", &return_value);
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_is_valid_context (SCARDCONTEXT hContext)
+{
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ GVariant *call;
+
+ call = g_variant_new ("(x)", (int64_t) hContext);
+
+ if (!grd_dbus_pcscd_session_call_is_valid_context_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] IsValidContext D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(x)", &return_value);
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_connect (SCARDCONTEXT hContext,
+ LPCSTR szReader,
+ DWORD dwShareMode,
+ DWORD dwPreferredProtocols,
+ LPSCARDHANDLE phCard,
+ LPDWORD pdwActiveProtocol)
+{
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ int64_t out_context = 0;
+ int64_t out_card = 0;
+ uint64_t active_protocol = 0;
+ GVariant *call;
+
+ call = g_variant_new ("(xstt)",
+ (int64_t) hContext,
+ szReader,
+ (uint64_t) dwShareMode,
+ (uint64_t) dwPreferredProtocols);
+
+ if (!grd_dbus_pcscd_session_call_connect_card_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] Connect D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(xxxt)",
+ &return_value,
+ &out_context,
+ &out_card,
+ &active_protocol);
+
+ if (phCard)
+ *phCard = out_card;
+ if (pdwActiveProtocol)
+ *pdwActiveProtocol = active_protocol;
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_reconnect (SCARDHANDLE hCard,
+ DWORD dwShareMode,
+ DWORD dwPreferredProtocols,
+ DWORD dwInitialization,
+ LPDWORD pdwActiveProtocol)
+{
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ uint64_t active_protocol = 0;
+ GVariant *call;
+
+ call = g_variant_new ("(xttt)",
+ (int64_t) hCard,
+ (uint64_t) dwShareMode,
+ (uint64_t) dwPreferredProtocols,
+ (uint64_t) dwInitialization);
+
+ if (!grd_dbus_pcscd_session_call_reconnect_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] Reconnect D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(xt)",
+ &return_value,
+ &active_protocol);
+
+ if (pdwActiveProtocol)
+ *pdwActiveProtocol = active_protocol;
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_disconnect (SCARDHANDLE hCard,
+ DWORD dwDisposition)
+{
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ GVariant *call;
+
+ call = g_variant_new ("(xt)",
+ (int64_t) hCard,
+ (uint64_t) dwDisposition);
+
+ if (!grd_dbus_pcscd_session_call_disconnect_card_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] Disconnect D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(x)", &return_value);
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_begin_transaction (SCARDHANDLE hCard)
+{
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ GVariant *call;
+
+ call = g_variant_new ("(x)", (int64_t) hCard);
+
+ if (!grd_dbus_pcscd_session_call_begin_transaction_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] BeginTransaction D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(x)", &return_value);
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_end_transaction (SCARDHANDLE hCard,
+ DWORD dwDisposition)
+{
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ GVariant *call;
+
+ call = g_variant_new ("(xt)",
+ (int64_t) hCard,
+ (uint64_t) dwDisposition);
+
+ if (!grd_dbus_pcscd_session_call_end_transaction_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] EndTransaction D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(x)", &return_value);
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_status (SCARDHANDLE hCard,
+ LPSTR mszReaderName,
+ LPDWORD pcchReaderLen,
+ LPDWORD pdwState,
+ LPDWORD pdwProtocol,
+ LPBYTE pbAtr,
+ LPDWORD pcbAtrLen)
+{
+ g_autoptr (GVariant) atr_variant = NULL;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ const char *reader_name = NULL;
+ int64_t return_value = 0;
+ uint64_t state = 0;
+ uint64_t protocol = 0;
+ const uint8_t *atr_data;
+ size_t atr_size;
+ size_t reader_len;
+ GVariant *call;
+
+ call = g_variant_new ("(xtt)",
+ (int64_t) hCard,
+ (uint64_t) (pcchReaderLen ? *pcchReaderLen : 0),
+ (uint64_t) (pcbAtrLen ? *pcbAtrLen : 0));
+
+ if (!grd_dbus_pcscd_session_call_status_card_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] Status D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(x&stt@ay)",
+ &return_value,
+ &reader_name,
+ &state,
+ &protocol,
+ &atr_variant);
+
+ if (pdwState)
+ *pdwState = state;
+ if (pdwProtocol)
+ *pdwProtocol = protocol;
+
+ reader_len = strlen (reader_name) + 1;
+ atr_data = g_variant_get_fixed_array (atr_variant, &atr_size,
+ sizeof (uint8_t));
+
+ if (pcchReaderLen)
+ {
+ if (mszReaderName)
+ {
+ if (reader_len > *pcchReaderLen)
+ return_value = SCARD_E_INSUFFICIENT_BUFFER;
+
+ strncpy (mszReaderName, reader_name, *pcchReaderLen);
+ }
+ *pcchReaderLen = reader_len;
+ }
+
+ if (pcbAtrLen)
+ {
+ if (pbAtr)
+ {
+ if (atr_size > *pcbAtrLen)
+ return_value = SCARD_E_INSUFFICIENT_BUFFER;
+
+ memcpy (pbAtr, atr_data, MIN (atr_size, *pcbAtrLen));
+ }
+ *pcbAtrLen = atr_size;
+ }
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_get_status_change (SCARDCONTEXT hContext,
+ DWORD dwTimeout,
+ SCARD_READERSTATE *rgReaderStates,
+ DWORD cReaders)
+{
+ g_autoptr (GVariant) reader_states_out = NULL;
+ g_autoptr (GVariant) ret = NULL;
+ GVariantBuilder *builder;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ uint64_t atr_event_state;
+ uint64_t atr_length;
+ GVariant *atr_variant;
+ GVariantIter iter;
+ unsigned int i;
+ GVariant *call;
+
+ builder = g_variant_builder_new (G_VARIANT_TYPE ("a(st)"));
+ for (i = 0; i < cReaders; i++)
+ {
+ g_variant_builder_add (builder, "(st)",
+ rgReaderStates[i].szReader,
+ rgReaderStates[i].dwCurrentState);
+ }
+
+ call = g_variant_new ("(xt@a(st))",
+ (int64_t) hContext,
+ (uint64_t) dwTimeout,
+ g_variant_builder_end (builder));
+
+ if (!grd_dbus_pcscd_session_call_get_status_change_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] GetStatusChange D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(x@a(ttay))",
+ &return_value,
+ &reader_states_out);
+
+ i = 0;
+ g_variant_iter_init (&iter, reader_states_out);
+ while (g_variant_iter_loop (&iter, "(tt@ay)",
+ &atr_event_state,
+ &atr_length,
+ &atr_variant))
+ {
+ if (i < cReaders)
+ {
+ const unsigned char *atr_data;
+ size_t atr_size;
+
+ rgReaderStates[i].dwEventState = atr_event_state;
+ rgReaderStates[i].cbAtr = atr_length;
+
+ atr_data = g_variant_get_fixed_array (atr_variant,
+ &atr_size,
+ sizeof (unsigned char));
+ if (atr_size > MAX_ATR_SIZE)
+ atr_size = MAX_ATR_SIZE;
+ memcpy (rgReaderStates[i].rgbAtr, atr_data, atr_size);
+ }
+ i++;
+ }
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_control (SCARDHANDLE hCard,
+ DWORD dwControlCode,
+ LPCVOID pbSendBuffer,
+ DWORD cbSendLength,
+ LPVOID pbRecvBuffer,
+ DWORD cbRecvLength,
+ LPDWORD lpBytesReturned)
+{
+ g_autoptr (GVariant) recv_variant = NULL;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ const uint8_t *recv_data;
+ size_t recv_size;
+ GVariant *call;
+
+ call = g_variant_new ("(xt@aytt)",
+ (int64_t) hCard,
+ (uint64_t) dwControlCode,
+ g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+ pbSendBuffer,
+ cbSendLength,
+ sizeof (uint8_t)),
+ (uint64_t) cbSendLength,
+ (uint64_t) cbRecvLength);
+
+ if (!grd_dbus_pcscd_session_call_control_card_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] Control D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(x@ay)",
+ &return_value,
+ &recv_variant);
+
+ recv_data = g_variant_get_fixed_array (recv_variant, &recv_size,
+ sizeof (uint8_t));
+ if (recv_size > cbRecvLength)
+ return_value = SCARD_E_INSUFFICIENT_BUFFER;
+
+ if (pbRecvBuffer)
+ memcpy (pbRecvBuffer, recv_data, MIN (recv_size, cbRecvLength));
+ if (lpBytesReturned)
+ *lpBytesReturned = recv_size;
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_transmit (SCARDHANDLE hCard,
+ const SCARD_IO_REQUEST *pioSendPci,
+ LPCBYTE pbSendBuffer,
+ DWORD cbSendLength,
+ SCARD_IO_REQUEST *pioRecvPci,
+ LPBYTE pbRecvBuffer,
+ LPDWORD pcbRecvLength)
+{
+ g_autoptr (GVariant) recv_variant = NULL;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ uint64_t recv_pci_protocol = 0;
+ const uint8_t *recv_data;
+ size_t recv_size;
+ size_t copy_size;
+ GVariant *call;
+
+ call = g_variant_new ("(xt@aytt)",
+ (int64_t) hCard,
+ (uint64_t) (pioSendPci ? pioSendPci->dwProtocol : 0),
+ g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+ pbSendBuffer,
+ cbSendLength,
+ sizeof (uint8_t)),
+ (uint64_t) (pioRecvPci ? pioRecvPci->dwProtocol : 0),
+ (uint64_t) (pcbRecvLength ? *pcbRecvLength : 0));
+
+ if (!grd_dbus_pcscd_session_call_transmit_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] Transmit D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(xt@ay)",
+ &return_value,
+ &recv_pci_protocol,
+ &recv_variant);
+
+ recv_data = g_variant_get_fixed_array (recv_variant, &recv_size,
+ sizeof (uint8_t));
+ copy_size = recv_size;
+ if (pcbRecvLength && recv_size > *pcbRecvLength)
+ {
+ copy_size = *pcbRecvLength;
+ return_value = SCARD_E_INSUFFICIENT_BUFFER;
+ }
+
+ if (pbRecvBuffer)
+ memcpy (pbRecvBuffer, recv_data, copy_size);
+ if (pcbRecvLength)
+ *pcbRecvLength = recv_size;
+
+ if (pioRecvPci)
+ {
+ pioRecvPci->dwProtocol = recv_pci_protocol;
+ pioRecvPci->cbPciLength = sizeof (SCARD_IO_REQUEST);
+ }
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_list_reader_groups (SCARDCONTEXT hContext,
+ LPSTR mszGroups,
+ LPDWORD pcchGroups)
+{
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ g_auto (GStrv) groups = NULL;
+ int64_t return_value = 0;
+ size_t groups_size = 0;
+ unsigned int i;
+ GVariant *call;
+
+ call = g_variant_new ("(x)", (int64_t) hContext);
+
+ if (!grd_dbus_pcscd_session_call_list_reader_groups_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] ListReaderGroups D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(x^as)",
+ &return_value,
+ &groups);
+
+ if (return_value != SCARD_S_SUCCESS)
+ return return_value;
+
+ for (i = 0; groups[i]; i++)
+ groups_size += strlen (groups[i]) + 1;
+ groups_size++;
+
+ if (!mszGroups)
+ {
+ if (pcchGroups)
+ *pcchGroups = groups_size;
+ return SCARD_S_SUCCESS;
+ }
+
+ if (pcchGroups && *pcchGroups < groups_size)
+ {
+ *pcchGroups = groups_size;
+ return SCARD_E_INSUFFICIENT_BUFFER;
+ }
+
+ if (pcchGroups)
+ *pcchGroups = groups_size;
+ for (i = 0; groups[i]; i++)
+ {
+ size_t len = strlen (groups[i]) + 1;
+ memcpy (mszGroups, groups[i], len);
+ mszGroups += len;
+ }
+ *mszGroups = '\0';
+
+ return SCARD_S_SUCCESS;
+}
+
+static LONG
+grd_pcscd_proxy_list_readers (SCARDCONTEXT hContext,
+ LPCSTR mszGroups,
+ LPSTR mszReaders,
+ LPDWORD pcchReaders)
+{
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ g_auto (GStrv) readers = NULL;
+ int64_t return_value = 0;
+ size_t readers_size = 0;
+ unsigned int i;
+ GVariant *call;
+
+ call = g_variant_new ("(x)", (int64_t) hContext);
+
+ if (!grd_dbus_pcscd_session_call_list_readers_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] ListReaders D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(x^as)",
+ &return_value,
+ &readers);
+
+ if (return_value != SCARD_S_SUCCESS)
+ return return_value;
+
+ for (i = 0; readers[i]; i++)
+ readers_size += strlen (readers[i]) + 1;
+ readers_size++;
+
+ if (!mszReaders)
+ {
+ if (pcchReaders)
+ *pcchReaders = readers_size;
+ return SCARD_S_SUCCESS;
+ }
+
+ if (pcchReaders && *pcchReaders < readers_size)
+ {
+ *pcchReaders = readers_size;
+ return SCARD_E_INSUFFICIENT_BUFFER;
+ }
+
+ if (pcchReaders)
+ *pcchReaders = readers_size;
+ for (i = 0; readers[i]; i++)
+ {
+ size_t len = strlen (readers[i]) + 1;
+ memcpy (mszReaders, readers[i], len);
+ mszReaders += len;
+ }
+ *mszReaders = '\0';
+
+ return SCARD_S_SUCCESS;
+}
+
+static LONG
+grd_pcscd_proxy_free_memory (SCARDCONTEXT hContext,
+ LPCVOID pvMem)
+{
+ return SCARD_S_SUCCESS;
+}
+
+static LONG
+grd_pcscd_proxy_cancel (SCARDCONTEXT hContext)
+{
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ GVariant *call;
+
+ call = g_variant_new ("(x)", (int64_t) hContext);
+
+ if (!grd_dbus_pcscd_session_call_cancel_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] Cancel D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(x)", &return_value);
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_get_attrib (SCARDHANDLE hCard,
+ DWORD dwAttrId,
+ LPBYTE pbAttr,
+ LPDWORD pcbAttrLen)
+{
+ g_autoptr (GVariant) attr_variant = NULL;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ const uint8_t *attr_data;
+ size_t attr_size;
+ size_t copy_size;
+ GVariant *call;
+
+ call = g_variant_new ("(xtt)",
+ (int64_t) hCard,
+ (uint64_t) dwAttrId,
+ (uint64_t) (pcbAttrLen ? *pcbAttrLen : 0));
+
+ if (!grd_dbus_pcscd_session_call_get_attrib_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] GetAttrib D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(x@ay)",
+ &return_value,
+ &attr_variant);
+
+ attr_data = g_variant_get_fixed_array (attr_variant, &attr_size,
+ sizeof (uint8_t));
+
+ if (!pbAttr)
+ {
+ if (pcbAttrLen)
+ *pcbAttrLen = attr_size;
+ return return_value;
+ }
+
+ copy_size = attr_size;
+ if (pcbAttrLen && attr_size > *pcbAttrLen)
+ {
+ copy_size = *pcbAttrLen;
+ return_value = SCARD_E_INSUFFICIENT_BUFFER;
+ }
+
+ memcpy (pbAttr, attr_data, copy_size);
+ if (pcbAttrLen)
+ *pcbAttrLen = attr_size;
+
+ return return_value;
+}
+
+static LONG
+grd_pcscd_proxy_set_attrib (SCARDHANDLE hCard,
+ DWORD dwAttrId,
+ LPCBYTE pbAttr,
+ DWORD cbAttrLen)
+{
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+ int64_t return_value = 0;
+ GVariant *call;
+
+ call = g_variant_new ("(xt@ay)",
+ (int64_t) hCard,
+ (uint64_t) dwAttrId,
+ g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+ pbAttr,
+ cbAttrLen,
+ sizeof (uint8_t)));
+
+ if (!grd_dbus_pcscd_session_call_set_attrib_sync (session_proxy,
+ call,
+ &ret,
+ NULL,
+ &error))
+ {
+ g_warning ("[PCSC] SetAttrib D-Bus call failed: %s",
+ error->message);
+ return SCARD_F_INTERNAL_ERROR;
+ }
+
+ g_variant_get (ret, "(x)", &return_value);
+
+ return return_value;
+}
+
+static char *
+get_session_id (void)
+{
+ const char *session_id;
+ char *logind_session_id = NULL;
+
+ session_id = g_getenv ("GDM_AUTH_SESSION_ID");
+ if (session_id && g_strcmp0 (session_id, "") != 0)
+ return g_strdup (session_id);
+
+ if (sd_pid_get_session (0, &logind_session_id) < 0)
+ return NULL;
+
+ return logind_session_id;
+}
+
+static gboolean
+dbus_interface_exists (GDBusProxy *proxy)
+{
+ g_autoptr (GVariant) result = NULL;
+
+ result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (proxy),
+ g_dbus_proxy_get_name (proxy),
+ g_dbus_proxy_get_object_path (proxy),
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ g_variant_new ("(s)",
+ g_dbus_proxy_get_interface_name (proxy)),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ NULL);
+ return result != NULL;
+}
+
+gboolean
+grd_pcsc_lib_backend_init_grd_pcscd_proxy (GrdPcscLibBackend *backend)
+{
+ g_autofree char *session_id = NULL;
+ g_autofree char *object_path = NULL;
+ g_autofree char *name_owner = NULL;
+ g_autoptr (GError) error = NULL;
+
+ session_id = get_session_id ();
+ if (!session_id)
+ return FALSE;
+
+ object_path = g_strdup_printf ("/org/gnome/RemoteDesktop/Pcscd/%s", session_id);
+ session_proxy = grd_dbus_pcscd_session_proxy_new_for_bus_sync (
+ G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ PCSCD_BUS_NAME,
+ object_path,
+ NULL,
+ &error);
+ if (!session_proxy)
+ return FALSE;
+
+ name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (session_proxy));
+ if (!name_owner)
+ return FALSE;
+
+ if (!dbus_interface_exists (G_DBUS_PROXY (session_proxy)))
+ {
+ g_clear_object (&session_proxy);
+ return FALSE;
+ }
+
+ backend->SCardEstablishContext = grd_pcscd_proxy_establish_context;
+ backend->SCardReleaseContext = grd_pcscd_proxy_release_context;
+ backend->SCardIsValidContext = grd_pcscd_proxy_is_valid_context;
+ backend->SCardConnect = grd_pcscd_proxy_connect;
+ backend->SCardReconnect = grd_pcscd_proxy_reconnect;
+ backend->SCardDisconnect = grd_pcscd_proxy_disconnect;
+ backend->SCardBeginTransaction = grd_pcscd_proxy_begin_transaction;
+ backend->SCardEndTransaction = grd_pcscd_proxy_end_transaction;
+ backend->SCardStatus = grd_pcscd_proxy_status;
+ backend->SCardGetStatusChange = grd_pcscd_proxy_get_status_change;
+ backend->SCardControl = grd_pcscd_proxy_control;
+ backend->SCardTransmit = grd_pcscd_proxy_transmit;
+ backend->SCardListReaderGroups = grd_pcscd_proxy_list_reader_groups;
+ backend->SCardListReaders = grd_pcscd_proxy_list_readers;
+ backend->SCardFreeMemory = grd_pcscd_proxy_free_memory;
+ backend->SCardCancel = grd_pcscd_proxy_cancel;
+ backend->SCardGetAttrib = grd_pcscd_proxy_get_attrib;
+ backend->SCardSetAttrib = grd_pcscd_proxy_set_attrib;
+
+ return TRUE;
+}
+
+void
+grd_pcsc_lib_backend_cleanup_grd_pcscd_proxy (void)
+{
+ g_clear_object (&session_proxy);
+}
diff --git a/src/grd-pcsc-lib-backend.c b/src/grd-pcsc-lib-backend.c
new file mode 100644
index 0000000..16d18a4
--- /dev/null
+++ b/src/grd-pcsc-lib-backend.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2026 Red Hat Inc.
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ * Joan Torres Lopez <joantolo@redhat.com>
+ */
+
+#include "config.h"
+
+#include "grd-pcsc-lib-backend.h"
+
+#define SET_DEFAULT(backend, member, func) \
+ (backend)->member = (typeof ((backend)->member)) (func)
+
+static LONG
+internal_error (void)
+{
+ return SCARD_F_INTERNAL_ERROR;
+}
+
+void
+grd_pcsc_lib_backend_set_default (GrdPcscLibBackend *backend)
+{
+ SET_DEFAULT (backend, SCardEstablishContext, internal_error);
+ SET_DEFAULT (backend, SCardReleaseContext, internal_error);
+ SET_DEFAULT (backend, SCardIsValidContext, internal_error);
+ SET_DEFAULT (backend, SCardConnect, internal_error);
+ SET_DEFAULT (backend, SCardReconnect, internal_error);
+ SET_DEFAULT (backend, SCardDisconnect, internal_error);
+ SET_DEFAULT (backend, SCardBeginTransaction, internal_error);
+ SET_DEFAULT (backend, SCardEndTransaction, internal_error);
+ SET_DEFAULT (backend, SCardStatus, internal_error);
+ SET_DEFAULT (backend, SCardGetStatusChange, internal_error);
+ SET_DEFAULT (backend, SCardControl, internal_error);
+ SET_DEFAULT (backend, SCardTransmit, internal_error);
+ SET_DEFAULT (backend, SCardListReaderGroups, internal_error);
+ SET_DEFAULT (backend, SCardListReaders, internal_error);
+ SET_DEFAULT (backend, SCardFreeMemory, internal_error);
+ SET_DEFAULT (backend, SCardCancel, internal_error);
+ SET_DEFAULT (backend, SCardGetAttrib, internal_error);
+ SET_DEFAULT (backend, SCardSetAttrib, internal_error);
+}
diff --git a/src/grd-pcsc-lib-backend.h b/src/grd-pcsc-lib-backend.h
new file mode 100644
index 0000000..47c6a13
--- /dev/null
+++ b/src/grd-pcsc-lib-backend.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2026 Red Hat Inc.
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ * Joan Torres Lopez <joantolo@redhat.com>
+ */
+
+#pragma once
+
+#include <glib.h>
+
+typedef unsigned long DWORD;
+typedef DWORD *LPDWORD;
+typedef long LONG;
+typedef LONG SCARDCONTEXT;
+typedef SCARDCONTEXT *LPSCARDCONTEXT;
+typedef LONG SCARDHANDLE;
+typedef SCARDHANDLE *LPSCARDHANDLE;
+typedef const void *LPCVOID;
+typedef void *LPVOID;
+typedef unsigned char BYTE;
+typedef BYTE *LPBYTE;
+typedef const BYTE *LPCBYTE;
+typedef const char *LPCSTR;
+typedef char *LPSTR;
+
+#define MAX_ATR_SIZE 33
+
+typedef struct
+{
+ const char *szReader;
+ void *pvUserData;
+ DWORD dwCurrentState;
+ DWORD dwEventState;
+ DWORD cbAtr;
+ unsigned char rgbAtr[MAX_ATR_SIZE];
+} SCARD_READERSTATE;
+
+typedef struct
+{
+ unsigned long dwProtocol;
+ unsigned long cbPciLength;
+} SCARD_IO_REQUEST;
+
+#define SCARD_S_SUCCESS ((LONG) 0x00000000)
+#define SCARD_F_INTERNAL_ERROR ((LONG) 0x80100001)
+#define SCARD_E_INSUFFICIENT_BUFFER ((LONG) 0x80100008)
+
+typedef struct
+{
+ LONG (*SCardEstablishContext) (DWORD,
+ LPCVOID,
+ LPCVOID,
+ LPSCARDCONTEXT);
+ LONG (*SCardReleaseContext) (SCARDCONTEXT);
+ LONG (*SCardIsValidContext) (SCARDCONTEXT);
+ LONG (*SCardConnect) (SCARDCONTEXT,
+ LPCSTR,
+ DWORD,
+ DWORD,
+ LPSCARDHANDLE,
+ LPDWORD);
+ LONG (*SCardReconnect) (SCARDHANDLE,
+ DWORD,
+ DWORD,
+ DWORD,
+ LPDWORD);
+ LONG (*SCardDisconnect) (SCARDHANDLE,
+ DWORD);
+ LONG (*SCardBeginTransaction) (SCARDHANDLE);
+ LONG (*SCardEndTransaction) (SCARDHANDLE,
+ DWORD);
+ LONG (*SCardStatus) (SCARDHANDLE,
+ LPSTR,
+ LPDWORD,
+ LPDWORD,
+ LPDWORD,
+ LPBYTE,
+ LPDWORD);
+ LONG (*SCardGetStatusChange) (SCARDCONTEXT,
+ DWORD,
+ SCARD_READERSTATE *,
+ DWORD);
+ LONG (*SCardControl) (SCARDHANDLE,
+ DWORD,
+ LPCVOID,
+ DWORD,
+ LPVOID,
+ DWORD,
+ LPDWORD);
+ LONG (*SCardTransmit) (SCARDHANDLE,
+ const SCARD_IO_REQUEST *,
+ LPCBYTE,
+ DWORD,
+ SCARD_IO_REQUEST *,
+ LPBYTE,
+ LPDWORD);
+ LONG (*SCardListReaderGroups) (SCARDCONTEXT,
+ LPSTR,
+ LPDWORD);
+ LONG (*SCardListReaders) (SCARDCONTEXT,
+ LPCSTR,
+ LPSTR,
+ LPDWORD);
+ LONG (*SCardFreeMemory) (SCARDCONTEXT,
+ LPCVOID);
+ LONG (*SCardCancel) (SCARDCONTEXT);
+ LONG (*SCardGetAttrib) (SCARDHANDLE,
+ DWORD,
+ LPBYTE,
+ LPDWORD);
+ LONG (*SCardSetAttrib) (SCARDHANDLE,
+ DWORD,
+ LPCBYTE,
+ DWORD);
+} GrdPcscLibBackend;
+
+void grd_pcsc_lib_backend_set_default (GrdPcscLibBackend *backend);
+
+gboolean grd_pcsc_lib_backend_init_grd_pcscd_proxy (GrdPcscLibBackend *backend);
+
+void grd_pcsc_lib_backend_cleanup_grd_pcscd_proxy (void);
diff --git a/src/grd-pcsc-lib.c b/src/grd-pcsc-lib.c
new file mode 100644
index 0000000..94ae36c
--- /dev/null
+++ b/src/grd-pcsc-lib.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2026 Red Hat Inc.
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ * Joan Torres Lopez <joantolo@redhat.com>
+ */
+
+#include "config.h"
+
+#include "grd-pcsc-lib-backend.h"
+
+#define PCSC_API __attribute__((visibility ("default")))
+#define PCSC_DESTRUCTOR __attribute__ ((destructor))
+
+static GrdPcscLibBackend backend;
+
+PCSC_DESTRUCTOR static void
+on_unload (void)
+{
+ grd_pcsc_lib_backend_cleanup_grd_pcscd_proxy ();
+}
+
+static void
+init_grd_pcsc_backend (void)
+{
+ grd_pcsc_lib_backend_set_default (&backend);
+
+ if (grd_pcsc_lib_backend_init_grd_pcscd_proxy (&backend))
+ return;
+
+ g_warning ("[PCSC] No backend available");
+}
+
+static void
+ensure_initialized (void)
+{
+ static gsize initialized = 0;
+
+ if (g_once_init_enter (&initialized))
+ {
+ init_grd_pcsc_backend ();
+ g_once_init_leave (&initialized, 1);
+ }
+}
+
+PCSC_API LONG
+SCardEstablishContext (DWORD dwScope,
+ LPCVOID pvReserved1,
+ LPCVOID pvReserved2,
+ LPSCARDCONTEXT phContext)
+{
+ ensure_initialized ();
+
+ return backend.SCardEstablishContext (dwScope,
+ pvReserved1,
+ pvReserved2,
+ phContext);
+}
+
+PCSC_API LONG
+SCardReleaseContext (SCARDCONTEXT hContext)
+{
+ ensure_initialized ();
+
+ return backend.SCardReleaseContext (hContext);
+}
+
+PCSC_API LONG
+SCardIsValidContext (SCARDCONTEXT hContext)
+{
+ ensure_initialized ();
+
+ return backend.SCardIsValidContext (hContext);
+}
+
+PCSC_API LONG
+SCardConnect (SCARDCONTEXT hContext,
+ LPCSTR szReader,
+ DWORD dwShareMode,
+ DWORD dwPreferredProtocols,
+ LPSCARDHANDLE phCard,
+ LPDWORD pdwActiveProtocol)
+{
+ ensure_initialized ();
+
+ return backend.SCardConnect (hContext,
+ szReader,
+ dwShareMode,
+ dwPreferredProtocols,
+ phCard,
+ pdwActiveProtocol);
+}
+
+PCSC_API LONG
+SCardReconnect (SCARDHANDLE hCard,
+ DWORD dwShareMode,
+ DWORD dwPreferredProtocols,
+ DWORD dwInitialization,
+ LPDWORD pdwActiveProtocol)
+{
+ ensure_initialized ();
+
+ return backend.SCardReconnect (hCard,
+ dwShareMode,
+ dwPreferredProtocols,
+ dwInitialization,
+ pdwActiveProtocol);
+}
+
+PCSC_API LONG
+SCardDisconnect (SCARDHANDLE hCard,
+ DWORD dwDisposition)
+{
+ ensure_initialized ();
+
+ return backend.SCardDisconnect (hCard,
+ dwDisposition);
+}
+
+PCSC_API LONG
+SCardBeginTransaction (SCARDHANDLE hCard)
+{
+ ensure_initialized ();
+
+ return backend.SCardBeginTransaction (hCard);
+}
+
+PCSC_API LONG
+SCardEndTransaction (SCARDHANDLE hCard,
+ DWORD dwDisposition)
+{
+ ensure_initialized ();
+
+ return backend.SCardEndTransaction (hCard,
+ dwDisposition);
+}
+
+PCSC_API LONG
+SCardStatus (SCARDHANDLE hCard,
+ LPSTR mszReaderName,
+ LPDWORD pcchReaderLen,
+ LPDWORD pdwState,
+ LPDWORD pdwProtocol,
+ LPBYTE pbAtr,
+ LPDWORD pcbAtrLen)
+{
+ ensure_initialized ();
+
+ return backend.SCardStatus (hCard,
+ mszReaderName,
+ pcchReaderLen,
+ pdwState,
+ pdwProtocol,
+ pbAtr,
+ pcbAtrLen);
+}
+
+PCSC_API LONG
+SCardGetStatusChange (SCARDCONTEXT hContext,
+ DWORD dwTimeout,
+ SCARD_READERSTATE *rgReaderStates,
+ DWORD cReaders)
+{
+ ensure_initialized ();
+
+ return backend.SCardGetStatusChange (hContext,
+ dwTimeout,
+ rgReaderStates,
+ cReaders);
+}
+
+PCSC_API LONG
+SCardControl (SCARDHANDLE hCard,
+ DWORD dwControlCode,
+ LPCVOID pbSendBuffer,
+ DWORD cbSendLength,
+ LPVOID pbRecvBuffer,
+ DWORD cbRecvLength,
+ LPDWORD lpBytesReturned)
+{
+ ensure_initialized ();
+
+ return backend.SCardControl (hCard,
+ dwControlCode,
+ pbSendBuffer,
+ cbSendLength,
+ pbRecvBuffer,
+ cbRecvLength,
+ lpBytesReturned);
+}
+
+PCSC_API LONG
+SCardTransmit (SCARDHANDLE hCard,
+ const SCARD_IO_REQUEST *pioSendPci,
+ LPCBYTE pbSendBuffer,
+ DWORD cbSendLength,
+ SCARD_IO_REQUEST *pioRecvPci,
+ LPBYTE pbRecvBuffer,
+ LPDWORD pcbRecvLength)
+{
+ ensure_initialized ();
+
+ return backend.SCardTransmit (hCard,
+ pioSendPci,
+ pbSendBuffer,
+ cbSendLength,
+ pioRecvPci,
+ pbRecvBuffer,
+ pcbRecvLength);
+}
+
+PCSC_API LONG
+SCardListReaderGroups (SCARDCONTEXT hContext,
+ LPSTR mszGroups,
+ LPDWORD pcchGroups)
+{
+ ensure_initialized ();
+
+ return backend.SCardListReaderGroups (hContext,
+ mszGroups,
+ pcchGroups);
+}
+
+PCSC_API LONG
+SCardListReaders (SCARDCONTEXT hContext,
+ LPCSTR mszGroups,
+ LPSTR mszReaders,
+ LPDWORD pcchReaders)
+{
+ ensure_initialized ();
+
+ return backend.SCardListReaders (hContext,
+ mszGroups,
+ mszReaders,
+ pcchReaders);
+}
+
+PCSC_API LONG
+SCardFreeMemory (SCARDCONTEXT hContext,
+ LPCVOID pvMem)
+{
+ ensure_initialized ();
+
+ return backend.SCardFreeMemory (hContext,
+ pvMem);
+}
+
+PCSC_API LONG
+SCardCancel (SCARDCONTEXT hContext)
+{
+ ensure_initialized ();
+
+ return backend.SCardCancel (hContext);
+}
+
+PCSC_API LONG
+SCardGetAttrib (SCARDHANDLE hCard,
+ DWORD dwAttrId,
+ LPBYTE pbAttr,
+ LPDWORD pcbAttrLen)
+{
+ ensure_initialized ();
+
+ return backend.SCardGetAttrib (hCard,
+ dwAttrId,
+ pbAttr,
+ pcbAttrLen);
+}
+
+PCSC_API LONG
+SCardSetAttrib (SCARDHANDLE hCard,
+ DWORD dwAttrId,
+ LPCBYTE pbAttr,
+ DWORD cbAttrLen)
+{
+ ensure_initialized ();
+
+ return backend.SCardSetAttrib (hCard,
+ dwAttrId,
+ pbAttr,
+ cbAttrLen);
+}
diff --git a/src/grd-pcscd-session.c b/src/grd-pcscd-session.c
index 26d76b7..aa45ac2 100644
--- a/src/grd-pcscd-session.c
+++ b/src/grd-pcscd-session.c
@@ -30,6 +30,8 @@
#include "grd-dbus-pcscd.h"
+#define PCSCD_OBJECT_PATH_PREFIX "/org/gnome/RemoteDesktop/Pcscd"
+
struct _GrdPcscdSession
{
GObject parent;
@@ -39,6 +41,7 @@ struct _GrdPcscdSession
GCancellable *cancellable;
GrdDBusPcscdSession *private_proxy;
+ GrdDBusPcscdSession *system_proxy;
};
enum
@@ -52,6 +55,822 @@ static guint signals[N_SIGNALS];
G_DEFINE_TYPE (GrdPcscdSession, grd_pcscd_session, G_TYPE_OBJECT)
+static void
+on_establish_context_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_establish_context_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(xx))",
+ ret));
+}
+
+static gboolean
+on_handle_establish_context (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_establish_context (session->private_proxy,
+ call,
+ session->cancellable,
+ on_establish_context_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_release_context_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_release_context_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(x))",
+ ret));
+}
+
+static gboolean
+on_handle_release_context (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_release_context (session->private_proxy,
+ call,
+ session->cancellable,
+ on_release_context_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_is_valid_context_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_is_valid_context_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(x))",
+ ret));
+}
+
+static gboolean
+on_handle_is_valid_context (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_is_valid_context (session->private_proxy,
+ call,
+ session->cancellable,
+ on_is_valid_context_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_connect_card_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_connect_card_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(xxxt))",
+ ret));
+}
+
+static gboolean
+on_handle_connect_card (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_connect_card (session->private_proxy,
+ call,
+ session->cancellable,
+ on_connect_card_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_reconnect_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_reconnect_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(xt))",
+ ret));
+}
+
+static gboolean
+on_handle_reconnect (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_reconnect (session->private_proxy,
+ call,
+ session->cancellable,
+ on_reconnect_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_disconnect_card_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_disconnect_card_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(x))",
+ ret));
+}
+
+static gboolean
+on_handle_disconnect_card (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_disconnect_card (session->private_proxy,
+ call,
+ session->cancellable,
+ on_disconnect_card_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_begin_transaction_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_begin_transaction_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(x))",
+ ret));
+}
+
+static gboolean
+on_handle_begin_transaction (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_begin_transaction (session->private_proxy,
+ call,
+ session->cancellable,
+ on_begin_transaction_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_end_transaction_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_end_transaction_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(x))",
+ ret));
+}
+
+static gboolean
+on_handle_end_transaction (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_end_transaction (session->private_proxy,
+ call,
+ session->cancellable,
+ on_end_transaction_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_status_card_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_status_card_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(xsttay))",
+ ret));
+}
+
+static gboolean
+on_handle_status_card (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_status_card (session->private_proxy,
+ call,
+ session->cancellable,
+ on_status_card_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_get_status_change_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_get_status_change_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(xa(ttay)))",
+ ret));
+}
+
+static gboolean
+on_handle_get_status_change (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_get_status_change (session->private_proxy,
+ call,
+ session->cancellable,
+ on_get_status_change_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_control_card_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_control_card_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(xay))",
+ ret));
+}
+
+static gboolean
+on_handle_control_card (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_control_card (session->private_proxy,
+ call,
+ session->cancellable,
+ on_control_card_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_transmit_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_transmit_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(xtay))",
+ ret));
+}
+
+static gboolean
+on_handle_transmit (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_transmit (session->private_proxy,
+ call,
+ session->cancellable,
+ on_transmit_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_list_reader_groups_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_list_reader_groups_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(xas))",
+ ret));
+}
+
+static gboolean
+on_handle_list_reader_groups (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_list_reader_groups (session->private_proxy,
+ call,
+ session->cancellable,
+ on_list_reader_groups_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_list_readers_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_list_readers_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(xas))",
+ ret));
+}
+
+static gboolean
+on_handle_list_readers (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_list_readers (session->private_proxy,
+ call,
+ session->cancellable,
+ on_list_readers_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_cancel_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_cancel_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(x))",
+ ret));
+}
+
+static gboolean
+on_handle_cancel (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_cancel (session->private_proxy,
+ call,
+ session->cancellable,
+ on_cancel_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_get_attrib_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_get_attrib_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(xay))",
+ ret));
+}
+
+static gboolean
+on_handle_get_attrib (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_get_attrib (session->private_proxy,
+ call,
+ session->cancellable,
+ on_get_attrib_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_set_attrib_finished (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (GDBusMethodInvocation) invocation = user_data;
+ g_autoptr (GVariant) ret = NULL;
+ g_autoptr (GError) error = NULL;
+
+ if (!grd_dbus_pcscd_session_call_set_attrib_finish (
+ GRD_DBUS_PCSCD_SESSION (source_object),
+ &ret, result, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ return;
+ }
+
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(@(x))",
+ ret));
+}
+
+static gboolean
+on_handle_set_attrib (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ if (!session->private_proxy)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "RDP Smartcard not connected");
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ grd_dbus_pcscd_session_call_set_attrib (session->private_proxy,
+ call,
+ session->cancellable,
+ on_set_attrib_finished,
+ g_object_ref (invocation));
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
static void
on_private_connection_closed (GDBusConnection *connection,
gboolean remote_peer_vanished,
@@ -128,6 +947,84 @@ on_private_connection_ready (GObject *source_object,
session);
}
+static gboolean
+create_system_proxy (GrdPcscdSession *session,
+ GDBusConnection *system_connection,
+ GError **error)
+{
+ g_autoptr (GrdDBusPcscdSession) system_proxy = NULL;
+ g_autofree char *object_path = NULL;
+
+ object_path =
+ g_strdup_printf ("%s/%s", PCSCD_OBJECT_PATH_PREFIX, session->session_id);
+
+ system_proxy = grd_dbus_pcscd_session_skeleton_new ();
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (system_proxy),
+ system_connection,
+ object_path,
+ error))
+ return FALSE;
+
+ g_signal_connect_object (system_proxy, "handle-establish-context",
+ G_CALLBACK (on_handle_establish_context),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-release-context",
+ G_CALLBACK (on_handle_release_context),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-is-valid-context",
+ G_CALLBACK (on_handle_is_valid_context),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-connect-card",
+ G_CALLBACK (on_handle_connect_card),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-reconnect",
+ G_CALLBACK (on_handle_reconnect),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-disconnect-card",
+ G_CALLBACK (on_handle_disconnect_card),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-begin-transaction",
+ G_CALLBACK (on_handle_begin_transaction),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-end-transaction",
+ G_CALLBACK (on_handle_end_transaction),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-status-card",
+ G_CALLBACK (on_handle_status_card),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-get-status-change",
+ G_CALLBACK (on_handle_get_status_change),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-control-card",
+ G_CALLBACK (on_handle_control_card),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-transmit",
+ G_CALLBACK (on_handle_transmit),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-list-reader-groups",
+ G_CALLBACK (on_handle_list_reader_groups),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-list-readers",
+ G_CALLBACK (on_handle_list_readers),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-cancel",
+ G_CALLBACK (on_handle_cancel),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-get-attrib",
+ G_CALLBACK (on_handle_get_attrib),
+ session, G_CONNECT_DEFAULT);
+ g_signal_connect_object (system_proxy, "handle-set-attrib",
+ G_CALLBACK (on_handle_set_attrib),
+ session, G_CONNECT_DEFAULT);
+
+ session->system_proxy = g_steal_pointer (&system_proxy);
+
+ g_debug ("[PCSCD.SESSION %s] Exported on system bus at %s",
+ session->session_id, object_path);
+
+ return TRUE;
+}
+
static void
create_private_proxy (GrdPcscdSession *session,
int fd)
@@ -160,9 +1057,10 @@ grd_pcscd_session_get_session_id (GrdPcscdSession *session)
}
GrdPcscdSession *
-grd_pcscd_session_new (const char *session_id,
- int fd,
- GDBusConnection *system_connection)
+grd_pcscd_session_new (const char *session_id,
+ int fd,
+ GDBusConnection *system_connection,
+ GError **error)
{
g_autoptr (GrdPcscdSession) session = NULL;
@@ -170,6 +1068,12 @@ grd_pcscd_session_new (const char *session_id,
session->session_id = g_strdup (session_id);
session->cancellable = g_cancellable_new ();
+ if (!create_system_proxy (session, system_connection, error))
+ {
+ close (fd);
+ return NULL;
+ }
+
create_private_proxy (session, fd);
return g_steal_pointer (&session);
@@ -194,6 +1098,13 @@ grd_pcscd_session_dispose (GObject *object)
g_clear_object (&session->private_proxy);
}
+ if (session->system_proxy)
+ {
+ g_dbus_interface_skeleton_unexport (
+ G_DBUS_INTERFACE_SKELETON (session->system_proxy));
+ g_clear_object (&session->system_proxy);
+ }
+
g_clear_pointer (&session->session_id, g_free);
G_OBJECT_CLASS (grd_pcscd_session_parent_class)->dispose (object);
diff --git a/src/grd-pcscd-session.h b/src/grd-pcscd-session.h
index bafcb1e..1a0cce2 100644
--- a/src/grd-pcscd-session.h
+++ b/src/grd-pcscd-session.h
@@ -28,8 +28,9 @@
G_DECLARE_FINAL_TYPE (GrdPcscdSession, grd_pcscd_session,
GRD, PCSCD_SESSION, GObject)
-GrdPcscdSession *grd_pcscd_session_new (const char *session_id,
- int fd,
- GDBusConnection *system_connection);
+GrdPcscdSession *grd_pcscd_session_new (const char *session_id,
+ int fd,
+ GDBusConnection *system_connection,
+ GError **error);
const char *grd_pcscd_session_get_session_id (GrdPcscdSession *session);
diff --git a/src/grd-pcscd.c b/src/grd-pcscd.c
index a70ce57..781f290 100644
--- a/src/grd-pcscd.c
+++ b/src/grd-pcscd.c
@@ -148,7 +148,18 @@ on_handle_connect (GrdDBusPcscd *skeleton,
session = grd_pcscd_session_new (session_id,
g_steal_fd (&fd),
- pcscd->connection);
+ pcscd->connection,
+ &error);
+ if (!session)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "Failed to create session %s: %s",
+ session_id,
+ error->message);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
g_signal_connect (session, "closed",
G_CALLBACK (on_session_closed),
diff --git a/src/grd-rdp-smartcard.c b/src/grd-rdp-smartcard.c
index a674fc0..746a7ab 100644
--- a/src/grd-rdp-smartcard.c
+++ b/src/grd-rdp-smartcard.c
@@ -24,6 +24,7 @@
#include "grd-rdp-smartcard.h"
+#include <freerdp/utils/smartcard_pack.h>
#include <gio/gunixinputstream.h>
#include <gio/gunixoutputstream.h>
#include <glib/gstdio.h>
@@ -34,6 +35,37 @@
#include "grd-rdp-device-redirection.h"
#include "grd-session-rdp.h"
+#define EXTEND_32_TO_64(val) \
+ (G_STATIC_ASSERT_EXPR (sizeof (val) == sizeof (uint32_t)), \
+ (uint64_t) (uint32_t) (val))
+
+#define NARROW_64_TO_32(val) \
+ (G_STATIC_ASSERT_EXPR (sizeof (val) == sizeof (uint64_t)), \
+ ((uint64_t) (val) > UINT32_MAX \
+ ? (g_warning ("Narrowing 64-to-32 lost data in '"#val"': 0x%lx", \
+ (uint64_t) (val)), \
+ (uint32_t) (val)) \
+ : (uint32_t) (val)))
+
+/*
+ * pcsc-lite dwState bitmask values. These differ from the WinSCard
+ * sequential values (0-6) defined by FreeRDP/WinPR in winpr/smartcard.h.
+ */
+#define PCSC_SCARD_UNKNOWN 0x0001
+#define PCSC_SCARD_ABSENT 0x0002
+#define PCSC_SCARD_PRESENT 0x0004
+#define PCSC_SCARD_SWALLOWED 0x0008
+#define PCSC_SCARD_POWERED 0x0010
+#define PCSC_SCARD_NEGOTIABLE 0x0020
+
+/*
+ * pcsc-lite and WinSCard use different values for SCARD_PROTOCOL_RAW.
+ * SCARD_PROTOCOL_T15 is not in WinSCard.
+ * T=0 (0x01) and T=1 (0x02) are the same in both.
+ */
+#define PCSC_SCARD_PROTOCOL_RAW 0x0004
+#define PCSC_SCARD_PROTOCOL_T15 0x0008
+
typedef struct
{
GrdRdpSmartcard *smartcard;
@@ -49,6 +81,7 @@ struct _GrdRdpSmartcard
RdpdrServerContext *rdpdr_context;
GHashTable *smartcard_device_ids;
+ GHashTable *pending_invocations;
/* System bus proxy to grd-pcscd */
GCancellable *pcscd_proxy_cancellable;
@@ -62,6 +95,1454 @@ struct _GrdRdpSmartcard
G_DEFINE_TYPE (GrdRdpSmartcard, grd_rdp_smartcard, G_TYPE_OBJECT)
+static uint32_t
+convert_card_state_to_pcsc (uint32_t card_reader_state)
+{
+ switch (card_reader_state)
+ {
+ case SCARD_UNKNOWN:
+ return PCSC_SCARD_UNKNOWN;
+ case SCARD_ABSENT:
+ return PCSC_SCARD_ABSENT;
+ case SCARD_PRESENT:
+ return PCSC_SCARD_PRESENT;
+ case SCARD_SWALLOWED:
+ return PCSC_SCARD_PRESENT | PCSC_SCARD_SWALLOWED;
+ case SCARD_POWERED:
+ return PCSC_SCARD_PRESENT | PCSC_SCARD_POWERED;
+ case SCARD_NEGOTIABLE:
+ case SCARD_SPECIFIC:
+ return PCSC_SCARD_PRESENT | PCSC_SCARD_POWERED | PCSC_SCARD_NEGOTIABLE;
+ default:
+ g_warning ("[RDP.SMARTCARD] Unknown CardReaderState: %u", card_reader_state);
+ return PCSC_SCARD_UNKNOWN;
+ }
+}
+
+static uint32_t
+convert_protocol_to_pcsc (uint32_t dwProtocols)
+{
+ if (dwProtocols & SCARD_PROTOCOL_RAW)
+ {
+ dwProtocols &= ~SCARD_PROTOCOL_RAW;
+ dwProtocols |= PCSC_SCARD_PROTOCOL_RAW;
+ }
+
+ if (dwProtocols & SCARD_PROTOCOL_DEFAULT)
+ {
+ dwProtocols &= ~SCARD_PROTOCOL_DEFAULT;
+ }
+
+ if (dwProtocols == SCARD_PROTOCOL_UNDEFINED)
+ {
+ dwProtocols = SCARD_PROTOCOL_Tx;
+ }
+
+ return dwProtocols;
+}
+
+static uint32_t
+convert_protocol_to_winscard (uint32_t dwProtocols)
+{
+ if (dwProtocols & PCSC_SCARD_PROTOCOL_RAW)
+ {
+ dwProtocols &= ~PCSC_SCARD_PROTOCOL_RAW;
+ dwProtocols |= SCARD_PROTOCOL_RAW;
+ }
+
+ if (dwProtocols & PCSC_SCARD_PROTOCOL_T15)
+ {
+ dwProtocols &= ~PCSC_SCARD_PROTOCOL_T15;
+ }
+
+ return dwProtocols;
+}
+
+static gboolean
+is_device_unavailable (GrdRdpSmartcard *smartcard,
+ GDBusMethodInvocation *invocation)
+{
+ g_mutex_lock (&smartcard->shutdown_mutex);
+ if (!smartcard->in_shutdown &&
+ g_hash_table_size (smartcard->smartcard_device_ids) > 0)
+ {
+ g_mutex_unlock (&smartcard->shutdown_mutex);
+ return FALSE;
+ }
+ g_mutex_unlock (&smartcard->shutdown_mutex);
+
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "Smartcard device not available");
+ return TRUE;
+}
+
+static void
+remove_pending_invocation (RdpdrServerContext *rdpdr_context,
+ GDBusMethodInvocation *invocation)
+{
+ GrdRdpDeviceRedirection *device_redirection = rdpdr_context->data;
+ GrdRdpSmartcard *smartcard;
+
+ smartcard = grd_rdp_device_redirection_get_smartcard (device_redirection);
+ if (!smartcard)
+ return;
+
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+}
+
+static void
+on_smartcard_establish_context_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code,
+ const EstablishContext_Return *ret)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ int64_t context = 0;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] EstablishContext complete: return_code=%i",
+ return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ if (ret)
+ context = (int64_t) smartcard_scard_context_native_from_redir (&ret->hContext);
+
+ ret_variant = g_variant_new ("((xx))",
+ (int64_t) EXTEND_32_TO_64 (return_code),
+ context);
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_establish_context (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ uint64_t scope;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] EstablishContext");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(t)", &scope);
+ status = rdpdr_context->SmartcardEstablishContext (rdpdr_context,
+ invocation,
+ NARROW_64_TO_32 (scope),
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardEstablishContext failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_release_context_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] ReleaseContext complete: return_code=%i",
+ return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ ret_variant = g_variant_new ("((x))",
+ (int64_t) EXTEND_32_TO_64 (return_code));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_release_context (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ REDIR_SCARDCONTEXT redir_context = {};
+ int64_t context;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] ReleaseContext");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(x)", &context);
+ smartcard_scard_context_native_to_redir (&redir_context, context);
+
+ status = rdpdr_context->SmartcardReleaseContext (rdpdr_context,
+ invocation,
+ &redir_context,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardReleaseContext failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_is_valid_context_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] IsValidContext complete: return_code=%i",
+ return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ ret_variant = g_variant_new ("((x))",
+ (int64_t) EXTEND_32_TO_64 (return_code));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_is_valid_context (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ REDIR_SCARDCONTEXT redir_context = {};
+ int64_t context;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] IsValidContext");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(x)", &context);
+ smartcard_scard_context_native_to_redir (&redir_context, context);
+
+ status = rdpdr_context->SmartcardIsValidContext (rdpdr_context,
+ invocation,
+ &redir_context,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardIsValidContext failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_connect_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code,
+ const Connect_Return *ret)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ GVariant *ret_variant;
+ int64_t out_context = 0;
+ int64_t card = 0;
+ uint64_t active_protocol = 0;
+
+ g_debug ("[RDP.SMARTCARD] Connect complete: return_code=%i", return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ if (ret)
+ {
+ out_context =
+ (int64_t) smartcard_scard_context_native_from_redir (&ret->hContext);
+ card =
+ (int64_t) smartcard_scard_handle_native_from_redir (&ret->hCard);
+ active_protocol =
+ EXTEND_32_TO_64 (convert_protocol_to_pcsc (ret->dwActiveProtocol));
+ }
+
+ ret_variant = g_variant_new ("((xxxt))",
+ (int64_t) EXTEND_32_TO_64 (return_code),
+ out_context, card, active_protocol);
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_connect_card (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *variant_call,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ ConnectA_Call call = {};
+ int64_t context;
+ const char *reader;
+ uint64_t share_mode;
+ uint64_t preferred_protocols;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] Connect");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (variant_call, "(x&stt)",
+ &context,
+ &reader,
+ &share_mode,
+ &preferred_protocols);
+
+ smartcard_scard_context_native_to_redir (&call.Common.handles.hContext,
+ context);
+ call.Common.dwShareMode = NARROW_64_TO_32 (share_mode);
+ call.szReader = (char *) reader;
+ call.Common.dwPreferredProtocols =
+ convert_protocol_to_winscard (NARROW_64_TO_32 (preferred_protocols));
+
+ status = rdpdr_context->SmartcardConnectA (rdpdr_context,
+ invocation,
+ &call,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardConnectA failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_reconnect_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code,
+ const Reconnect_Return *ret)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ uint64_t active_protocol = 0;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] Reconnect complete: return_code=%i", return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ if (ret)
+ {
+ active_protocol =
+ EXTEND_32_TO_64 (convert_protocol_to_pcsc (ret->dwActiveProtocol));
+ }
+
+ ret_variant = g_variant_new ("((xt))",
+ (int64_t) EXTEND_32_TO_64 (return_code),
+ active_protocol);
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_reconnect (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ Reconnect_Call call = {};
+ int64_t card;
+ uint64_t share_mode;
+ uint64_t preferred_protocols;
+ uint64_t initialization;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] Reconnect");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(xttt)",
+ &card,
+ &share_mode,
+ &preferred_protocols,
+ &initialization);
+
+ smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
+ call.dwShareMode = NARROW_64_TO_32 (share_mode);
+ call.dwPreferredProtocols =
+ convert_protocol_to_winscard (NARROW_64_TO_32 (preferred_protocols));
+ call.dwInitialization = NARROW_64_TO_32 (initialization);
+
+ status = rdpdr_context->SmartcardReconnect (rdpdr_context,
+ invocation,
+ &call,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardReconnect failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_disconnect_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] Disconnect complete: return_code=%i", return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ ret_variant = g_variant_new ("((x))",
+ (int64_t) EXTEND_32_TO_64 (return_code));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_disconnect_card (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ HCardAndDisposition_Call call = {};
+ int64_t card;
+ uint64_t disposition;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] Disconnect");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(xt)", &card, &disposition);
+
+ smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
+ call.dwDisposition = NARROW_64_TO_32 (disposition);
+
+ status = rdpdr_context->SmartcardDisconnect (rdpdr_context,
+ invocation,
+ &call,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardDisconnect failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_begin_transaction_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] BeginTransaction complete: return_code=%i",
+ return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ ret_variant = g_variant_new ("((x))",
+ (int64_t) EXTEND_32_TO_64 (return_code));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_begin_transaction (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ HCardAndDisposition_Call call = {};
+ int64_t card;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] BeginTransaction");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(x)", &card);
+
+ smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
+
+ status = rdpdr_context->SmartcardBeginTransaction (rdpdr_context,
+ invocation,
+ &call,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardBeginTransaction failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_end_transaction_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] EndTransaction complete: return_code=%i",
+ return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ ret_variant = g_variant_new ("((x))",
+ (int64_t) EXTEND_32_TO_64 (return_code));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_end_transaction (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ HCardAndDisposition_Call call = {};
+ int64_t card;
+ uint64_t disposition;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] EndTransaction");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(xt)", &card, &disposition);
+
+ smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
+ call.dwDisposition = NARROW_64_TO_32 (disposition);
+
+ status = rdpdr_context->SmartcardEndTransaction (rdpdr_context,
+ invocation,
+ &call,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardEndTransaction failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_status_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code,
+ const Status_Return *ret)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ GVariant *ret_variant;
+ const char *reader_name = "";
+ uint64_t state = 0;
+ uint64_t protocol = 0;
+ const uint8_t *atr = NULL;
+ size_t atr_len = 0;
+
+ g_debug ("[RDP.SMARTCARD] Status complete: return_code=%i", return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ if (ret)
+ {
+ if (ret->mszReaderNames)
+ reader_name = (const char *) ret->mszReaderNames;
+ state = EXTEND_32_TO_64 (convert_card_state_to_pcsc (ret->dwState));
+ protocol = EXTEND_32_TO_64 (convert_protocol_to_pcsc (ret->dwProtocol));
+ atr = ret->pbAtr;
+ atr_len = ret->cbAtrLen;
+ }
+
+ ret_variant = g_variant_new ("((xstt@ay))",
+ (int64_t) EXTEND_32_TO_64 (return_code),
+ reader_name, state, protocol,
+ g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+ atr, atr_len,
+ sizeof (uint8_t)));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_status_card (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ Status_Call call = {};
+ int64_t card;
+ uint64_t reader_len;
+ uint64_t atr_len;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] Status");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(xtt)", &card, &reader_len, &atr_len);
+
+ smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
+ call.cchReaderLen = NARROW_64_TO_32 (reader_len);
+ call.cbAtrLen = NARROW_64_TO_32 (atr_len);
+
+ status = rdpdr_context->SmartcardStatusA (rdpdr_context,
+ invocation,
+ &call,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardStatusA failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_get_status_change_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code,
+ const GetStatusChange_Return *ret)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ GVariantBuilder *builder;
+ GVariant *ret_variant;
+ int i;
+
+ g_debug ("[RDP.SMARTCARD] GetStatusChange complete: return_code=%i",
+ return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ builder = g_variant_builder_new (G_VARIANT_TYPE ("a(ttay)"));
+
+ if (ret)
+ {
+ for (i = 0; i < ret->cReaders; i++)
+ {
+ g_variant_builder_add (builder, "(tt@ay)",
+ EXTEND_32_TO_64 (ret->rgReaderStates[i].dwEventState),
+ EXTEND_32_TO_64 (ret->rgReaderStates[i].cbAtr),
+ g_variant_new_fixed_array (
+ G_VARIANT_TYPE_BYTE,
+ ret->rgReaderStates[i].rgbAtr,
+ ret->rgReaderStates[i].cbAtr,
+ sizeof (uint8_t)));
+ }
+ }
+
+ ret_variant = g_variant_new ("((x@a(ttay)))",
+ (int64_t) EXTEND_32_TO_64 (return_code),
+ g_variant_builder_end (builder));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_get_status_change (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ g_autoptr (GVariant) reader_states = NULL;
+ g_autofree SCARD_READERSTATEA *reader_states_a = NULL;
+ GetStatusChangeA_Call call = {};
+ int64_t context;
+ uint64_t timeout;
+ char *reader_name;
+ uint64_t current_state;
+ uint32_t completion_id;
+ GVariantIter iter;
+ size_t n_readers;
+ uint32_t status;
+ int i;
+
+ g_debug ("[RDP.SMARTCARD] GetStatusChange");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(xt@a(st))",
+ &context,
+ &timeout,
+ &reader_states);
+
+ n_readers = g_variant_n_children (reader_states);
+ reader_states_a = g_new0 (SCARD_READERSTATEA, n_readers);
+
+ i = 0;
+ g_variant_iter_init (&iter, reader_states);
+ while (g_variant_iter_next (&iter, "(&st)", &reader_name, &current_state))
+ {
+ reader_states_a[i].szReader = reader_name;
+ reader_states_a[i].dwCurrentState = NARROW_64_TO_32 (current_state);
+ i++;
+ }
+
+ smartcard_scard_context_native_to_redir (&call.handles.hContext, context);
+ call.dwTimeOut = NARROW_64_TO_32 (timeout);
+ call.cReaders = NARROW_64_TO_32 (n_readers);
+ call.rgReaderStates = reader_states_a;
+
+ status = rdpdr_context->SmartcardGetStatusChangeA (rdpdr_context,
+ invocation,
+ &call,
+ &completion_id);
+
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardGetStatusChange failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_control_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code,
+ const Control_Return *ret)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ const uint8_t *out_buffer = NULL;
+ size_t out_buffer_size = 0;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] Control complete: return_code=%i", return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ if (ret)
+ {
+ out_buffer = ret->pvOutBuffer;
+ out_buffer_size = ret->cbOutBufferSize;
+ }
+
+ ret_variant = g_variant_new ("((x@ay))",
+ (int64_t) EXTEND_32_TO_64 (return_code),
+ g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+ out_buffer,
+ out_buffer_size,
+ sizeof (uint8_t)));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_control_card (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ g_autoptr (GVariant) send_buffer = NULL;
+ Control_Call call = {};
+ int64_t card;
+ uint64_t control_code;
+ uint64_t send_buffer_size;
+ uint64_t recv_buffer_size;
+ const uint8_t *send_data;
+ size_t send_size;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] Control");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(xt@aytt)",
+ &card,
+ &control_code,
+ &send_buffer,
+ &send_buffer_size,
+ &recv_buffer_size);
+
+ send_data = g_variant_get_fixed_array (send_buffer, &send_size,
+ sizeof (uint8_t));
+
+ smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
+ call.dwControlCode = NARROW_64_TO_32 (control_code);
+ call.cbInBufferSize = NARROW_64_TO_32 (send_buffer_size);
+ call.pvInBuffer = (uint8_t *) send_data;
+ call.cbOutBufferSize = NARROW_64_TO_32 (recv_buffer_size);
+
+ status = rdpdr_context->SmartcardControl (rdpdr_context,
+ invocation,
+ &call,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardControl failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_transmit_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code,
+ const Transmit_Return *ret)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ uint64_t recv_pci_protocol = 0;
+ const uint8_t *recv_buffer = NULL;
+ size_t recv_length = 0;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] Transmit complete: return_code=%i", return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ if (ret)
+ {
+ if (ret->pioRecvPci)
+ {
+ recv_pci_protocol =
+ EXTEND_32_TO_64 (convert_protocol_to_pcsc (ret->pioRecvPci->dwProtocol));
+ }
+ recv_buffer = ret->pbRecvBuffer;
+ recv_length = ret->cbRecvLength;
+ }
+
+ ret_variant = g_variant_new ("((xt@ay))",
+ (int64_t) EXTEND_32_TO_64 (return_code),
+ recv_pci_protocol,
+ g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+ recv_buffer,
+ recv_length,
+ sizeof (uint8_t)));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_transmit (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ g_autoptr (GVariant) send_buffer = NULL;
+ SCARD_IO_REQUEST send_pci = {};
+ SCARD_IO_REQUEST recv_pci = {};
+ Transmit_Call call = {};
+ int64_t card;
+ uint64_t send_pci_protocol;
+ uint64_t recv_pci_protocol_in;
+ uint64_t recv_buffer_size;
+ const uint8_t *send_data;
+ size_t send_size;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] Transmit");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(xt@aytt)",
+ &card,
+ &send_pci_protocol,
+ &send_buffer,
+ &recv_pci_protocol_in,
+ &recv_buffer_size);
+
+ send_data = g_variant_get_fixed_array (send_buffer, &send_size,
+ sizeof (uint8_t));
+
+ send_pci.dwProtocol =
+ convert_protocol_to_winscard (NARROW_64_TO_32 (send_pci_protocol));
+ send_pci.cbPciLength = sizeof (SCARD_IO_REQUEST);
+
+ recv_pci.dwProtocol =
+ convert_protocol_to_winscard (NARROW_64_TO_32 (recv_pci_protocol_in));
+ recv_pci.cbPciLength = sizeof (SCARD_IO_REQUEST);
+
+ smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
+ call.pioSendPci = &send_pci;
+ call.cbSendLength = NARROW_64_TO_32 (send_size);
+ call.pbSendBuffer = (uint8_t *) send_data;
+ call.pioRecvPci = &recv_pci;
+ call.cbRecvLength = NARROW_64_TO_32 (recv_buffer_size);
+
+ status = rdpdr_context->SmartcardTransmit (rdpdr_context,
+ invocation,
+ &call,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardTransmit failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_list_reader_groups_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code,
+ const ListReaderGroups_Return *ret)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ GVariantBuilder *builder;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] ListReaderGroups complete: return_code=%i",
+ return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
+
+ if (ret && ret->msz && return_code == SCARD_S_SUCCESS)
+ {
+ const char *p = (const char *) ret->msz;
+ const char *end = (const char *) (ret->msz + ret->cBytes);
+
+ while (p < end && *p)
+ {
+ g_variant_builder_add (builder, "s", p);
+ p += strlen (p) + 1;
+ }
+ }
+
+ ret_variant = g_variant_new ("((x@as))",
+ (int64_t) EXTEND_32_TO_64 (return_code),
+ g_variant_builder_end (builder));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_list_reader_groups (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ ListReaderGroups_Call call = {};
+ int64_t context;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] ListReaderGroups");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(x)", &context);
+
+ smartcard_scard_context_native_to_redir (&call.handles.hContext, context);
+ call.cchGroups = SCARD_AUTOALLOCATE;
+
+ status = rdpdr_context->SmartcardListReaderGroupsA (rdpdr_context,
+ invocation,
+ &call,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardListReaderGroups failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_list_readers_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code,
+ const ListReaders_Return *ret)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ GVariantBuilder *builder;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] ListReaders complete: return_code=%i", return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
+
+ if (ret && ret->msz && return_code == SCARD_S_SUCCESS)
+ {
+ const char *p = (const char *) ret->msz;
+ const char *end = (const char *) (ret->msz + ret->cBytes);
+
+ while (p < end && *p)
+ {
+ g_variant_builder_add (builder, "s", p);
+ p += strlen (p) + 1;
+ }
+ }
+
+ ret_variant = g_variant_new ("((x@as))",
+ (int64_t) EXTEND_32_TO_64 (return_code),
+ g_variant_builder_end (builder));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_list_readers (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ ListReaders_Call call = {};
+ int64_t context;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] ListReaders");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(x)", &context);
+
+ smartcard_scard_context_native_to_redir (&call.handles.hContext, context);
+ call.cchReaders = SCARD_AUTOALLOCATE;
+
+ status = rdpdr_context->SmartcardListReadersA (rdpdr_context,
+ invocation,
+ &call,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardListReaders failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_cancel_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] Cancel complete: return_code=%i", return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ ret_variant = g_variant_new ("((x))",
+ (int64_t) EXTEND_32_TO_64 (return_code));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_cancel (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ REDIR_SCARDCONTEXT redir_context = {};
+ int64_t context;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] Cancel");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(x)", &context);
+ smartcard_scard_context_native_to_redir (&redir_context, context);
+
+ status = rdpdr_context->SmartcardCancel (rdpdr_context,
+ invocation,
+ &redir_context,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardCancel failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_get_attrib_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code,
+ const GetAttrib_Return *ret)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ const uint8_t *attr = NULL;
+ size_t attr_len = 0;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] GetAttrib complete: return_code=%i", return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ if (ret)
+ {
+ attr = ret->pbAttr;
+ attr_len = ret->cbAttrLen;
+ }
+
+ ret_variant = g_variant_new ("((x@ay))",
+ (int64_t) EXTEND_32_TO_64 (return_code),
+ g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+ attr, attr_len,
+ sizeof (uint8_t)));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_get_attrib (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ GetAttrib_Call call = {};
+ int64_t card;
+ uint64_t attr_id;
+ uint64_t attr_len;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] GetAttrib");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(xtt)", &card, &attr_id, &attr_len);
+
+ smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
+ call.dwAttrId = NARROW_64_TO_32 (attr_id);
+ call.cbAttrLen = NARROW_64_TO_32 (attr_len);
+
+ status = rdpdr_context->SmartcardGetAttrib (rdpdr_context,
+ invocation,
+ &call,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardGetAttrib failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_smartcard_set_attrib_complete (RdpdrServerContext *rdpdr_context,
+ void *callback_data,
+ uint32_t io_status,
+ int32_t return_code)
+{
+ GDBusMethodInvocation *invocation = callback_data;
+ GVariant *ret_variant;
+
+ g_debug ("[RDP.SMARTCARD] SetAttrib complete: return_code=%i", return_code);
+
+ remove_pending_invocation (rdpdr_context, invocation);
+
+ ret_variant = g_variant_new ("((x))",
+ (int64_t) EXTEND_32_TO_64 (return_code));
+ g_dbus_method_invocation_return_value (invocation, ret_variant);
+}
+
+static gboolean
+on_handle_set_attrib (GrdDBusPcscdSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *call_variant,
+ gpointer user_data)
+{
+ GrdRdpSmartcard *smartcard = user_data;
+ RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
+ g_autoptr (GVariant) attr_buffer = NULL;
+ SetAttrib_Call call = {};
+ int64_t card;
+ uint64_t attr_id;
+ const uint8_t *attr_data;
+ size_t attr_size;
+ uint32_t completion_id;
+ uint32_t status;
+
+ g_debug ("[RDP.SMARTCARD] SetAttrib");
+
+ if (is_device_unavailable (smartcard, invocation))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ g_variant_get (call_variant, "(xt@ay)",
+ &card,
+ &attr_id,
+ &attr_buffer);
+
+ attr_data = g_variant_get_fixed_array (attr_buffer, &attr_size,
+ sizeof (uint8_t));
+
+ smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
+ call.dwAttrId = NARROW_64_TO_32 (attr_id);
+ call.cbAttrLen = attr_size;
+ call.pbAttr = (uint8_t *) attr_data;
+
+ status = rdpdr_context->SmartcardSetAttrib (rdpdr_context,
+ invocation,
+ &call,
+ &completion_id);
+ if (status != CHANNEL_RC_OK)
+ {
+ g_hash_table_remove (smartcard->pending_invocations, invocation);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "SmartcardSetAttrib failed: %u",
+ status);
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+ }
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+connect_pcscd_session_handlers (GrdDBusPcscdSession *skeleton,
+ GrdRdpSmartcard *smartcard)
+{
+ g_signal_connect_object (skeleton, "handle-establish-context",
+ G_CALLBACK (on_handle_establish_context),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-release-context",
+ G_CALLBACK (on_handle_release_context),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-is-valid-context",
+ G_CALLBACK (on_handle_is_valid_context),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-connect-card",
+ G_CALLBACK (on_handle_connect_card),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-reconnect",
+ G_CALLBACK (on_handle_reconnect),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-disconnect-card",
+ G_CALLBACK (on_handle_disconnect_card),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-begin-transaction",
+ G_CALLBACK (on_handle_begin_transaction),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-end-transaction",
+ G_CALLBACK (on_handle_end_transaction),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-status-card",
+ G_CALLBACK (on_handle_status_card),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-get-status-change",
+ G_CALLBACK (on_handle_get_status_change),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-control-card",
+ G_CALLBACK (on_handle_control_card),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-transmit",
+ G_CALLBACK (on_handle_transmit),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-list-reader-groups",
+ G_CALLBACK (on_handle_list_reader_groups),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-list-readers",
+ G_CALLBACK (on_handle_list_readers),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-cancel",
+ G_CALLBACK (on_handle_cancel),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-get-attrib",
+ G_CALLBACK (on_handle_get_attrib),
+ smartcard, G_CONNECT_DEFAULT);
+ g_signal_connect_object (skeleton, "handle-set-attrib",
+ G_CALLBACK (on_handle_set_attrib),
+ smartcard, G_CONNECT_DEFAULT);
+}
+
static void
on_private_connection_ready (GObject *source_object,
GAsyncResult *result,
@@ -95,6 +1576,8 @@ on_private_connection_ready (GObject *source_object,
return;
}
+ connect_pcscd_session_handlers (private_proxy, smartcard);
+
smartcard->private_proxy = g_steal_pointer (&private_proxy);
g_debug ("[RDP.SMARTCARD] Private D-Bus connection established");
@@ -199,18 +1682,52 @@ connect_to_grd_pcscd (GrdRdpSmartcard *smartcard)
data);
}
+static gboolean
+fail_pending_invocation_by_connection (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GDBusMethodInvocation *invocation = key;
+ GDBusConnection *connection = user_data;
+
+ if (g_dbus_method_invocation_get_connection (invocation) != connection)
+ return FALSE;
+
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "Smartcard device disconnected");
+ return TRUE;
+}
+
+static void
+unexport_and_fail_pending_invocations (GrdRdpSmartcard *smartcard,
+ GrdDBusPcscdSession **proxy)
+{
+ GDBusInterfaceSkeleton *skeleton;
+ GDBusConnection *connection;
+
+ if (!*proxy)
+ return;
+
+ skeleton = G_DBUS_INTERFACE_SKELETON (*proxy);
+ connection = g_dbus_interface_skeleton_get_connection (skeleton);
+
+ g_dbus_interface_skeleton_unexport (skeleton);
+ g_clear_object (proxy);
+
+ g_hash_table_foreach_remove (smartcard->pending_invocations,
+ fail_pending_invocation_by_connection,
+ connection);
+}
+
static void
disconnect_from_grd_pcscd (GrdRdpSmartcard *smartcard)
{
g_cancellable_cancel (smartcard->private_proxy_cancellable);
g_clear_object (&smartcard->private_proxy_cancellable);
- if (smartcard->private_proxy)
- {
- g_dbus_interface_skeleton_unexport (
- G_DBUS_INTERFACE_SKELETON (smartcard->private_proxy));
- g_clear_object (&smartcard->private_proxy);
- }
+ unexport_and_fail_pending_invocations (smartcard, &smartcard->private_proxy);
}
static void
@@ -395,6 +1912,40 @@ grd_rdp_smartcard_new (GrdSessionRdp *session_rdp,
rdpdr_context->OnSmartcardCreate = on_smartcard_create;
rdpdr_context->OnSmartcardDelete = on_smartcard_delete;
+ rdpdr_context->OnSmartcardEstablishContextComplete =
+ on_smartcard_establish_context_complete;
+ rdpdr_context->OnSmartcardReleaseContextComplete =
+ on_smartcard_release_context_complete;
+ rdpdr_context->OnSmartcardIsValidContextComplete =
+ on_smartcard_is_valid_context_complete;
+ rdpdr_context->OnSmartcardConnectComplete =
+ on_smartcard_connect_complete;
+ rdpdr_context->OnSmartcardReconnectComplete =
+ on_smartcard_reconnect_complete;
+ rdpdr_context->OnSmartcardDisconnectComplete =
+ on_smartcard_disconnect_complete;
+ rdpdr_context->OnSmartcardBeginTransactionComplete =
+ on_smartcard_begin_transaction_complete;
+ rdpdr_context->OnSmartcardEndTransactionComplete =
+ on_smartcard_end_transaction_complete;
+ rdpdr_context->OnSmartcardStatusComplete =
+ on_smartcard_status_complete;
+ rdpdr_context->OnSmartcardGetStatusChangeComplete =
+ on_smartcard_get_status_change_complete;
+ rdpdr_context->OnSmartcardControlComplete =
+ on_smartcard_control_complete;
+ rdpdr_context->OnSmartcardTransmitComplete =
+ on_smartcard_transmit_complete;
+ rdpdr_context->OnSmartcardListReaderGroupsComplete =
+ on_smartcard_list_reader_groups_complete;
+ rdpdr_context->OnSmartcardListReadersComplete =
+ on_smartcard_list_readers_complete;
+ rdpdr_context->OnSmartcardCancelComplete =
+ on_smartcard_cancel_complete;
+ rdpdr_context->OnSmartcardGetAttribComplete =
+ on_smartcard_get_attrib_complete;
+ rdpdr_context->OnSmartcardSetAttribComplete =
+ on_smartcard_set_attrib_complete;
rdpdr_context->supported |= RDPDR_DTYP_SMARTCARD;
@@ -408,6 +1959,7 @@ grd_rdp_smartcard_dispose (GObject *object)
teardown_pcscd_proxies (smartcard);
g_clear_pointer (&smartcard->smartcard_device_ids, g_hash_table_unref);
+ g_clear_pointer (&smartcard->pending_invocations, g_hash_table_unref);
G_OBJECT_CLASS (grd_rdp_smartcard_parent_class)->dispose (object);
}
@@ -428,6 +1980,7 @@ grd_rdp_smartcard_init (GrdRdpSmartcard *smartcard)
g_mutex_init (&smartcard->shutdown_mutex);
smartcard->smartcard_device_ids = g_hash_table_new (NULL, NULL);
+ smartcard->pending_invocations = g_hash_table_new (NULL, NULL);
}
static void
diff --git a/src/meson.build b/src/meson.build
index 62e32a5..73c59cd 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -437,6 +437,23 @@ if have_rdp
include_directories: [configinc],
install: true,
install_dir: libexecdir)
+
+ shared_library('grdpcsc',
+ 'grd-pcsc-lib.c',
+ 'grd-pcsc-lib-backend.c',
+ 'grd-pcsc-lib-backend-grd-pcscd-proxy.c',
+ dbus_gen_pcscd,
+ dependencies: [glib_dep,
+ gio_dep,
+ dl_dep,
+ libsystemd_dep],
+ include_directories: [configinc],
+ # GLib's GType system cannot survive dlclose/dlopen cycles
+ link_args: ['-Wl,-z,nodelete'],
+ version: '0.0.0',
+ soversion: 0,
+ gnu_symbol_visibility: 'hidden',
+ install: true)
endif
custom_target('gsettings-enums',
diff --git a/src/org.gnome.RemoteDesktop.Pcscd.xml b/src/org.gnome.RemoteDesktop.Pcscd.xml
index 34e98a9..d4042e4 100644
--- a/src/org.gnome.RemoteDesktop.Pcscd.xml
+++ b/src/org.gnome.RemoteDesktop.Pcscd.xml
@@ -54,6 +54,210 @@
-->
<interface name="org.gnome.RemoteDesktop.Pcscd.Session">
+ <!--
+ EstablishContext:
+
+ Establish a PC/SC resource manager context.
+ call: (scope)
+ ret: (return_value, context)
+ -->
+ <method name="EstablishContext">
+ <arg name="call" direction="in" type="(t)"/>
+ <arg name="ret" direction="out" type="(xx)"/>
+ </method>
+
+ <!--
+ ReleaseContext:
+
+ Release a previously established PC/SC context.
+ call: (context)
+ ret: (return_value)
+ -->
+ <method name="ReleaseContext">
+ <arg name="call" direction="in" type="(x)"/>
+ <arg name="ret" direction="out" type="(x)"/>
+ </method>
+
+ <!--
+ IsValidContext:
+
+ Validate a PC/SC context.
+ call: (context)
+ ret: (return_value)
+ -->
+ <method name="IsValidContext">
+ <arg name="call" direction="in" type="(x)"/>
+ <arg name="ret" direction="out" type="(x)"/>
+ </method>
+
+ <!--
+ ConnectCard:
+
+ Connect to a smartcard in a reader.
+ call: (context, reader, share_mode, preferred_protocols)
+ ret: (return_value, out_context, out_card, active_protocol)
+ -->
+ <method name="ConnectCard">
+ <arg name="call" direction="in" type="(xstt)"/>
+ <arg name="ret" direction="out" type="(xxxt)"/>
+ </method>
+
+ <!--
+ Reconnect:
+
+ Reconnect to a smartcard.
+ call: (card, share_mode, preferred_protocols, initialization)
+ ret: (return_value, active_protocol)
+ -->
+ <method name="Reconnect">
+ <arg name="call" direction="in" type="(xttt)"/>
+ <arg name="ret" direction="out" type="(xt)"/>
+ </method>
+
+ <!--
+ DisconnectCard:
+
+ Disconnect from a smartcard.
+ call: (card, disposition)
+ ret: (return_value)
+ -->
+ <method name="DisconnectCard">
+ <arg name="call" direction="in" type="(xt)"/>
+ <arg name="ret" direction="out" type="(x)"/>
+ </method>
+
+ <!--
+ BeginTransaction:
+
+ Begin a transaction on a smartcard.
+ call: (card)
+ ret: (return_value)
+ -->
+ <method name="BeginTransaction">
+ <arg name="call" direction="in" type="(x)"/>
+ <arg name="ret" direction="out" type="(x)"/>
+ </method>
+
+ <!--
+ EndTransaction:
+
+ End a transaction on a smartcard.
+ call: (card, disposition)
+ ret: (return_value)
+ -->
+ <method name="EndTransaction">
+ <arg name="call" direction="in" type="(xt)"/>
+ <arg name="ret" direction="out" type="(x)"/>
+ </method>
+
+ <!--
+ StatusCard:
+
+ Get the status of a smartcard.
+ call: (card, reader_len, atr_len)
+ ret: (return_value, reader_name, state, protocol, atr)
+ -->
+ <method name="StatusCard">
+ <arg name="call" direction="in" type="(xtt)"/>
+ <arg name="ret" direction="out" type="(xsttay)"/>
+ </method>
+
+ <!--
+ GetStatusChange:
+
+ Wait for status changes on readers.
+ call: (context, timeout, reader_states[(name, current_state)])
+ ret: (return_value, reader_states_out[(event_state, atr_length, atr)])
+ -->
+ <method name="GetStatusChange">
+ <arg name="call" direction="in" type="(xta(st))"/>
+ <arg name="ret" direction="out" type="(xa(ttay))"/>
+ </method>
+
+ <!--
+ ControlCard:
+
+ Send a control command to a smartcard.
+ call: (card, control_code, send_buffer, send_buffer_size, recv_buffer_size)
+ ret: (return_value, recv_buffer)
+ -->
+ <method name="ControlCard">
+ <arg name="call" direction="in" type="(xtaytt)"/>
+ <arg name="ret" direction="out" type="(xay)"/>
+ </method>
+
+ <!--
+ Transmit:
+
+ Transmit an APDU to a smartcard.
+ call: (card, send_pci_protocol, send_buffer, recv_pci_protocol, recv_buffer_size)
+ ret: (return_value, recv_pci_protocol, recv_buffer)
+ -->
+ <method name="Transmit">
+ <arg name="call" direction="in" type="(xtaytt)"/>
+ <arg name="ret" direction="out" type="(xtay)"/>
+ </method>
+
+ <!--
+ ListReaderGroups:
+
+ List available smartcard reader groups.
+ call: (context)
+ ret: (return_value, groups)
+ -->
+ <method name="ListReaderGroups">
+ <arg name="call" direction="in" type="(x)"/>
+ <arg name="ret" direction="out" type="(xas)"/>
+ </method>
+
+ <!--
+ ListReaders:
+
+ List available smartcard readers.
+ call: (context)
+ ret: (return_value, readers)
+ -->
+ <method name="ListReaders">
+ <arg name="call" direction="in" type="(x)"/>
+ <arg name="ret" direction="out" type="(xas)"/>
+ </method>
+
+ <!--
+ Cancel:
+
+ Cancel a pending smartcard operation.
+ call: (context)
+ ret: (return_value)
+ -->
+ <method name="Cancel">
+ <arg name="call" direction="in" type="(x)"/>
+ <arg name="ret" direction="out" type="(x)"/>
+ </method>
+
+ <!--
+ GetAttrib:
+
+ Get an attribute from a smartcard.
+ call: (card, attr_id, attr_len)
+ ret: (return_value, attr)
+ -->
+ <method name="GetAttrib">
+ <arg name="call" direction="in" type="(xtt)"/>
+ <arg name="ret" direction="out" type="(xay)"/>
+ </method>
+
+ <!--
+ SetAttrib:
+
+ Set an attribute on a smartcard.
+ call: (card, attr_id, attr)
+ ret: (return_value)
+ -->
+ <method name="SetAttrib">
+ <arg name="call" direction="in" type="(xtay)"/>
+ <arg name="ret" direction="out" type="(x)"/>
+ </method>
+
</interface>
</node>
--
2.51.0
From de2047feab4712684bf933de04ba8e9c4d75bb5a Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Mon, 3 Aug 2026 19:38:51 +0200
Subject: [PATCH 06/11] rdp-smartcard: Add session bus proxy for direct PC/SC
access
Add a session-level D-Bus skeleton to GrdRdpSmartcard so that processes
running inside the user session can issue PC/SC commands directly to the
RDP client, without depending on grd-pcscd on the system bus.
When a smartcard device is created, GrdRdpSmartcard now owns the
org.gnome.RemoteDesktop.Pcscd bus name on the session bus and exports
a PcscdSession skeleton at /org/gnome/RemoteDesktop/Pcscd/{session_id}.
This session proxy is fully independent from the private peer-to-peer
proxy used by grd-pcscd: each has its own pending invocations table, and
a grd-pcscd name owner change only affects the private path.
libgrdpcsc first tries to connect to the session bus object, falling
back to the system bus object if it is not available.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/merge_requests/406>
---
src/grd-pcsc-lib-backend-grd-pcscd-proxy.c | 130 +++++++++++++++------
src/grd-rdp-smartcard.c | 104 +++++++++++++++++
2 files changed, 199 insertions(+), 35 deletions(-)
diff --git a/src/grd-pcsc-lib-backend-grd-pcscd-proxy.c b/src/grd-pcsc-lib-backend-grd-pcscd-proxy.c
index 7c8b1442..779e1586 100644
--- a/src/grd-pcsc-lib-backend-grd-pcscd-proxy.c
+++ b/src/grd-pcsc-lib-backend-grd-pcscd-proxy.c
@@ -822,6 +822,40 @@ grd_pcscd_proxy_set_attrib (SCARDHANDLE hCard,
return return_value;
}
+static char *
+get_session_id_from_uid (uid_t uid)
+{
+ const char * const graphical_types[] = { "wayland", "x11", NULL };
+ const char * const active_states[] = { "active", "online", NULL };
+ g_auto (GStrv) sessions = NULL;
+ int n_sessions;
+ int i;
+
+ n_sessions = sd_uid_get_sessions (uid, 0, &sessions);
+
+ for (i = n_sessions - 1; i >= 0; i--)
+ {
+ g_autofree char *type = NULL;
+ g_autofree char *state = NULL;
+
+ if (sd_session_get_type (sessions[i], &type) < 0)
+ continue;
+
+ if (!g_strv_contains (graphical_types, type))
+ continue;
+
+ if (sd_session_get_state (sessions[i], &state) < 0)
+ continue;
+
+ if (!g_strv_contains (active_states, state))
+ continue;
+
+ return g_strdup (sessions[i]);
+ }
+
+ return NULL;
+}
+
static char *
get_session_id (void)
{
@@ -832,10 +866,14 @@ get_session_id (void)
if (session_id && g_strcmp0 (session_id, "") != 0)
return g_strdup (session_id);
- if (sd_pid_get_session (0, &logind_session_id) < 0)
- return NULL;
+ session_id = g_getenv ("XDG_SESSION_ID");
+ if (session_id && session_id[0] != '\0')
+ return g_strdup (session_id);
+
+ if (sd_pid_get_session (0, &logind_session_id) >= 0)
+ return logind_session_id;
- return logind_session_id;
+ return get_session_id_from_uid (getuid ());
}
static gboolean
@@ -858,39 +896,9 @@ dbus_interface_exists (GDBusProxy *proxy)
return result != NULL;
}
-gboolean
-grd_pcsc_lib_backend_init_grd_pcscd_proxy (GrdPcscLibBackend *backend)
+static void
+set_backend_functions (GrdPcscLibBackend *backend)
{
- g_autofree char *session_id = NULL;
- g_autofree char *object_path = NULL;
- g_autofree char *name_owner = NULL;
- g_autoptr (GError) error = NULL;
-
- session_id = get_session_id ();
- if (!session_id)
- return FALSE;
-
- object_path = g_strdup_printf ("/org/gnome/RemoteDesktop/Pcscd/%s", session_id);
- session_proxy = grd_dbus_pcscd_session_proxy_new_for_bus_sync (
- G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
- PCSCD_BUS_NAME,
- object_path,
- NULL,
- &error);
- if (!session_proxy)
- return FALSE;
-
- name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (session_proxy));
- if (!name_owner)
- return FALSE;
-
- if (!dbus_interface_exists (G_DBUS_PROXY (session_proxy)))
- {
- g_clear_object (&session_proxy);
- return FALSE;
- }
-
backend->SCardEstablishContext = grd_pcscd_proxy_establish_context;
backend->SCardReleaseContext = grd_pcscd_proxy_release_context;
backend->SCardIsValidContext = grd_pcscd_proxy_is_valid_context;
@@ -909,10 +917,62 @@ grd_pcsc_lib_backend_init_grd_pcscd_proxy (GrdPcscLibBackend *backend)
backend->SCardCancel = grd_pcscd_proxy_cancel;
backend->SCardGetAttrib = grd_pcscd_proxy_get_attrib;
backend->SCardSetAttrib = grd_pcscd_proxy_set_attrib;
+}
+
+static gboolean
+try_bus_type (GBusType bus_type,
+ const char *object_path)
+{
+ g_autofree char *name_owner = NULL;
+
+ session_proxy = grd_dbus_pcscd_session_proxy_new_for_bus_sync (
+ bus_type,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ PCSCD_BUS_NAME,
+ object_path,
+ NULL,
+ NULL);
+ if (!session_proxy)
+ return FALSE;
+
+ name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (session_proxy));
+ if (!name_owner)
+ {
+ g_clear_object (&session_proxy);
+ return FALSE;
+ }
+
+ if (!dbus_interface_exists (G_DBUS_PROXY (session_proxy)))
+ {
+ g_clear_object (&session_proxy);
+ return FALSE;
+ }
return TRUE;
}
+gboolean
+grd_pcsc_lib_backend_init_grd_pcscd_proxy (GrdPcscLibBackend *backend)
+{
+ g_autofree char *session_id = NULL;
+ g_autofree char *object_path = NULL;
+
+ session_id = get_session_id ();
+ if (!session_id)
+ return FALSE;
+
+ object_path = g_strdup_printf ("/org/gnome/RemoteDesktop/Pcscd/%s", session_id);
+
+ if (try_bus_type (G_BUS_TYPE_SESSION, object_path) ||
+ try_bus_type (G_BUS_TYPE_SYSTEM, object_path))
+ {
+ set_backend_functions (backend);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
void
grd_pcsc_lib_backend_cleanup_grd_pcscd_proxy (void)
{
diff --git a/src/grd-rdp-smartcard.c b/src/grd-rdp-smartcard.c
index 746a7abb..c73858ad 100644
--- a/src/grd-rdp-smartcard.c
+++ b/src/grd-rdp-smartcard.c
@@ -30,6 +30,7 @@
#include <glib/gstdio.h>
#include "grd-context.h"
+#include "grd-daemon-utils.h"
#include "grd-dbus-pcscd.h"
#include "grd-private.h"
#include "grd-rdp-device-redirection.h"
@@ -91,6 +92,10 @@ struct _GrdRdpSmartcard
/* Private peer-to-peer proxy for system-level clients (via grd-pcscd) */
GCancellable *private_proxy_cancellable;
GrdDBusPcscdSession *private_proxy;
+
+ /* Session proxy for session-level clients (direct access) */
+ GrdDBusPcscdSession *session_proxy;
+ unsigned int session_bus_name_id;
};
G_DEFINE_TYPE (GrdRdpSmartcard, grd_rdp_smartcard, G_TYPE_OBJECT)
@@ -1807,16 +1812,115 @@ teardown_private_proxy (GrdRdpSmartcard *smartcard)
g_clear_object (&smartcard->pcscd_proxy);
}
+static char *
+get_session_id (void)
+{
+ const char *session_id;
+ char *logind_session_id = NULL;
+
+ session_id = g_getenv ("XDG_SESSION_ID");
+ if (session_id && session_id[0] != '\0')
+ return g_strdup (session_id);
+
+ logind_session_id = grd_get_session_id_from_pid (getpid ());
+ if (logind_session_id)
+ return logind_session_id;
+
+ logind_session_id = grd_get_session_id_from_uid (getuid ());
+ if (!logind_session_id)
+ g_warning ("[RDP.SMARTCARD] Failed to get session ID");
+
+ return logind_session_id;
+}
+
+static void
+on_session_bus_acquired (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ g_autoptr (GrdDBusPcscdSession) session_proxy = NULL;
+ g_autoptr (GError) error = NULL;
+ g_autofree char *session_id = NULL;
+ g_autofree char *object_path = NULL;
+ GrdRdpSmartcard *smartcard = user_data;
+
+ session_id = get_session_id ();
+ if (!session_id)
+ return;
+
+ object_path =
+ g_strdup_printf (REMOTE_DESKTOP_PCSCD_OBJECT_PATH "/%s", session_id);
+
+ session_proxy = grd_dbus_pcscd_session_skeleton_new ();
+ if (!g_dbus_interface_skeleton_export (
+ G_DBUS_INTERFACE_SKELETON (session_proxy),
+ connection,
+ object_path,
+ &error))
+ {
+ g_warning ("[RDP.SMARTCARD] Failed to export session proxy: %s",
+ error->message);
+ return;
+ }
+
+ connect_pcscd_session_handlers (session_proxy, smartcard);
+
+ smartcard->session_proxy = g_steal_pointer (&session_proxy);
+
+ g_debug ("[RDP.SMARTCARD] Session bus proxy established at %s", object_path);
+}
+
+static void
+on_session_bus_name_acquired (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ g_debug ("[RDP.SMARTCARD] Acquired session bus name %s", name);
+}
+
+static void
+on_session_bus_name_lost (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ g_debug ("[RDP.SMARTCARD] Lost session bus name %s", name);
+}
+
+static void
+setup_session_proxy (GrdRdpSmartcard *smartcard)
+{
+ g_assert (!smartcard->session_bus_name_id);
+
+ smartcard->session_bus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+ REMOTE_DESKTOP_PCSCD_BUS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ on_session_bus_acquired,
+ on_session_bus_name_acquired,
+ on_session_bus_name_lost,
+ smartcard,
+ NULL);
+}
+
+static void
+teardown_session_proxy (GrdRdpSmartcard *smartcard)
+{
+ g_clear_handle_id (&smartcard->session_bus_name_id, g_bus_unown_name);
+
+ unexport_and_fail_pending_invocations (smartcard, &smartcard->session_proxy);
+}
+
static void
setup_pcscd_proxies (GrdRdpSmartcard *smartcard)
{
setup_private_proxy (smartcard);
+ setup_session_proxy (smartcard);
}
static void
teardown_pcscd_proxies (GrdRdpSmartcard *smartcard)
{
teardown_private_proxy (smartcard);
+ teardown_session_proxy (smartcard);
}
static UINT
--
2.51.0
From 50454a9bb9b81c269b72c2479ab144279f408631 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 8 Jul 2026 02:04:51 +0200
Subject: [PATCH 07/11] rdp-smartcard: Add libpcsclite fallback backend to
libgrdpcsc
When libgrdpcsc cannot connect to grd-pcscd (e.g. no smartcard is
being redirected), fall back to loading the real libpcsclite.so.1 via
dlopen and forwarding all PC/SC calls to it. This allows libgrdpcsc to
be configured as the default provider_library without breaking local
smartcard usage.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/merge_requests/406>
---
src/grd-pcsc-lib-backend-libpcsclite.c | 78 ++++++++++++++++++++++++++
src/grd-pcsc-lib-backend.h | 4 ++
src/grd-pcsc-lib.c | 4 ++
src/meson.build | 1 +
4 files changed, 87 insertions(+)
create mode 100644 src/grd-pcsc-lib-backend-libpcsclite.c
diff --git a/src/grd-pcsc-lib-backend-libpcsclite.c b/src/grd-pcsc-lib-backend-libpcsclite.c
new file mode 100644
index 00000000..624cece9
--- /dev/null
+++ b/src/grd-pcsc-lib-backend-libpcsclite.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2026 Red Hat Inc.
+ *
+ * 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 of the
+ * License, or (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ * Joan Torres Lopez <joantolo@redhat.com>
+ */
+
+#include "config.h"
+
+#include "grd-pcsc-lib-backend.h"
+
+#include <dlfcn.h>
+
+#define LIBPCSCLITE "libpcsclite.so.1"
+
+#define LOAD_SYMBOL(backend, member, handle) \
+ do \
+ { \
+ (backend)->member = dlsym ((handle), #member); \
+ if (!(backend)->member) \
+ { \
+ g_clear_pointer (&(handle), dlclose); \
+ return FALSE; \
+ } \
+ } \
+ while (0)
+
+static void *lib_handle;
+
+gboolean
+grd_pcsc_lib_backend_init_libpcsclite (GrdPcscLibBackend *backend)
+{
+ lib_handle = dlopen (LIBPCSCLITE, RTLD_LAZY);
+ if (!lib_handle)
+ return FALSE;
+
+ LOAD_SYMBOL (backend, SCardEstablishContext, lib_handle);
+ LOAD_SYMBOL (backend, SCardReleaseContext, lib_handle);
+ LOAD_SYMBOL (backend, SCardIsValidContext, lib_handle);
+ LOAD_SYMBOL (backend, SCardConnect, lib_handle);
+ LOAD_SYMBOL (backend, SCardReconnect, lib_handle);
+ LOAD_SYMBOL (backend, SCardDisconnect, lib_handle);
+ LOAD_SYMBOL (backend, SCardBeginTransaction, lib_handle);
+ LOAD_SYMBOL (backend, SCardEndTransaction, lib_handle);
+ LOAD_SYMBOL (backend, SCardStatus, lib_handle);
+ LOAD_SYMBOL (backend, SCardGetStatusChange, lib_handle);
+ LOAD_SYMBOL (backend, SCardControl, lib_handle);
+ LOAD_SYMBOL (backend, SCardTransmit, lib_handle);
+ LOAD_SYMBOL (backend, SCardListReaderGroups, lib_handle);
+ LOAD_SYMBOL (backend, SCardListReaders, lib_handle);
+ LOAD_SYMBOL (backend, SCardFreeMemory, lib_handle);
+ LOAD_SYMBOL (backend, SCardCancel, lib_handle);
+ LOAD_SYMBOL (backend, SCardGetAttrib, lib_handle);
+ LOAD_SYMBOL (backend, SCardSetAttrib, lib_handle);
+
+ return TRUE;
+}
+
+void
+grd_pcsc_lib_backend_cleanup_libpcsclite (void)
+{
+ g_clear_pointer (&lib_handle, dlclose);
+}
diff --git a/src/grd-pcsc-lib-backend.h b/src/grd-pcsc-lib-backend.h
index 47c6a13a..0fa01551 100644
--- a/src/grd-pcsc-lib-backend.h
+++ b/src/grd-pcsc-lib-backend.h
@@ -135,3 +135,7 @@ void grd_pcsc_lib_backend_set_default (GrdPcscLibBackend *backend);
gboolean grd_pcsc_lib_backend_init_grd_pcscd_proxy (GrdPcscLibBackend *backend);
void grd_pcsc_lib_backend_cleanup_grd_pcscd_proxy (void);
+
+gboolean grd_pcsc_lib_backend_init_libpcsclite (GrdPcscLibBackend *backend);
+
+void grd_pcsc_lib_backend_cleanup_libpcsclite (void);
diff --git a/src/grd-pcsc-lib.c b/src/grd-pcsc-lib.c
index 94ae36c3..7dda0627 100644
--- a/src/grd-pcsc-lib.c
+++ b/src/grd-pcsc-lib.c
@@ -33,6 +33,7 @@ PCSC_DESTRUCTOR static void
on_unload (void)
{
grd_pcsc_lib_backend_cleanup_grd_pcscd_proxy ();
+ grd_pcsc_lib_backend_cleanup_libpcsclite ();
}
static void
@@ -43,6 +44,9 @@ init_grd_pcsc_backend (void)
if (grd_pcsc_lib_backend_init_grd_pcscd_proxy (&backend))
return;
+ if (grd_pcsc_lib_backend_init_libpcsclite (&backend))
+ return;
+
g_warning ("[PCSC] No backend available");
}
diff --git a/src/meson.build b/src/meson.build
index f3087945..890ebca4 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -460,6 +460,7 @@ if have_rdp
'grd-pcsc-lib.c',
'grd-pcsc-lib-backend.c',
'grd-pcsc-lib-backend-grd-pcscd-proxy.c',
+ 'grd-pcsc-lib-backend-libpcsclite.c',
dbus_gen_pcscd,
dependencies: [glib_dep,
gio_dep,
--
2.51.0
From fb42f2fa75469def41b47e0ac6afccaa897de619 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 8 Jul 2026 02:09:43 +0200
Subject: [PATCH 08/11] pcscd-session: Add D-Bus method authorization
Restrict access to the Pcscd.Session interface exported on the system
bus. A caller is authorized if its UID matches the session owner or
root, or if it holds the org.gnome.remotedesktop.use-grd-pcscd polkit
action. The latter allows services like sssd that run under a different
UID to access the redirected smartcard by adding a polkit rule.
Authorized senders are cached per session to avoid the polkit roundtrip
on every PC/SC call, since the high volume of operations would
otherwise add significant latency. The cache is cleared when polkit
signals that its rules have changed.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/merge_requests/406>
---
data/meson.build | 7 ++
...nome.remotedesktop.use-grd-pcscd.policy.in | 21 ++++
src/grd-pcscd-session.c | 112 ++++++++++++++++++
src/meson.build | 3 +-
4 files changed, 142 insertions(+), 1 deletion(-)
create mode 100644 data/org.gnome.remotedesktop.use-grd-pcscd.policy.in
diff --git a/data/meson.build b/data/meson.build
index b81acf5c..4c16d890 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -118,6 +118,13 @@ if get_option('systemd')
},
install_dir: join_paths(datadir, 'polkit-1', 'actions'))
+ configure_file(input: 'org.gnome.remotedesktop.use-grd-pcscd.policy.in',
+ output: 'org.gnome.remotedesktop.use-grd-pcscd.policy',
+ configuration: {
+ 'GRD_USERNAME': grd_username,
+ },
+ install_dir: join_paths(datadir, 'polkit-1', 'actions'))
+
configure_file(input: 'org.gnome.RemoteDesktop.Pcscd.service.in',
output: 'org.gnome.RemoteDesktop.Pcscd.service',
configuration: {
diff --git a/data/org.gnome.remotedesktop.use-grd-pcscd.policy.in b/data/org.gnome.remotedesktop.use-grd-pcscd.policy.in
new file mode 100644
index 00000000..d4efe1c9
--- /dev/null
+++ b/data/org.gnome.remotedesktop.use-grd-pcscd.policy.in
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+<policyconfig>
+
+ <vendor>The GNOME Project</vendor>
+ <vendor_url>http://www.gnome.org/</vendor_url>
+
+ <action id="org.gnome.remotedesktop.use-grd-pcscd">
+ <description>Allow to use gnome-remote-desktop's PC/SC smart card session</description>
+ <message>Authentication is required to use gnome-remote-desktop's PC/SC smart card session</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:@GRD_USERNAME@</annotate>
+ </action>
+
+</policyconfig>
diff --git a/src/grd-pcscd-session.c b/src/grd-pcscd-session.c
index aa45ac2f..0cc3e942 100644
--- a/src/grd-pcscd-session.c
+++ b/src/grd-pcscd-session.c
@@ -27,10 +27,12 @@
#include <gio/gunixinputstream.h>
#include <gio/gunixoutputstream.h>
#include <glib/gstdio.h>
+#include <polkit/polkit.h>
#include "grd-dbus-pcscd.h"
#define PCSCD_OBJECT_PATH_PREFIX "/org/gnome/RemoteDesktop/Pcscd"
+#define GRD_USE_GRD_PCSCD_POLKIT_ACTION "org.gnome.remotedesktop.use-grd-pcscd"
struct _GrdPcscdSession
{
@@ -38,6 +40,9 @@ struct _GrdPcscdSession
char *session_id;
+ PolkitAuthority *authority;
+ GHashTable *authorized_senders;
+
GCancellable *cancellable;
GrdDBusPcscdSession *private_proxy;
@@ -55,6 +60,105 @@ static guint signals[N_SIGNALS];
G_DEFINE_TYPE (GrdPcscdSession, grd_pcscd_session, G_TYPE_OBJECT)
+static void
+on_polkit_authority_changed (PolkitAuthority *authority,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+
+ g_debug ("[PCSCD.SESSION %s] Polkit rules changed, clearing authorization cache",
+ session->session_id);
+ g_hash_table_remove_all (session->authorized_senders);
+}
+
+static gboolean
+ensure_polkit_authority (GrdPcscdSession *session)
+{
+ g_autoptr (GError) error = NULL;
+
+ if (session->authority)
+ return TRUE;
+
+ session->authority = polkit_authority_get_sync (session->cancellable, &error);
+ if (!session->authority)
+ {
+ g_warning ("[PCSCD.SESSION %s] Failed to get polkit authority: %s",
+ session->session_id, error->message);
+ return FALSE;
+ }
+
+ g_signal_connect_object (session->authority, "changed",
+ G_CALLBACK (on_polkit_authority_changed),
+ session, G_CONNECT_DEFAULT);
+
+ return TRUE;
+}
+
+static gboolean
+check_polkit_action (GrdPcscdSession *session,
+ const char *sender,
+ const char *action)
+{
+ g_autoptr (PolkitAuthorizationResult) result = NULL;
+ g_autoptr (PolkitSubject) subject = NULL;
+ g_autoptr (GError) error = NULL;
+
+ subject = polkit_system_bus_name_new (sender);
+ result = polkit_authority_check_authorization_sync (session->authority,
+ subject,
+ action,
+ NULL,
+ POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE,
+ session->cancellable,
+ &error);
+ if (!result)
+ {
+ g_warning ("[PCSCD.SESSION %s] Failed to check authorization for %s: %s",
+ session->session_id, action, error->message);
+ return FALSE;
+ }
+
+ return polkit_authorization_result_get_is_authorized (result);
+}
+
+static gboolean
+check_polkit (GrdPcscdSession *session,
+ const char *sender)
+{
+ if (!ensure_polkit_authority (session))
+ return FALSE;
+
+ return check_polkit_action (session, sender,
+ GRD_USE_GRD_PCSCD_POLKIT_ACTION);
+}
+
+static gboolean
+on_authorize_method (GDBusInterfaceSkeleton *interface,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ GrdPcscdSession *session = user_data;
+ const char *sender;
+
+ sender = g_dbus_method_invocation_get_sender (invocation);
+
+ if (g_hash_table_contains (session->authorized_senders, sender))
+ return TRUE;
+
+ if (!check_polkit (session, sender))
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ "Not authorized");
+ return FALSE;
+ }
+
+ g_hash_table_add (session->authorized_senders, g_strdup (sender));
+
+ return TRUE;
+}
+
static void
on_establish_context_finished (GObject *source_object,
GAsyncResult *result,
@@ -965,6 +1069,9 @@ create_system_proxy (GrdPcscdSession *session,
error))
return FALSE;
+ g_signal_connect_object (system_proxy, "g-authorize-method",
+ G_CALLBACK (on_authorize_method),
+ session, G_CONNECT_DEFAULT);
g_signal_connect_object (system_proxy, "handle-establish-context",
G_CALLBACK (on_handle_establish_context),
session, G_CONNECT_DEFAULT);
@@ -1066,6 +1173,8 @@ grd_pcscd_session_new (const char *session_id,
session = g_object_new (GRD_TYPE_PCSCD_SESSION, NULL);
session->session_id = g_strdup (session_id);
+ session->authorized_senders = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
session->cancellable = g_cancellable_new ();
if (!create_system_proxy (session, system_connection, error))
@@ -1087,6 +1196,9 @@ grd_pcscd_session_dispose (GObject *object)
g_cancellable_cancel (session->cancellable);
g_clear_object (&session->cancellable);
+ g_clear_object (&session->authority);
+ g_clear_pointer (&session->authorized_senders, g_hash_table_destroy);
+
if (session->private_proxy)
{
GDBusConnection *private_connection =
diff --git a/src/meson.build b/src/meson.build
index 890ebca4..e665770a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -451,7 +451,8 @@ if have_rdp
dependencies: [glib_dep,
gio_dep,
gio_unix_dep,
- libsystemd_dep],
+ libsystemd_dep,
+ polkit_dep],
include_directories: [configinc],
install: true,
install_dir: libexecdir)
--
2.51.0
From 419a1fe61128e46c749d7e0660fbbd717a8030d2 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 8 Jul 2026 03:04:10 +0200
Subject: [PATCH 09/11] ci: Add WITHOUT_WINPR_3x_DEPRECATED flag
Build WinPR without 3.x deprecated symbols to avoid deprecation
warnings during compilation.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/merge_requests/406>
---
config.h.meson | 1 +
meson.build | 1 +
2 files changed, 2 insertions(+)
diff --git a/config.h.meson b/config.h.meson
index cafba4e..d77b696 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -35,3 +35,4 @@
/* Workaround for https://github.com/FreeRDP/FreeRDP/issues/11412 */
#mesondefine WITHOUT_FREERDP_3x_DEPRECATED
+#mesondefine WITHOUT_WINPR_3x_DEPRECATED
diff --git a/meson.build b/meson.build
index 5e91d25..3b02e28 100644
--- a/meson.build
+++ b/meson.build
@@ -117,6 +117,7 @@ cdata.set_quoted('GRD_SHADER_DIR', grd_shaderdir)
# Workaround for https://github.com/FreeRDP/FreeRDP/issues/11412
cdata.set('WITHOUT_FREERDP_3x_DEPRECATED', true)
+cdata.set('WITHOUT_WINPR_3x_DEPRECATED', true)
configure_file(input: 'config.h.meson',
output: 'config.h',
From e816e9773d1f0fc70c08a5276cfd49e4824dbd00 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Mon, 17 Aug 2026 22:43:59 +0200
Subject: [PATCH 10/11] rdp-smartcard: Populate hContext for card-handle
operations
PC/SC APIs like SCardControl only take a card handle since the
context is implicit, but MS-RDPESC requires every card-handle
operation to send both hContext and hCard explicitly. All nine
operations only set hCard, causing mstsc to disconnect. Track each
card's context in a new card_to_context hash table, populated on
Connect and purged on Disconnect/ReleaseContext.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/merge_requests/406>
---
src/grd-rdp-smartcard.c | 158 +++++++++++++++++++++++++++++++++++-----
1 file changed, 141 insertions(+), 17 deletions(-)
diff --git a/src/grd-rdp-smartcard.c b/src/grd-rdp-smartcard.c
index c73858ad..4abda0a9 100644
--- a/src/grd-rdp-smartcard.c
+++ b/src/grd-rdp-smartcard.c
@@ -48,6 +48,14 @@
(uint32_t) (val)) \
: (uint32_t) (val)))
+#define INT64_TO_POINTER(val) \
+ (G_STATIC_ASSERT_EXPR (sizeof (val) == sizeof (gpointer)), \
+ (gpointer) (val))
+
+#define POINTER_TO_INT64(ptr) \
+ (G_STATIC_ASSERT_EXPR (sizeof (ptr) == sizeof (int64_t)), \
+ (int64_t) (ptr))
+
/*
* pcsc-lite dwState bitmask values. These differ from the WinSCard
* sequential values (0-6) defined by FreeRDP/WinPR in winpr/smartcard.h.
@@ -96,6 +104,8 @@ struct _GrdRdpSmartcard
/* Session proxy for session-level clients (direct access) */
GrdDBusPcscdSession *session_proxy;
unsigned int session_bus_name_id;
+
+ GHashTable *card_to_context;
};
G_DEFINE_TYPE (GrdRdpSmartcard, grd_rdp_smartcard, G_TYPE_OBJECT)
@@ -279,6 +289,14 @@ on_smartcard_release_context_complete (RdpdrServerContext *rdpdr_context,
g_dbus_method_invocation_return_value (invocation, ret_variant);
}
+static gboolean
+remove_cards_for_context (gpointer key,
+ gpointer card_context,
+ gpointer context)
+{
+ return POINTER_TO_INT64 (card_context) == POINTER_TO_INT64 (context);
+}
+
static gboolean
on_handle_release_context (GrdDBusPcscdSession *skeleton,
GDBusMethodInvocation *invocation,
@@ -300,6 +318,11 @@ on_handle_release_context (GrdDBusPcscdSession *skeleton,
g_hash_table_add (smartcard->pending_invocations, invocation);
g_variant_get (call_variant, "(x)", &context);
+
+ g_hash_table_foreach_remove (smartcard->card_to_context,
+ remove_cards_for_context,
+ INT64_TO_POINTER (context));
+
smartcard_scard_context_native_to_redir (&redir_context, context);
status = rdpdr_context->SmartcardReleaseContext (rdpdr_context,
@@ -380,6 +403,30 @@ on_handle_is_valid_context (GrdDBusPcscdSession *skeleton,
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
+static void
+track_card_context (RdpdrServerContext *rdpdr_context,
+ int64_t card,
+ int64_t context)
+{
+ GrdRdpDeviceRedirection *device_redirection = rdpdr_context->data;
+ GrdRdpSmartcard *smartcard;
+
+ smartcard = grd_rdp_device_redirection_get_smartcard (device_redirection);
+ if (smartcard)
+ {
+ g_hash_table_insert (smartcard->card_to_context,
+ INT64_TO_POINTER (card),
+ INT64_TO_POINTER (context));
+ }
+}
+
+static void
+untrack_card_context (GrdRdpSmartcard *smartcard,
+ int64_t card)
+{
+ g_hash_table_remove (smartcard->card_to_context, INT64_TO_POINTER (card));
+}
+
static void
on_smartcard_connect_complete (RdpdrServerContext *rdpdr_context,
void *callback_data,
@@ -405,6 +452,9 @@ on_smartcard_connect_complete (RdpdrServerContext *rdpdr_context,
(int64_t) smartcard_scard_handle_native_from_redir (&ret->hCard);
active_protocol =
EXTEND_32_TO_64 (convert_protocol_to_pcsc (ret->dwActiveProtocol));
+
+ if (card != 0)
+ track_card_context (rdpdr_context, card, out_context);
}
ret_variant = g_variant_new ("((xxxt))",
@@ -494,6 +544,30 @@ on_smartcard_reconnect_complete (RdpdrServerContext *rdpdr_context,
g_dbus_method_invocation_return_value (invocation, ret_variant);
}
+static gboolean
+resolve_context_for_card (GrdRdpSmartcard *smartcard,
+ GDBusMethodInvocation *invocation,
+ int64_t card,
+ int64_t *out_context)
+{
+ gpointer value;
+
+ if (!g_hash_table_lookup_extended (smartcard->card_to_context,
+ INT64_TO_POINTER (card),
+ NULL,
+ &value))
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "No context found for card handle");
+ return FALSE;
+ }
+
+ *out_context = POINTER_TO_INT64 (value);
+ return TRUE;
+}
+
static gboolean
on_handle_reconnect (GrdDBusPcscdSession *skeleton,
GDBusMethodInvocation *invocation,
@@ -504,6 +578,7 @@ on_handle_reconnect (GrdDBusPcscdSession *skeleton,
RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
Reconnect_Call call = {};
int64_t card;
+ int64_t context;
uint64_t share_mode;
uint64_t preferred_protocols;
uint64_t initialization;
@@ -515,14 +590,18 @@ on_handle_reconnect (GrdDBusPcscdSession *skeleton,
if (is_device_unavailable (smartcard, invocation))
return G_DBUS_METHOD_INVOCATION_HANDLED;
- g_hash_table_add (smartcard->pending_invocations, invocation);
-
g_variant_get (call_variant, "(xttt)",
&card,
&share_mode,
&preferred_protocols,
&initialization);
+ if (!resolve_context_for_card (smartcard, invocation, card, &context))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ smartcard_scard_context_native_to_redir (&call.handles.hContext, context);
smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
call.dwShareMode = NARROW_64_TO_32 (share_mode);
call.dwPreferredProtocols =
@@ -575,6 +654,7 @@ on_handle_disconnect_card (GrdDBusPcscdSession *skeleton,
RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
HCardAndDisposition_Call call = {};
int64_t card;
+ int64_t context;
uint64_t disposition;
uint32_t completion_id;
uint32_t status;
@@ -584,10 +664,16 @@ on_handle_disconnect_card (GrdDBusPcscdSession *skeleton,
if (is_device_unavailable (smartcard, invocation))
return G_DBUS_METHOD_INVOCATION_HANDLED;
+ g_variant_get (call_variant, "(xt)", &card, &disposition);
+
+ if (!resolve_context_for_card (smartcard, invocation, card, &context))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
g_hash_table_add (smartcard->pending_invocations, invocation);
- g_variant_get (call_variant, "(xt)", &card, &disposition);
+ untrack_card_context (smartcard, card);
+ smartcard_scard_context_native_to_redir (&call.handles.hContext, context);
smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
call.dwDisposition = NARROW_64_TO_32 (disposition);
@@ -638,6 +724,7 @@ on_handle_begin_transaction (GrdDBusPcscdSession *skeleton,
RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
HCardAndDisposition_Call call = {};
int64_t card;
+ int64_t context;
uint32_t completion_id;
uint32_t status;
@@ -646,10 +733,14 @@ on_handle_begin_transaction (GrdDBusPcscdSession *skeleton,
if (is_device_unavailable (smartcard, invocation))
return G_DBUS_METHOD_INVOCATION_HANDLED;
- g_hash_table_add (smartcard->pending_invocations, invocation);
-
g_variant_get (call_variant, "(x)", &card);
+ if (!resolve_context_for_card (smartcard, invocation, card, &context))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ smartcard_scard_context_native_to_redir (&call.handles.hContext, context);
smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
status = rdpdr_context->SmartcardBeginTransaction (rdpdr_context,
@@ -699,6 +790,7 @@ on_handle_end_transaction (GrdDBusPcscdSession *skeleton,
RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
HCardAndDisposition_Call call = {};
int64_t card;
+ int64_t context;
uint64_t disposition;
uint32_t completion_id;
uint32_t status;
@@ -708,10 +800,14 @@ on_handle_end_transaction (GrdDBusPcscdSession *skeleton,
if (is_device_unavailable (smartcard, invocation))
return G_DBUS_METHOD_INVOCATION_HANDLED;
- g_hash_table_add (smartcard->pending_invocations, invocation);
-
g_variant_get (call_variant, "(xt)", &card, &disposition);
+ if (!resolve_context_for_card (smartcard, invocation, card, &context))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ smartcard_scard_context_native_to_redir (&call.handles.hContext, context);
smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
call.dwDisposition = NARROW_64_TO_32 (disposition);
@@ -781,6 +877,7 @@ on_handle_status_card (GrdDBusPcscdSession *skeleton,
RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
Status_Call call = {};
int64_t card;
+ int64_t context;
uint64_t reader_len;
uint64_t atr_len;
uint32_t completion_id;
@@ -791,10 +888,14 @@ on_handle_status_card (GrdDBusPcscdSession *skeleton,
if (is_device_unavailable (smartcard, invocation))
return G_DBUS_METHOD_INVOCATION_HANDLED;
- g_hash_table_add (smartcard->pending_invocations, invocation);
-
g_variant_get (call_variant, "(xtt)", &card, &reader_len, &atr_len);
+ if (!resolve_context_for_card (smartcard, invocation, card, &context))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ smartcard_scard_context_native_to_redir (&call.handles.hContext, context);
smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
call.cchReaderLen = NARROW_64_TO_32 (reader_len);
call.cbAtrLen = NARROW_64_TO_32 (atr_len);
@@ -968,6 +1069,7 @@ on_handle_control_card (GrdDBusPcscdSession *skeleton,
g_autoptr (GVariant) send_buffer = NULL;
Control_Call call = {};
int64_t card;
+ int64_t context;
uint64_t control_code;
uint64_t send_buffer_size;
uint64_t recv_buffer_size;
@@ -981,8 +1083,6 @@ on_handle_control_card (GrdDBusPcscdSession *skeleton,
if (is_device_unavailable (smartcard, invocation))
return G_DBUS_METHOD_INVOCATION_HANDLED;
- g_hash_table_add (smartcard->pending_invocations, invocation);
-
g_variant_get (call_variant, "(xt@aytt)",
&card,
&control_code,
@@ -990,9 +1090,15 @@ on_handle_control_card (GrdDBusPcscdSession *skeleton,
&send_buffer_size,
&recv_buffer_size);
+ if (!resolve_context_for_card (smartcard, invocation, card, &context))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
send_data = g_variant_get_fixed_array (send_buffer, &send_size,
sizeof (uint8_t));
+ smartcard_scard_context_native_to_redir (&call.handles.hContext, context);
smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
call.dwControlCode = NARROW_64_TO_32 (control_code);
call.cbInBufferSize = NARROW_64_TO_32 (send_buffer_size);
@@ -1068,6 +1174,7 @@ on_handle_transmit (GrdDBusPcscdSession *skeleton,
SCARD_IO_REQUEST recv_pci = {};
Transmit_Call call = {};
int64_t card;
+ int64_t context;
uint64_t send_pci_protocol;
uint64_t recv_pci_protocol_in;
uint64_t recv_buffer_size;
@@ -1081,8 +1188,6 @@ on_handle_transmit (GrdDBusPcscdSession *skeleton,
if (is_device_unavailable (smartcard, invocation))
return G_DBUS_METHOD_INVOCATION_HANDLED;
- g_hash_table_add (smartcard->pending_invocations, invocation);
-
g_variant_get (call_variant, "(xt@aytt)",
&card,
&send_pci_protocol,
@@ -1090,6 +1195,11 @@ on_handle_transmit (GrdDBusPcscdSession *skeleton,
&recv_pci_protocol_in,
&recv_buffer_size);
+ if (!resolve_context_for_card (smartcard, invocation, card, &context))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
send_data = g_variant_get_fixed_array (send_buffer, &send_size,
sizeof (uint8_t));
@@ -1101,6 +1211,7 @@ on_handle_transmit (GrdDBusPcscdSession *skeleton,
convert_protocol_to_winscard (NARROW_64_TO_32 (recv_pci_protocol_in));
recv_pci.cbPciLength = sizeof (SCARD_IO_REQUEST);
+ smartcard_scard_context_native_to_redir (&call.handles.hContext, context);
smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
call.pioSendPci = &send_pci;
call.cbSendLength = NARROW_64_TO_32 (send_size);
@@ -1382,6 +1493,7 @@ on_handle_get_attrib (GrdDBusPcscdSession *skeleton,
RdpdrServerContext *rdpdr_context = smartcard->rdpdr_context;
GetAttrib_Call call = {};
int64_t card;
+ int64_t context;
uint64_t attr_id;
uint64_t attr_len;
uint32_t completion_id;
@@ -1392,10 +1504,14 @@ on_handle_get_attrib (GrdDBusPcscdSession *skeleton,
if (is_device_unavailable (smartcard, invocation))
return G_DBUS_METHOD_INVOCATION_HANDLED;
- g_hash_table_add (smartcard->pending_invocations, invocation);
-
g_variant_get (call_variant, "(xtt)", &card, &attr_id, &attr_len);
+ if (!resolve_context_for_card (smartcard, invocation, card, &context))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
+ smartcard_scard_context_native_to_redir (&call.handles.hContext, context);
smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
call.dwAttrId = NARROW_64_TO_32 (attr_id);
call.cbAttrLen = NARROW_64_TO_32 (attr_len);
@@ -1447,6 +1563,7 @@ on_handle_set_attrib (GrdDBusPcscdSession *skeleton,
g_autoptr (GVariant) attr_buffer = NULL;
SetAttrib_Call call = {};
int64_t card;
+ int64_t context;
uint64_t attr_id;
const uint8_t *attr_data;
size_t attr_size;
@@ -1458,16 +1575,20 @@ on_handle_set_attrib (GrdDBusPcscdSession *skeleton,
if (is_device_unavailable (smartcard, invocation))
return G_DBUS_METHOD_INVOCATION_HANDLED;
- g_hash_table_add (smartcard->pending_invocations, invocation);
-
g_variant_get (call_variant, "(xt@ay)",
&card,
&attr_id,
&attr_buffer);
+ if (!resolve_context_for_card (smartcard, invocation, card, &context))
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+
+ g_hash_table_add (smartcard->pending_invocations, invocation);
+
attr_data = g_variant_get_fixed_array (attr_buffer, &attr_size,
sizeof (uint8_t));
+ smartcard_scard_context_native_to_redir (&call.handles.hContext, context);
smartcard_scard_handle_native_to_redir (&call.handles.hCard, card);
call.dwAttrId = NARROW_64_TO_32 (attr_id);
call.cbAttrLen = attr_size;
@@ -1921,6 +2042,7 @@ teardown_pcscd_proxies (GrdRdpSmartcard *smartcard)
{
teardown_private_proxy (smartcard);
teardown_session_proxy (smartcard);
+ g_hash_table_remove_all (smartcard->card_to_context);
}
static UINT
@@ -2064,6 +2186,7 @@ grd_rdp_smartcard_dispose (GObject *object)
g_clear_pointer (&smartcard->smartcard_device_ids, g_hash_table_unref);
g_clear_pointer (&smartcard->pending_invocations, g_hash_table_unref);
+ g_clear_pointer (&smartcard->card_to_context, g_hash_table_unref);
G_OBJECT_CLASS (grd_rdp_smartcard_parent_class)->dispose (object);
}
@@ -2085,6 +2208,7 @@ grd_rdp_smartcard_init (GrdRdpSmartcard *smartcard)
smartcard->smartcard_device_ids = g_hash_table_new (NULL, NULL);
smartcard->pending_invocations = g_hash_table_new (NULL, NULL);
+ smartcard->card_to_context = g_hash_table_new (NULL, NULL);
}
static void
--
2.51.0
From 2afaced792b91874eb71dc0d0338d295f844601c Mon Sep 17 00:00:00 2001
From: Pascal Nowack <Pascal.Nowack@gmx.de>
Date: Sun, 10 May 2026 19:14:08 +0200
Subject: [PATCH 11/11] build: Add support for FreeRDP 3.25.0
FreeRDP 3.25.0 introduced an API break on the client side, requiring the
test client to exchange the old authenticate callback with a revised
version. Since it is not possible to increase dependencies on the stable
branch, simply check the installed FreeRDP version at build time and use
the correct API depending on it.
As the API change only affects the test client, it should not matter if
a distribution decides to update FreeRDP to 3.25 or later when
gnome-remote-desktop was already built.
Closes: https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/work_items/326
Part-of: <https://gitlab.gnome.org/GNOME/gnome-remote-desktop/-/merge_requests/401>
---
tests/test-client-rdp.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/tests/test-client-rdp.c b/tests/test-client-rdp.c
index a06a1879..b97e6fa3 100644
--- a/tests/test-client-rdp.c
+++ b/tests/test-client-rdp.c
@@ -24,6 +24,7 @@
#include <freerdp/client/cmdline.h>
#include <freerdp/freerdp.h>
#include <freerdp/gdi/gfx.h>
+#include <freerdp/version.h>
#include <glib.h>
#define TEST_1_WIDTH 800
@@ -246,10 +247,18 @@ rdp_client_post_disconnect (freerdp *instance)
}
static BOOL
+#if FREERDP_VERSION_MAJOR == 3 && FREERDP_VERSION_MINOR >= 25
+rdp_client_authenticate_ex (freerdp *instance,
+ char **username,
+ char **password,
+ char **domain,
+ rdp_auth_reason reason)
+#else
rdp_client_authenticate (freerdp *instance,
char **username,
char **password,
char **domain)
+#endif /* FREERDP_VERSION_MAJOR == 3 && FREERDP_VERSION_MINOR >= 25 */
{
/* Credentials were already parsed from the command line */
@@ -307,7 +316,11 @@ rdp_client_new (freerdp *instance,
instance->PreConnect = rdp_client_pre_connect;
instance->PostConnect = rdp_client_post_connect;
instance->PostDisconnect = rdp_client_post_disconnect;
+#if FREERDP_VERSION_MAJOR == 3 && FREERDP_VERSION_MINOR >= 25
+ instance->AuthenticateEx = rdp_client_authenticate_ex;
+#else
instance->Authenticate = rdp_client_authenticate;
+#endif /* FREERDP_VERSION_MAJOR == 3 && FREERDP_VERSION_MINOR >= 25 */
instance->VerifyCertificateEx = rdp_client_verify_certificate_ex;
instance->VerifyChangedCertificateEx = rdp_client_verify_changed_certificate_ex;
--
2.51.0