7534 lines
259 KiB
Diff
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, ¤t_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
|
|
|