2559 lines
73 KiB
C
2559 lines
73 KiB
C
/**
|
|
* xrdp: A Remote Desktop Protocol server.
|
|
*
|
|
* Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com
|
|
* Copyright (C) Jay Sorg 2013 jay.sorg@gmail.com
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* smartcard redirection support
|
|
*/
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include <config_ac.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include "os_calls.h"
|
|
#include "string_calls.h"
|
|
#include "smartcard.h"
|
|
#include "log.h"
|
|
#include "irp.h"
|
|
#include "devredir.h"
|
|
#include "smartcard_pcsc.h"
|
|
#include "chansrv.h"
|
|
|
|
/*
|
|
* TODO
|
|
*
|
|
* o ensure that all wide calls are handled correctly
|
|
*
|
|
* o need to query client for build number and determine whether we should use
|
|
* SCREDIR_VERSION_XP or SCREDIR_VERSION_LONGHORN
|
|
*
|
|
* o need to call scard_release_resources()
|
|
*
|
|
* o why is win 7 sending SCARD_IOCTL_ACCESS_STARTED_EVENT first
|
|
* 0000 00 01 00 00 04 00 00 00 e0 00 09 00 00 00 00 00 ................
|
|
* 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
|
* 0020 28 b7 9d 02
|
|
*/
|
|
|
|
/*
|
|
* Notes:
|
|
*
|
|
* XP and Server 2003 use version SCREDIR_VERSION_XP functions 5 - 58
|
|
* Vista and Server 2008 use version SCREDIR_VERSION_LONGHORN functions 5 - 64
|
|
* if TS Client's build number is >= 4,034 use SCREDIR_VERSION_LONGHORN
|
|
*/
|
|
|
|
/* [MS-RDPESC] 3.1.4 */
|
|
#define SCARD_IOCTL_ESTABLISH_CONTEXT 0x00090014 /* EstablishContext */
|
|
#define SCARD_IOCTL_RELEASE_CONTEXT 0x00090018 /* ReleaseContext */
|
|
#define SCARD_IOCTL_IS_VALID_CONTEXT 0x0009001C /* IsValidContext */
|
|
#define SCARD_IOCTL_LIST_READER_GROUPS 0x00090020 /* ListReaderGroups */
|
|
#define SCARD_IOCTL_LIST_READERS_A 0x00090028 /* ListReaders ASCII */
|
|
#define SCARD_IOCTL_LIST_READERS_W 0x0009002C /* ListReaders Wide */
|
|
#define SCARD_IOCTL_INTRODUCE_READER_GROUP 0x00090050 /* IntroduceReaderGroup */
|
|
#define SCARD_IOCTL_FORGET_READER_GROUP 0x00090058 /* ForgetReader */
|
|
#define SCARD_IOCTL_INTRODUCE_READER 0x00090060 /* IntroduceReader */
|
|
#define SCARD_IOCTL_FORGET_READER 0x00090068 /* IntroduceReader */
|
|
#define SCARD_IOCTL_ADD_READER_TO_GROUP 0x00090070 /* AddReaderToGroup */
|
|
#define SCARD_IOCTL_REMOVE_READER_FROM_GROUP 0x00090078 /* RemoveReaderFromGroup*/
|
|
#define SCARD_IOCTL_GET_STATUS_CHANGE_A 0x000900A0 /* GetStatusChangeA */
|
|
#define SCARD_IOCTL_GET_STATUS_CHANGE_W 0x000900A4 /* GetStatusChangeW */
|
|
#define SCARD_IOCTL_CANCEL 0x000900A8 /* Cancel */
|
|
#define SCARD_IOCTL_CONNECT_A 0x000900AC /* ConnectA */
|
|
#define SCARD_IOCTL_CONNECT_W 0x000900B0 /* ConnectW */
|
|
#define SCARD_IOCTL_RECONNECT 0x000900B4 /* Reconnect */
|
|
#define SCARD_IOCTL_DISCONNECT 0x000900B8 /* Disconnect */
|
|
#define SCARD_IOCTL_BEGIN_TRANSACTION 0x000900BC /* BeginTransaction */
|
|
#define SCARD_IOCTL_END_TRANSACTION 0x000900C0 /* EndTransaction */
|
|
#define SCARD_IOCTL_STATE 0x000900C4 /* State */
|
|
#define SCARD_IOCTL_STATUS_A 0x000900C8 /* StatusA */
|
|
#define SCARD_IOCTL_STATUS_W 0x000900CC /* StatusW */
|
|
#define SCARD_IOCTL_TRANSMIT 0x000900D0 /* Transmit */
|
|
#define SCARD_IOCTL_CONTROL 0x000900D4 /* Control */
|
|
#define SCARD_IOCTL_GETATTRIB 0x000900D8 /* GetAttrib */
|
|
#define SCARD_IOCTL_SETATTRIB 0x000900DC /* SetAttrib */
|
|
#define SCARD_IOCTL_ACCESS_STARTED_EVENT 0x000900E0 /* SCardAccessStartedEvent */
|
|
#define SCARD_IOCTL_LOCATE_CARDS_BY_ATR 0x000900E8 /* LocateCardsByATR */
|
|
|
|
/* scope used in EstablishContextCall */
|
|
#define SCARD_SCOPE_USER 0x00000000
|
|
#define SCARD_SCOPE_TERMINAL 0x00000001
|
|
#define SCARD_SCOPE_SYSTEM 0x00000002
|
|
|
|
/* disposition - action to take on card */
|
|
#define SCARD_LEAVE_CARD 0x00000000
|
|
#define SCARD_RESET_CARD 0x00000001
|
|
#define SCARD_UNPOWER_CARD 0x00000002
|
|
#define SCARD_EJECT_CARD 0x00000003
|
|
|
|
#define MAX_SMARTCARDS 16
|
|
|
|
/* stores info about a smart card */
|
|
typedef struct smartcard
|
|
{
|
|
tui32 DeviceId;
|
|
} SMARTCARD;
|
|
|
|
/* globals */
|
|
SMARTCARD *smartcards[MAX_SMARTCARDS];
|
|
int g_smartcards_inited = 0;
|
|
static tui32 g_device_id = 0;
|
|
static int g_scard_index = 0;
|
|
|
|
/* externs */
|
|
extern tui32 g_completion_id;
|
|
extern int g_rdpdr_chan_id; /* in chansrv.c */
|
|
|
|
|
|
/******************************************************************************
|
|
** static functions local to this file **
|
|
******************************************************************************/
|
|
static struct stream *scard_make_new_ioctl(IRP *irp, tui32 ioctl);
|
|
static int scard_add_new_device(tui32 device_id);
|
|
static int scard_get_free_slot(void);
|
|
static void scard_release_resources(void);
|
|
static void scard_send_EstablishContext(IRP *irp, int scope);
|
|
static void scard_send_ReleaseContext(IRP *irp,
|
|
char *context, int context_bytes);
|
|
static void scard_send_IsContextValid(IRP *irp,
|
|
char *context, int context_bytes);
|
|
static void scard_send_ListReaders(IRP *irp,
|
|
char *context, int context_bytes,
|
|
char *groups, int cchReaders,
|
|
int wide);
|
|
static void scard_send_GetStatusChange(IRP *irp,
|
|
char *context, int context_bytes,
|
|
int wide,
|
|
tui32 timeout, tui32 num_readers,
|
|
READER_STATE *rsa);
|
|
static void scard_send_Connect(IRP *irp,
|
|
char *context, int context_bytes,
|
|
int wide,
|
|
READER_STATE *rs);
|
|
static void scard_send_Reconnect(IRP *irp,
|
|
char *context, int context_bytes,
|
|
char *card, int card_bytes,
|
|
READER_STATE *rs);
|
|
static void scard_send_BeginTransaction(IRP *irp,
|
|
char *context, int context_bytes,
|
|
char *card, int card_bytes);
|
|
static void scard_send_EndTransaction(IRP *irp,
|
|
char *context, int context_bytes,
|
|
char *card, int card_bytes,
|
|
tui32 dwDisposition);
|
|
static void scard_send_Status(IRP *irp, int wide,
|
|
char *context, int context_bytes,
|
|
char *card, int card_bytes,
|
|
int cchReaderLen, int cbAtrLen);
|
|
static void scard_send_Disconnect(IRP *irp,
|
|
char *context, int context_bytes,
|
|
char *card, int card_bytes,
|
|
int dwDisposition);
|
|
static int scard_send_Transmit(IRP *irp,
|
|
char *context, int context_byte,
|
|
char *card, int card_bytes,
|
|
char *send_data, int send_bytes,
|
|
int recv_bytes,
|
|
struct xrdp_scard_io_request *send_ior,
|
|
struct xrdp_scard_io_request *recv_ior);
|
|
static int scard_send_Control(IRP *irp, char *context, int context_bytes,
|
|
char *card, int card_bytes,
|
|
char *send_data, int send_bytes,
|
|
int recv_bytes, int control_code);
|
|
static int scard_send_Cancel(IRP *irp, char *context, int context_bytes);
|
|
static int scard_send_GetAttrib(IRP *irp, char *card, int card_bytes,
|
|
READER_STATE *rs);
|
|
|
|
/******************************************************************************
|
|
** local callbacks into this module **
|
|
******************************************************************************/
|
|
|
|
static void scard_handle_EstablishContext_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
static void scard_handle_ReleaseContext_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
|
|
static void scard_handle_IsContextValid_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
static void scard_handle_ListReaders_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
static void scard_handle_GetStatusChange_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
static void scard_handle_Connect_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
static void scard_handle_Reconnect_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
static void scard_handle_BeginTransaction_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
static void scard_handle_EndTransaction_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId,
|
|
tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
static void scard_handle_Status_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
static void scard_handle_Disconnect_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
|
|
static void scard_handle_Transmit_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId,
|
|
tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
static void scard_handle_Control_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId,
|
|
tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
static void scard_handle_Cancel_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId,
|
|
tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
static void scard_handle_GetAttrib_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId,
|
|
tui32 CompletionId,
|
|
tui32 IoStatus);
|
|
|
|
/******************************************************************************
|
|
** **
|
|
** externally accessible functions, defined in smartcard.h **
|
|
** **
|
|
******************************************************************************/
|
|
|
|
/**
|
|
*****************************************************************************/
|
|
void
|
|
scard_device_announce(tui32 device_id)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered: device_id=%d", device_id);
|
|
|
|
if (g_smartcards_inited)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "already init");
|
|
return;
|
|
}
|
|
|
|
g_memset(&smartcards, 0, sizeof(smartcards));
|
|
g_smartcards_inited = 1;
|
|
g_device_id = device_id;
|
|
g_scard_index = scard_add_new_device(device_id);
|
|
|
|
if (g_scard_index < 0)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_add_new_device failed with DeviceId=%d", g_device_id);
|
|
}
|
|
else
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "added smartcard with DeviceId=%d to list", g_device_id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
int
|
|
scard_get_wait_objs(tbus *objs, int *count, int *timeout)
|
|
{
|
|
return scard_pcsc_get_wait_objs(objs, count, timeout);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
int
|
|
scard_check_wait_objs(void)
|
|
{
|
|
return scard_pcsc_check_wait_objs();
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
int
|
|
scard_init(void)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_INFO, "scard_init:");
|
|
return scard_pcsc_init();
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
int
|
|
scard_deinit(void)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_INFO, "scard_deinit:");
|
|
scard_pcsc_deinit();
|
|
scard_release_resources();
|
|
g_smartcards_inited = 0;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_establish_context(void *user_data, int scope)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_EstablishContext_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_EstablishContext(irp, scope);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Release a previously established Smart Card context
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_release_context(void *user_data,
|
|
char *context, int context_bytes)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_ReleaseContext_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_ReleaseContext(irp, context, context_bytes);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Checks if a previously established context is still valid
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_is_valid_context(void *user_data, char *context, int context_bytes)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_IsContextValid_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_IsContextValid(irp, context, context_bytes);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_list_readers(void *user_data, char *context, int context_bytes,
|
|
char *groups, int cchReaders, int wide)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_ListReaders_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_ListReaders(irp, context, context_bytes, groups,
|
|
cchReaders, wide);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Send get change in status command
|
|
*
|
|
* @param con connection to client
|
|
* @param wide TRUE if unicode string
|
|
* @param timeout timeout in milliseconds, -1 for infinity
|
|
* @param num_readers number of entries in rsa
|
|
* @param rsa array of READER_STATEs
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_get_status_change(void *user_data, char *context, int context_bytes,
|
|
int wide, tui32 timeout, tui32 num_readers,
|
|
READER_STATE *rsa)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_GetStatusChange_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_GetStatusChange(irp, context, context_bytes, wide, timeout,
|
|
num_readers, rsa);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Open a connection to the smart card located in the reader
|
|
*
|
|
* @param con connection to client
|
|
* @param wide TRUE if unicode string
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_connect(void *user_data, char *context, int context_bytes,
|
|
int wide, READER_STATE *rs)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_Connect_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_Connect(irp, context, context_bytes, wide, rs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* The reconnect method re-establishes a smart card reader handle. On success,
|
|
* the handle is valid once again.
|
|
*
|
|
* @param con connection to client
|
|
* @param sc_handle handle to device
|
|
* @param rs reader state where following fields are set
|
|
* rs.shared_mode_flag
|
|
* rs.preferred_protocol
|
|
* rs.init_type
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_reconnect(void *user_data, char *context, int context_bytes,
|
|
char *card, int card_bytes, READER_STATE *rs)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_Reconnect_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_Reconnect(irp, context, context_bytes, card, card_bytes, rs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Lock smart card reader for exclusive access for specified smart
|
|
* card reader handle.
|
|
*
|
|
* @param con connection to client
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_begin_transaction(void *user_data, char *context, int context_bytes,
|
|
char *card, int card_bytes)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_BeginTransaction_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_BeginTransaction(irp, context, context_bytes, card, card_bytes);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Release a smart card reader after being locked by a previously
|
|
* successful call to Begin Transaction
|
|
*
|
|
* @param con connection to client
|
|
* @param sc_handle handle to smartcard
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_end_transaction(void *user_data, char *context, int context_bytes,
|
|
char *card, int card_bytes,
|
|
tui32 dwDisposition)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_EndTransaction_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_EndTransaction(irp, context, context_bytes,
|
|
card, card_bytes, dwDisposition);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get the status of a connection for a valid smart card reader handle
|
|
*
|
|
* @param con connection to client
|
|
* @param wide TRUE if unicode string
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_status(void *user_data, int wide, char *context, int context_bytes,
|
|
char *card, int card_bytes,
|
|
int cchReaderLen, int cbAtrLen)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_Status_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_Status(irp, wide, context, context_bytes, card, card_bytes,
|
|
cchReaderLen, cbAtrLen);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Release a smart card reader handle that was acquired in ConnectA/ConnectW
|
|
*
|
|
* @param con connection to client
|
|
* @param sc_handle handle to smartcard
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_disconnect(void *user_data, char *context, int context_bytes,
|
|
char *card, int card_bytes, int dwDisposition)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_Disconnect_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_Disconnect(irp, context, context_bytes,
|
|
card, card_bytes, dwDisposition);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* The Transmit_Call structure is used to send data to the smart card
|
|
* associated with a valid context.
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_transmit(void *user_data, char *context, int context_bytes,
|
|
char *card, int card_bytes,
|
|
char *send_data, int send_bytes, int recv_bytes,
|
|
struct xrdp_scard_io_request *send_ior,
|
|
struct xrdp_scard_io_request *recv_ior)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_Transmit_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_Transmit(irp, context, context_bytes, card, card_bytes,
|
|
send_data, send_bytes,
|
|
recv_bytes, send_ior, recv_ior);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Communicate directly with the smart card reader
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_control(void *user_data, char *context, int context_bytes,
|
|
char *card, int card_bytes,
|
|
char *send_data, int send_bytes,
|
|
int recv_bytes, int control_code)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_Control_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_Control(irp, context, context_bytes,
|
|
card, card_bytes,
|
|
send_data, send_bytes,
|
|
recv_bytes, control_code);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Cancel any outstanding calls
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_cancel(void *user_data, char *context, int context_bytes)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_Cancel_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_Cancel(irp, context, context_bytes);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get reader attributes
|
|
*****************************************************************************/
|
|
int
|
|
scard_send_get_attrib(void *user_data, char *card, int card_bytes,
|
|
READER_STATE *rs)
|
|
{
|
|
IRP *irp;
|
|
|
|
/* setup up IRP */
|
|
if ((irp = devredir_irp_new()) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return 1;
|
|
}
|
|
|
|
irp->scard_index = g_scard_index;
|
|
irp->CompletionId = g_completion_id++;
|
|
irp->DeviceId = g_device_id;
|
|
irp->callback = scard_handle_GetAttrib_Return;
|
|
irp->user_data = user_data;
|
|
|
|
/* send IRP to client */
|
|
scard_send_GetAttrib(irp, card, card_bytes, rs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** **
|
|
** static functions local to this file **
|
|
** **
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Create a new stream and insert specified IOCTL
|
|
*
|
|
* @param irp information about the I/O
|
|
* @param ioctl the IOCTL code
|
|
*
|
|
* @return stream with IOCTL inserted in it, NULL on error
|
|
*****************************************************************************/
|
|
static struct stream *
|
|
scard_make_new_ioctl(IRP *irp, tui32 ioctl)
|
|
{
|
|
/*
|
|
* format of device control request
|
|
*
|
|
* DeviceIoRequest
|
|
* u16 RDPDR_CTYP_CORE
|
|
* u16 PAKID_CORE_DEVICE_IOREQUEST
|
|
* u32 DeviceId
|
|
* u32 FileId
|
|
* u32 CompletionId
|
|
* u32 MajorFunction
|
|
* u32 MinorFunction
|
|
*
|
|
* u32 OutputBufferLength SHOULD be 2048
|
|
* u32 InputBufferLength
|
|
* u32 IoControlCode
|
|
* 20 bytes padding
|
|
* xx bytes InputBuffer (variable)
|
|
*/
|
|
|
|
struct stream *s;
|
|
|
|
xstream_new(s, 1024 * 4);
|
|
|
|
devredir_insert_DeviceIoRequest(s,
|
|
irp->DeviceId,
|
|
irp->FileId,
|
|
irp->CompletionId,
|
|
IRP_MJ_DEVICE_CONTROL,
|
|
IRP_MN_NONE);
|
|
|
|
xstream_wr_u32_le(s, 2048); /* OutputBufferLength */
|
|
s_push_layer(s, iso_hdr, 4); /* InputBufferLength - insert later */
|
|
xstream_wr_u32_le(s, ioctl); /* Ioctl Code */
|
|
out_uint8s(s, 20); /* padding */
|
|
|
|
/* [MS-RPCE] 2.2.6.1 */
|
|
xstream_wr_u32_le(s, 0x00081001); /* len 8, LE, v1 */
|
|
xstream_wr_u32_le(s, 0xcccccccc); /* filler */
|
|
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Create a new smart card device entry and insert it into smartcards[]
|
|
*
|
|
* @param device_id DeviceId of new card
|
|
*
|
|
* @return index into smartcards[] on success, -1 on failure
|
|
*****************************************************************************/
|
|
static int
|
|
scard_add_new_device(tui32 device_id)
|
|
{
|
|
int index;
|
|
SMARTCARD *sc;
|
|
|
|
if ((index = scard_get_free_slot()) < 0)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_get_free_slot failed");
|
|
return -1;
|
|
}
|
|
|
|
sc = g_new0(SMARTCARD, 1);
|
|
if (sc == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
|
|
return -1;
|
|
}
|
|
|
|
sc->DeviceId = device_id;
|
|
smartcards[index] = sc;
|
|
|
|
return index;
|
|
}
|
|
|
|
/**
|
|
* Find first unused entry in smartcards
|
|
*
|
|
* @return index of first unused entry in smartcards or -1 if smartcards
|
|
* is full
|
|
*****************************************************************************/
|
|
static int
|
|
scard_get_free_slot(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_SMARTCARDS; i++)
|
|
{
|
|
if (smartcards[i] == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "found free slot at index %d", i);
|
|
return i;
|
|
}
|
|
}
|
|
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "too many smart card devices; rejecting this one");
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Release resources prior to shutting down
|
|
*****************************************************************************/
|
|
static void
|
|
scard_release_resources(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_SMARTCARDS; i++)
|
|
{
|
|
if (smartcards[i] != NULL)
|
|
{
|
|
g_free(smartcards[i]);
|
|
smartcards[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_send_EstablishContext(IRP *irp, int scope)
|
|
{
|
|
struct stream *s;
|
|
int bytes;
|
|
|
|
if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_ESTABLISH_CONTEXT)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
|
|
return;
|
|
}
|
|
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, scope);
|
|
out_uint32_le(s, 0x00000000);
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 8;
|
|
out_uint32_le(s, bytes);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
free_stream(s);
|
|
}
|
|
|
|
/**
|
|
* Release a previously established Smart Card context
|
|
*****************************************************************************/
|
|
static void
|
|
scard_send_ReleaseContext(IRP *irp, char *context, int context_bytes)
|
|
{
|
|
/* see [MS-RDPESC] 3.1.4.2 */
|
|
|
|
SMARTCARD *sc;
|
|
struct stream *s;
|
|
int bytes;
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return;
|
|
}
|
|
|
|
if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_RELEASE_CONTEXT)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
|
|
return;
|
|
}
|
|
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint32_le(s, 0x00020000);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint8a(s, context, context_bytes);
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 8;
|
|
out_uint32_le(s, bytes);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
free_stream(s);
|
|
}
|
|
|
|
/**
|
|
* Checks if a previously established context is still valid
|
|
*****************************************************************************/
|
|
static void
|
|
scard_send_IsContextValid(IRP *irp, char *context, int context_bytes)
|
|
{
|
|
/* see [MS-RDPESC] 3.1.4.3 */
|
|
|
|
SMARTCARD *sc;
|
|
struct stream *s;
|
|
int bytes;
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return;
|
|
}
|
|
|
|
if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_IS_VALID_CONTEXT)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* command format
|
|
*
|
|
* ......
|
|
* 20 bytes padding
|
|
* u32 4 bytes len 8, LE, v1
|
|
* u32 4 bytes filler
|
|
* 16 bytes unused (s->p currently pointed here at unused[0])
|
|
* u32 4 bytes context len
|
|
* u32 4 bytes context
|
|
*/
|
|
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
|
|
/* insert context */
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint8a(s, context, context_bytes);
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 8;
|
|
out_uint32_le(s, bytes);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
free_stream(s);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_send_ListReaders(IRP *irp, char *context, int context_bytes,
|
|
char *groups, int cchReaders, int wide)
|
|
{
|
|
/* see [MS-RDPESC] 2.2.2.4 */
|
|
|
|
SMARTCARD *sc;
|
|
struct stream *s;
|
|
int bytes;
|
|
int bytes_groups;
|
|
int val;
|
|
int index;
|
|
int num_chars;
|
|
tui32 ioctl;
|
|
twchar w_groups[100];
|
|
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return;
|
|
}
|
|
|
|
ioctl = (wide) ? SCARD_IOCTL_LIST_READERS_W :
|
|
SCARD_IOCTL_LIST_READERS_A;
|
|
|
|
if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
|
|
return;
|
|
}
|
|
|
|
num_chars = 0;
|
|
bytes_groups = 0;
|
|
w_groups[0] = 0;
|
|
val = 0;
|
|
if (groups != 0)
|
|
{
|
|
if (groups[0] != 0)
|
|
{
|
|
num_chars = g_mbstowcs(w_groups, groups, 99);
|
|
bytes_groups = wide ? (num_chars + 2) * 2 : num_chars + 2;
|
|
val = 0x00020004;
|
|
}
|
|
}
|
|
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint32_le(s, 0x00020000);
|
|
out_uint32_le(s, bytes_groups);
|
|
out_uint32_le(s, val);
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, cchReaders);
|
|
|
|
/* insert context */
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint8a(s, context, context_bytes);
|
|
|
|
if (bytes_groups > 0)
|
|
{
|
|
if (wide)
|
|
{
|
|
out_uint32_le(s, bytes_groups);
|
|
for (index = 0; index < num_chars; index++)
|
|
{
|
|
out_uint16_le(s, w_groups[index]);
|
|
}
|
|
out_uint16_le(s, 0);
|
|
out_uint16_le(s, 0);
|
|
}
|
|
else
|
|
{
|
|
out_uint32_le(s, bytes_groups);
|
|
for (index = 0; index < num_chars; index++)
|
|
{
|
|
out_uint8(s, w_groups[index]);
|
|
}
|
|
out_uint16_le(s, 0);
|
|
out_uint16_le(s, 0);
|
|
}
|
|
}
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 8;
|
|
out_uint32_le(s, bytes);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "scard_send_ListReaders:", s->data, bytes);
|
|
|
|
free_stream(s);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static int
|
|
align_s(struct stream *s, int bytes)
|
|
{
|
|
int i32;
|
|
|
|
i32 = (int) (s->p - s->data);
|
|
while ((i32 % bytes) != 0)
|
|
{
|
|
out_uint8s(s, 1);
|
|
i32 = (int) (s->p - s->data);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get change in status
|
|
*
|
|
* @param irp I/O resource pkt
|
|
* @param wide TRUE if unicode string
|
|
* @param timeout timeout in milliseconds, -1 for infinity
|
|
* @param num_readers number of entries in rsa
|
|
* @param rsa array of READER_STATEs
|
|
*****************************************************************************/
|
|
static void
|
|
scard_send_GetStatusChange(IRP *irp, char *context, int context_bytes,
|
|
int wide, tui32 timeout,
|
|
tui32 num_readers, READER_STATE *rsa)
|
|
{
|
|
/* see [MS-RDPESC] 2.2.2.11 for ASCII */
|
|
/* see [MS-RDPESC] 2.2.2.12 for Wide char */
|
|
|
|
SMARTCARD *sc;
|
|
READER_STATE *rs;
|
|
struct stream *s;
|
|
tui32 ioctl;
|
|
int bytes;
|
|
unsigned int i;
|
|
int num_chars;
|
|
int index;
|
|
twchar w_reader_name[100];
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return;
|
|
}
|
|
|
|
ioctl = (wide) ? SCARD_IOCTL_GET_STATUS_CHANGE_W :
|
|
SCARD_IOCTL_GET_STATUS_CHANGE_A;
|
|
|
|
if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
|
|
return;
|
|
}
|
|
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint32_le(s, 0x00020000);
|
|
|
|
out_uint32_le(s, timeout);
|
|
out_uint32_le(s, num_readers);
|
|
out_uint32_le(s, 0x00020004); /* ? */
|
|
|
|
/* insert context */
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint8a(s, context, context_bytes);
|
|
|
|
out_uint32_le(s, num_readers);
|
|
|
|
/* insert card reader state */
|
|
for (i = 0; i < num_readers; i++)
|
|
{
|
|
rs = &rsa[i];
|
|
out_uint32_le(s, 0x00020008); /* ? */
|
|
out_uint32_le(s, rs->current_state);
|
|
out_uint32_le(s, rs->event_state);
|
|
out_uint32_le(s, rs->atr_len);
|
|
out_uint8p(s, rs->atr, 33);
|
|
out_uint8s(s, 3);
|
|
}
|
|
|
|
if (wide)
|
|
{
|
|
/* insert card reader names */
|
|
for (i = 0; i < num_readers; i++)
|
|
{
|
|
rs = &rsa[i];
|
|
num_chars = g_mbstowcs(w_reader_name, rs->reader_name, 99);
|
|
out_uint32_le(s, num_chars + 2);
|
|
out_uint32_le(s, 0);
|
|
out_uint32_le(s, num_chars + 2);
|
|
for (index = 0; index < num_chars; index++)
|
|
{
|
|
out_uint16_le(s, w_reader_name[index]);
|
|
}
|
|
out_uint16_le(s, 0);
|
|
out_uint16_le(s, 0);
|
|
align_s(s, 4);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* insert card reader names */
|
|
for (i = 0; i < num_readers; i++)
|
|
{
|
|
rs = &rsa[i];
|
|
num_chars = g_mbstowcs(w_reader_name, rs->reader_name, 99);
|
|
out_uint32_le(s, num_chars + 2);
|
|
out_uint32_le(s, 0);
|
|
out_uint32_le(s, num_chars + 2);
|
|
for (index = 0; index < num_chars; index++)
|
|
{
|
|
out_uint8(s, w_reader_name[index]);
|
|
}
|
|
out_uint8(s, 0);
|
|
out_uint8(s, 0);
|
|
align_s(s, 4);
|
|
}
|
|
}
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 8;
|
|
out_uint32_le(s, bytes);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "scard_send_GetStatusChange:", s->data, bytes);
|
|
|
|
free_stream(s);
|
|
}
|
|
|
|
/**
|
|
* Send connect command
|
|
*
|
|
* @param irp I/O resource pkt
|
|
* @param wide TRUE if unicode string
|
|
* @param rs reader state
|
|
*****************************************************************************/
|
|
static void
|
|
scard_send_Connect(IRP *irp, char *context, int context_bytes,
|
|
int wide, READER_STATE *rs)
|
|
{
|
|
/* see [MS-RDPESC] 2.2.2.13 for ASCII */
|
|
/* see [MS-RDPESC] 2.2.2.14 for Wide char */
|
|
|
|
SMARTCARD *sc;
|
|
struct stream *s;
|
|
tui32 ioctl;
|
|
int bytes;
|
|
int num_chars;
|
|
int index;
|
|
twchar w_reader_name[100];
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return;
|
|
}
|
|
|
|
ioctl = (wide) ? SCARD_IOCTL_CONNECT_W :
|
|
SCARD_IOCTL_CONNECT_A;
|
|
|
|
if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
|
|
return;
|
|
}
|
|
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, 0x00020000);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint32_le(s, 0x00020004);
|
|
out_uint32_le(s, rs->dwShareMode);
|
|
out_uint32_le(s, rs->dwPreferredProtocols);
|
|
num_chars = g_mbstowcs(w_reader_name, rs->reader_name, 99);
|
|
out_uint32_le(s, num_chars + 2);
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, num_chars + 2);
|
|
if (wide)
|
|
{
|
|
for (index = 0; index < num_chars; index++)
|
|
{
|
|
out_uint16_le(s, w_reader_name[index]);
|
|
}
|
|
out_uint16_le(s, 0);
|
|
out_uint16_le(s, 0);
|
|
}
|
|
else
|
|
{
|
|
for (index = 0; index < num_chars; index++)
|
|
{
|
|
out_uint8(s, w_reader_name[index]);
|
|
}
|
|
out_uint8(s, 0);
|
|
out_uint8(s, 0);
|
|
}
|
|
align_s(s, 4);
|
|
|
|
/* insert context */
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint8a(s, context, context_bytes);
|
|
out_uint32_le(s, 0);
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 8;
|
|
out_uint32_le(s, bytes);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
free_stream(s);
|
|
}
|
|
|
|
/**
|
|
* The reconnect method re-establishes a smart card reader handle. On success,
|
|
* the handle is valid once again.
|
|
*
|
|
* @param con connection to client
|
|
* @param sc_handle handle to device
|
|
* @param rs reader state where following fields are set
|
|
* rs.shared_mode_flag
|
|
* rs.preferred_protocol
|
|
* rs.init_type
|
|
*****************************************************************************/
|
|
static void
|
|
scard_send_Reconnect(IRP *irp, char *context, int context_bytes,
|
|
char *card, int card_bytes, READER_STATE *rs)
|
|
{
|
|
/* see [MS-RDPESC] 2.2.2.15 */
|
|
/* see [MS-RDPESC] 3.1.4.36 */
|
|
|
|
SMARTCARD *sc;
|
|
struct stream *s;
|
|
int bytes;
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return;
|
|
}
|
|
|
|
if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_RECONNECT)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* command format
|
|
*
|
|
* ......
|
|
* 20 bytes padding
|
|
* u32 4 bytes len 8, LE, v1
|
|
* u32 4 bytes filler
|
|
* 24 bytes unused (s->p currently pointed here at unused[0])
|
|
* u32 4 bytes dwShareMode
|
|
* u32 4 bytes dwPreferredProtocols
|
|
* u32 4 bytes dwInitialization
|
|
* u32 4 bytes context length
|
|
* u32 4 bytes context
|
|
* u32 4 bytes handle length
|
|
* u32 4 bytes handle
|
|
*/
|
|
|
|
xstream_seek(s, 24); /* TODO */
|
|
|
|
out_uint32_le(s, rs->dwShareMode);
|
|
out_uint32_le(s, rs->dwPreferredProtocols);
|
|
out_uint32_le(s, rs->init_type);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint8a(s, context, context_bytes);
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint8a(s, card, card_bytes);
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
free_stream(s);
|
|
}
|
|
|
|
/**
|
|
* Lock smart card reader for exclusive access for specified smart
|
|
* card reader handle.
|
|
*
|
|
* @param con connection to client
|
|
*****************************************************************************/
|
|
static void
|
|
scard_send_BeginTransaction(IRP *irp, char *context, int context_bytes,
|
|
char *card, int card_bytes)
|
|
{
|
|
/* see [MS-RDPESC] 4.9 */
|
|
|
|
SMARTCARD *sc;
|
|
struct stream *s;
|
|
int bytes;
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return;
|
|
}
|
|
|
|
if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_BEGIN_TRANSACTION)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
|
|
return;
|
|
}
|
|
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint32_le(s, 0x00020000);
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint32_le(s, 0x00020004);
|
|
out_uint32_le(s, 0x00000000);
|
|
|
|
/* insert context */
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint8a(s, context, context_bytes);
|
|
|
|
/* insert card */
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint8a(s, card, card_bytes);
|
|
|
|
out_uint32_le(s, 0x00000000);
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 8;
|
|
out_uint32_le(s, bytes);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
free_stream(s);
|
|
}
|
|
|
|
/**
|
|
* Release a smart card reader after being locked by a previously
|
|
* successful call to Begin Transaction
|
|
*
|
|
* @param con connection to client
|
|
* @param sc_handle handle to smartcard
|
|
*****************************************************************************/
|
|
static void
|
|
scard_send_EndTransaction(IRP *irp, char *context, int context_bytes,
|
|
char *card, int card_bytes,
|
|
tui32 dwDisposition)
|
|
{
|
|
/* see [MS-RDPESC] 3.1.4.32 */
|
|
|
|
SMARTCARD *sc;
|
|
struct stream *s;
|
|
int bytes;
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return;
|
|
}
|
|
|
|
if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_END_TRANSACTION)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
|
|
return;
|
|
}
|
|
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint32_le(s, 0x00020000);
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint32_le(s, 0x00020004);
|
|
out_uint32_le(s, dwDisposition);
|
|
|
|
/* insert context */
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint8a(s, context, context_bytes);
|
|
|
|
/* insert card */
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint8a(s, card, card_bytes);
|
|
|
|
out_uint32_le(s, 0);
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 8;
|
|
out_uint32_le(s, bytes);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
free_stream(s);
|
|
}
|
|
|
|
/**
|
|
* Get the status of a connection for a valid smart card reader handle
|
|
*
|
|
* @param con connection to client
|
|
* @param wide TRUE if unicode string
|
|
*****************************************************************************/
|
|
static void
|
|
scard_send_Status(IRP *irp, int wide, char *context, int context_bytes,
|
|
char *card, int card_bytes,
|
|
int cchReaderLen, int cbAtrLen)
|
|
{
|
|
/* see [MS-RDPESC] 2.2.2.18 */
|
|
|
|
SMARTCARD *sc;
|
|
struct stream *s;
|
|
int bytes;
|
|
tui32 ioctl;
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return;
|
|
}
|
|
|
|
ioctl = wide ? SCARD_IOCTL_STATUS_W : SCARD_IOCTL_STATUS_A;
|
|
if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl");
|
|
return;
|
|
}
|
|
/*
|
|
30 00 00 00
|
|
00 00 00 00
|
|
04 00 00 00
|
|
00 00 02 00
|
|
04 00 00 00
|
|
04 00 02 00
|
|
01 00 00 00
|
|
00 00 00 00 dwReaderLen
|
|
40 00 00 00 dwAtrLen
|
|
04 00 00 00
|
|
07 00 00 00
|
|
04 00 00 00
|
|
09 00 00 00 hCard
|
|
00 00 00 00
|
|
*/
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint32_le(s, 0x00020000);
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint32_le(s, 0x00020004);
|
|
out_uint32_le(s, 0x00000001);
|
|
out_uint32_le(s, cchReaderLen); /* readerLen, see [MS-RDPESC] 4.11 */
|
|
out_uint32_le(s, cbAtrLen); /* atrLen, see [MS-RDPESC] 4.11 */
|
|
|
|
/* insert context */
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint8a(s, context, context_bytes);
|
|
|
|
/* insert card */
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint8a(s, card, card_bytes);
|
|
|
|
out_uint32_le(s, 0);
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 8;
|
|
out_uint32_le(s, bytes);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "", s->data, bytes);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
free_stream(s);
|
|
}
|
|
|
|
/**
|
|
* Release a smart card reader handle that was acquired in ConnectA/ConnectW
|
|
*
|
|
* @param con connection to client
|
|
* @param sc_handle handle to smartcard
|
|
*****************************************************************************/
|
|
static void
|
|
scard_send_Disconnect(IRP *irp, char *context, int context_bytes,
|
|
char *card, int card_bytes, int dwDisposition)
|
|
{
|
|
/* see [MS-RDPESC] 3.1.4.30 */
|
|
|
|
SMARTCARD *sc;
|
|
struct stream *s;
|
|
int bytes;
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return;
|
|
}
|
|
|
|
if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_DISCONNECT)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
|
|
return;
|
|
}
|
|
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint32_le(s, 0x00020000);
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint32_le(s, 0x00020004);
|
|
out_uint32_le(s, dwDisposition);
|
|
|
|
/* insert context */
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint8a(s, context, context_bytes);
|
|
|
|
/* insert card */
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint8a(s, card, card_bytes);
|
|
|
|
out_uint32_le(s, 0x00000000);
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 8;
|
|
out_uint32_le(s, bytes);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
free_stream(s);
|
|
}
|
|
|
|
/**
|
|
* The Transmit_Call structure is used to send data to the smart card
|
|
* associated with a valid context.
|
|
*****************************************************************************/
|
|
static int
|
|
scard_send_Transmit(IRP *irp, char *context, int context_bytes,
|
|
char *card, int card_bytes, char *send_data,
|
|
int send_bytes, int recv_bytes,
|
|
struct xrdp_scard_io_request *send_ior,
|
|
struct xrdp_scard_io_request *recv_ior)
|
|
{
|
|
/* see [MS-RDPESC] 2.2.2.19 */
|
|
|
|
SMARTCARD *sc;
|
|
struct stream *s;
|
|
int bytes;
|
|
int val;
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return 1;
|
|
}
|
|
|
|
if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_TRANSMIT)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl");
|
|
return 1;
|
|
}
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "send_bytes %d recv_bytes %d send dwProtocol %d cbPciLength %d "
|
|
"extra_bytes %d recv dwProtocol %d cbPciLength %d extra_bytes %d",
|
|
send_bytes, recv_bytes, send_ior->dwProtocol, send_ior->cbPciLength,
|
|
send_ior->extra_bytes, recv_ior->dwProtocol, recv_ior->cbPciLength,
|
|
recv_ior->extra_bytes);
|
|
|
|
/*
|
|
* command format
|
|
*
|
|
* ......
|
|
* 20 bytes padding
|
|
* u32 4 bytes len 8, LE, v1
|
|
* u32 4 bytes filler
|
|
* 12 bytes unused (s->p currently pointed here at unused[0])
|
|
* u32 4 bytes map0
|
|
* 4 bytes unused
|
|
* u32 4 bytes map1
|
|
* u32 4 bytes dwProtocol
|
|
* u32 4 bytes cbPciLength
|
|
* u32 4 bytes map2
|
|
* u32 4 bytes cbSendLength
|
|
* u32 4 bytes map3
|
|
* u32 4 bytes map4
|
|
* u32 4 bytes map5
|
|
* u32 4 bytes map6
|
|
* u32 4 bytes cbRecvLength
|
|
* u32 4 bytes len of sc_handle
|
|
* u32 4 bytes sc_handle
|
|
*/
|
|
|
|
//g_writeln("send_bytes %d", send_bytes);
|
|
//g_writeln("recv_bytes %d", recv_bytes);
|
|
|
|
#if 0
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
out_uint32_be(s, 0x00000000);
|
|
out_uint32_be(s, 0x04000000);
|
|
out_uint32_be(s, 0x00000200); // map 0
|
|
out_uint32_be(s, 0x04000000);
|
|
out_uint32_be(s, 0x04000200); // map 1
|
|
out_uint32_be(s, 0x01000000);
|
|
out_uint32_be(s, 0x00000000);
|
|
out_uint32_be(s, 0x00000000);
|
|
|
|
//out_uint32_be(s, 0x05000000);
|
|
out_uint32_le(s, send_bytes);
|
|
|
|
out_uint32_be(s, 0x08000200);
|
|
out_uint32_be(s, 0x0c000200);
|
|
out_uint32_be(s, 0x00000000);
|
|
|
|
//out_uint32_be(s, 0x02010000);
|
|
out_uint32_le(s, recv_bytes);
|
|
|
|
out_uint32_be(s, 0x04000000);
|
|
out_uint32_be(s, 0x05000000);
|
|
out_uint32_be(s, 0x04000000);
|
|
out_uint32_be(s, 0x0b000000);
|
|
|
|
//out_uint32_be(s, 0x05000000);
|
|
//out_uint32_be(s, 0x00b00704);
|
|
//out_uint32_be(s, 0x10000000);
|
|
out_uint32_le(s, send_bytes);
|
|
out_uint8p(s, send_data, send_bytes);
|
|
align_s(s, 4);
|
|
|
|
out_uint32_be(s, 0x01000000);
|
|
out_uint32_be(s, 0x00000000);
|
|
out_uint32_be(s, 0x00000000);
|
|
#else
|
|
|
|
//g_printf("send cbPciLength %d\n", send_ior->cbPciLength);
|
|
//g_printf("send extra_bytes %d\n", send_ior->extra_bytes);
|
|
//g_printf("recv cbPciLength %d\n", recv_ior->cbPciLength);
|
|
//g_printf("recv extra_bytes %d\n", recv_ior->extra_bytes);
|
|
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
out_uint32_le(s, 0x00000000);
|
|
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint32_le(s, 0x00020000); /* map0 */
|
|
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint32_le(s, 0x00020004); /* map1 */
|
|
|
|
out_uint32_le(s, send_ior->dwProtocol);
|
|
out_uint32_le(s, send_ior->cbPciLength - 8);
|
|
|
|
val = send_ior->extra_bytes > 0 ? 1 : 0;
|
|
out_uint32_le(s, val); /* map2 */
|
|
|
|
out_uint32_le(s, send_bytes);
|
|
|
|
val = send_bytes > 0 ? 0x00020008 : 0;
|
|
out_uint32_le(s, val); /* map3 */
|
|
|
|
val = recv_ior->cbPciLength > 0 ? 0x0002000c : 0;
|
|
out_uint32_le(s, val); /* map 4 */
|
|
|
|
out_uint32_le(s, 0); // map5
|
|
out_uint32_le(s, recv_bytes);
|
|
|
|
/* map0 */
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint8a(s, context, context_bytes);
|
|
|
|
/* map1 */
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint8a(s, card, card_bytes);
|
|
|
|
if (send_ior->extra_bytes > 0)
|
|
{
|
|
out_uint32_le(s, send_ior->extra_bytes);
|
|
out_uint8a(s, send_ior->extra_data, send_ior->extra_bytes);
|
|
}
|
|
|
|
if (send_bytes > 0)
|
|
{
|
|
out_uint32_le(s, send_bytes);
|
|
out_uint8a(s, send_data, send_bytes);
|
|
align_s(s, 4);
|
|
}
|
|
|
|
if (recv_ior->cbPciLength > 0)
|
|
{
|
|
/* map4 */
|
|
out_uint32_le(s, recv_ior->dwProtocol);
|
|
out_uint32_le(s, recv_ior->cbPciLength - 8);
|
|
val = recv_ior->extra_bytes > 0 ? 1 : 0;
|
|
out_uint32_le(s, val); /* map6*/
|
|
if (val)
|
|
{
|
|
out_uint32_le(s, recv_ior->extra_bytes);
|
|
out_uint8a(s, recv_ior->extra_data, recv_ior->extra_bytes);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 8;
|
|
out_uint32_le(s, bytes);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "scard_send_Transmit:", s->data, bytes);
|
|
|
|
free_stream(s);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Communicate directly with the smart card reader
|
|
*****************************************************************************/
|
|
static int
|
|
scard_send_Control(IRP *irp, char *context, int context_bytes,
|
|
char *card, int card_bytes, char *send_data,
|
|
int send_bytes, int recv_bytes, int control_code)
|
|
{
|
|
/* see [MS-RDPESC] 2.2.2.19 */
|
|
|
|
SMARTCARD *sc;
|
|
struct stream *s;
|
|
int bytes;
|
|
int val;
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return 1;
|
|
}
|
|
|
|
if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_CONTROL)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl");
|
|
return 1;
|
|
}
|
|
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint32_le(s, 0x00020000); /* map0 */
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint32_le(s, 0x00020004); /* map1 */
|
|
out_uint32_le(s, control_code);
|
|
out_uint32_le(s, send_bytes);
|
|
val = send_bytes > 0 ? 0x00020008 : 0;
|
|
out_uint32_le(s, val); /* map2 */
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, recv_bytes);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint8a(s, context, context_bytes);
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint8a(s, card, card_bytes);
|
|
if (send_bytes > 0)
|
|
{
|
|
out_uint32_le(s, send_bytes);
|
|
out_uint8a(s, send_data, send_bytes);
|
|
}
|
|
else
|
|
{
|
|
out_uint32_le(s, 0x00000000);
|
|
}
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 8;
|
|
out_uint32_le(s, bytes);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "", s->data, bytes);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
free_stream(s);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Cancel any outstanding calls
|
|
*****************************************************************************/
|
|
static int
|
|
scard_send_Cancel(IRP *irp, char *context, int context_bytes)
|
|
{
|
|
/* see [MS-RDPESC] 3.1.4.27 */
|
|
|
|
SMARTCARD *sc;
|
|
struct stream *s;
|
|
int bytes;
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return 1;
|
|
}
|
|
|
|
if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_CANCEL)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl");
|
|
return 1;
|
|
}
|
|
|
|
s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
|
|
out_uint32_le(s, 0x00000000);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint32_le(s, 0x00020000);
|
|
out_uint32_le(s, context_bytes);
|
|
out_uint8a(s, context, context_bytes);
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 8;
|
|
out_uint32_le(s, bytes);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
free_stream(s);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get reader attributes
|
|
*****************************************************************************/
|
|
static int
|
|
scard_send_GetAttrib(IRP *irp, char *card, int card_bytes, READER_STATE *rs)
|
|
{
|
|
/* see [MS-RDPESC] 2.2.2.21 */
|
|
|
|
SMARTCARD *sc;
|
|
struct stream *s;
|
|
int bytes;
|
|
|
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
|
|
return 1;
|
|
}
|
|
|
|
if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_GETATTRIB)) == NULL)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl");
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* command format
|
|
*
|
|
* ......
|
|
* 20 bytes padding
|
|
* u32 4 bytes len 8, LE, v1
|
|
* u32 4 bytes filler
|
|
* 24 bytes unused (s->p currently pointed here at unused[0])
|
|
* u32 4 bytes dwAttribId
|
|
* 4 bytes unused
|
|
* u32 4 bytes dwAttrLen
|
|
* 8 bytes unused
|
|
* u32 4 bytes handle len
|
|
* u32 4 bytes handle
|
|
*/
|
|
|
|
xstream_seek(s, 24); /* TODO */
|
|
out_uint32_le(s, rs->dwAttribId);
|
|
out_uint32_le(s, 0);
|
|
out_uint32_le(s, rs->dwAttrLen);
|
|
xstream_seek(s, 8);
|
|
out_uint32_le(s, card_bytes);
|
|
out_uint8a(s, card, card_bytes);
|
|
|
|
s_mark_end(s);
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
bytes = (int) (s->end - s->p);
|
|
bytes -= 28;
|
|
out_uint32_le(s, bytes);
|
|
|
|
bytes = (int) (s->end - s->data);
|
|
|
|
/* send to client */
|
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
|
|
|
free_stream(s);
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** **
|
|
** local callbacks into this module **
|
|
** **
|
|
******************************************************************************/
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_EstablishContext_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_establish_context_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_ReleaseContext_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_release_context_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_IsContextValid_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_is_context_valid_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_ListReaders_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_list_readers_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_GetStatusChange_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_get_status_change_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_Connect_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
|
|
scard_function_connect_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_Reconnect_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_reconnect_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_BeginTransaction_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_begin_transaction_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_EndTransaction_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_end_transaction_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_Status_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_status_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_Disconnect_Return(struct stream *s, IRP *irp,
|
|
tui32 DeviceId, tui32 CompletionId,
|
|
tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_disconnect_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_Transmit_Return(struct stream *s, IRP *irp, tui32 DeviceId,
|
|
tui32 CompletionId, tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_transmit_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_Control_Return(struct stream *s, IRP *irp, tui32 DeviceId,
|
|
tui32 CompletionId, tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_control_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_Cancel_Return(struct stream *s, IRP *irp, tui32 DeviceId,
|
|
tui32 CompletionId, tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_cancel_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
scard_handle_GetAttrib_Return(struct stream *s, IRP *irp, tui32 DeviceId,
|
|
tui32 CompletionId, tui32 IoStatus)
|
|
{
|
|
tui32 len;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
|
|
|
|
/* sanity check */
|
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
|
|
return;
|
|
}
|
|
|
|
/* get OutputBufferLen */
|
|
xstream_rd_u32_le(s, len);
|
|
scard_function_get_attrib_return(irp->user_data, s, len, IoStatus);
|
|
devredir_irp_delete(irp);
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
|
|
}
|