diff options
Diffstat (limited to 'sesman/chansrv/smartcard.c')
-rw-r--r-- | sesman/chansrv/smartcard.c | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/sesman/chansrv/smartcard.c b/sesman/chansrv/smartcard.c new file mode 100644 index 00000000..3fdb6723 --- /dev/null +++ b/sesman/chansrv/smartcard.c @@ -0,0 +1,528 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@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 + */ + +#include "os_calls.h" +#include "smartcard.h" +#include "log.h" +#include "irp.h" +#include "devredir.h" + +/* + * TODO + * + * 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 + */ + +/* module based logging */ +#define LOG_ERROR 0 +#define LOG_INFO 1 +#define LOG_DEBUG 2 + +#ifndef LOG_LEVEL +#define LOG_LEVEL LOG_DEBUG +#endif + +#define log_error(_params...) \ +{ \ + g_write("[%10.10u]: SMART_CARD %s: %d : ERROR: ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ +} + +#define log_info(_params...) \ +{ \ + if (LOG_INFO <= LOG_LEVEL) \ + { \ + g_write("[%10.10u]: SMART_CARD %s: %d : ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } \ +} + +#define log_debug(_params...) \ +{ \ + if (LOG_DEBUG <= LOG_LEVEL) \ + { \ + g_write("[%10.10u]: SMART_CARD %s: %d : ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } \ +} + +/* [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 0x000900A0 /* GetStatusChangeA */ +#define SCARD_IOCTL_CANCEL 0x000900A8 /* Cancel */ +#define SCARD_IOCTL_CONNECT 0x000900AC /* ConnectA */ +#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 0x000900C8 /* StatusA */ +#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 + +#define MAX_SMARTCARDS 16 + +/* stores info about a smart card */ +typedef struct smartcard +{ + tui32 DeviceId; + char Context[16]; /* opaque context; save as passed to us */ + int Context_len; /* Context len in bytes */ +} SMARTCARD; + +SMARTCARD *smartcards[MAX_SMARTCARDS]; +int g_smartcards_inited = 0; + +extern tui32 g_completion_id; +extern int g_rdpdr_chan_id; /* in chansrv.c */ + +/* forward declarations specific to this file */ +static void scard_send_EstablishContext(IRP *irp); +static void scard_send_ListReaders(IRP *irp, int wide); +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(); +static void scard_release_resources(); + +/****************************************************************************** +** non static functions ** +******************************************************************************/ + +void scard_device_announce(tui32 device_id) +{ + IRP *irp; + + log_debug("entered: device_id=%d", device_id); + + if (!g_smartcards_inited) + { + g_memset(&smartcards, 0, sizeof(smartcards)); + g_smartcards_inited = 1; + } + + if ((irp = devredir_irp_new()) == NULL) + { + log_error("system out of memory"); + return; + } + + irp->scard_index = scard_add_new_device(device_id); + if (irp->scard_index < 0) + { + log_debug("NOT adding smartcard with DeviceId=%d to list", device_id); + devredir_irp_delete(irp); + return; + } + + log_debug("added smartcard with DeviceId=%d to list", device_id); + + irp->CompletionId = g_completion_id++; + irp->DeviceId = device_id; + irp->callback = scard_handle_EstablishContext_Return; + + scard_send_EstablishContext(irp); + log_debug("leaving"); +} + +/****************************************************************************** +** callbacks into this module ** +******************************************************************************/ + +void scard_handle_EstablishContext_Return(struct stream *s, IRP *irp, + tui32 DeviceId, tui32 CompletionId, + tui32 IoStatus) +{ + tui32 len; + int tmp; + SMARTCARD *sc; + + log_debug("entered"); + + /* sanity check */ + if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) + { + log_error("DeviceId/CompletionId do not match those in IRP"); + return; + } + + if (IoStatus != 0) + { + log_error("failed to establish context - device not usable"); + /* LK_TODO delete irp and smartcard entry */ + return; + } + + sc = smartcards[irp->scard_index]; + + /* get OutputBufferLen */ + xstream_rd_u32_le(s, len); + + /* LK_TODO */ + g_hexdump(s->p, len); + + xstream_rd_u32_le(s, tmp); /* should be len 8, LE, V1 */ + xstream_rd_u32_le(s, tmp); /* marshalling flag */ + xstream_rd_u32_le(s, tmp); /* ?? */ + xstream_rd_u32_le(s, tmp); /* ?? */ + xstream_rd_u32_le(s, tmp); /* ?? */ + xstream_rd_u32_le(s, tmp); /* ?? */ + xstream_rd_u32_le(s, tmp); /* ?? */ + xstream_rd_u32_le(s, len); /* len of context in bytes */ + sc->Context_len = len; + xstream_copyout(sc->Context, s, len); + + if (LOG_LEVEL == LOG_DEBUG) + { + log_debug("dumping context (%d bytes)", sc->Context_len); + g_hexdump(sc->Context, sc->Context_len); + } + + irp->callback = scard_handle_ListReaders_Return; + scard_send_ListReaders(irp, 1); + + /* LK_TODO need to delete IRP */ + log_debug("leaving"); +} + +void scard_handle_ListReaders_Return(struct stream *s, IRP *irp, + tui32 DeviceId, tui32 CompletionId, + tui32 IoStatus) +{ + tui32 len; + + log_debug("entered"); + + /* sanity check */ + if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) + { + log_error("DeviceId/CompletionId do not match those in IRP"); + return; + } + + if (IoStatus != 0) + { + log_error("failed to list readers - device not usable"); + /* LK_TODO delete irp and smartcard entry */ + return; + } + + /* get OutputBufferLen */ + xstream_rd_u32_le(s, len); + + /* LK_TODO */ + log_debug("dumping %d bytes", len); + g_hexdump(s->p, len); + + log_debug("leaving"); +} + +/****************************************************************************** +** static functions local to this file ** +******************************************************************************/ + +/** + * + *****************************************************************************/ + +static void scard_send_EstablishContext(IRP *irp) +{ + struct stream *s; + int bytes; + + if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_ESTABLISH_CONTEXT)) == NULL) + return; + + xstream_wr_u32_le(s, 0x08); /* len */ + xstream_wr_u32_le(s, 0); /* unused */ + xstream_wr_u32_le(s, SCARD_SCOPE_SYSTEM); /* Ioctl specific data */ + xstream_wr_u32_le(s, 0); /* don't know what this is, */ + /* but Win7 is sending it */ + /* get stream len */ + bytes = xstream_len(s); + + /* InputBufferLength is number of bytes AFTER 20 byte padding */ + *(s->data + 28) = bytes - 56; + + /* send to client */ + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + xstream_free(s); +} + +/** + * + *****************************************************************************/ + +static void scard_send_ListReaders(IRP *irp, int wide) +{ + /* see [MS-RDPESC] 2.2.2.4 */ + + SMARTCARD *sc; + struct stream *s; + int bytes; + tui32 ioctl; + + if ((sc = smartcards[irp->scard_index]) == NULL) + { + log_error("smartcards[%d] is NULL", irp->scard_index); + return; + } + + ioctl = (wide > 0) ? SCARD_IOCTL_LIST_READERS_W : + SCARD_IOCTL_LIST_READERS_A; + + if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL) + return; + + xstream_wr_u32_le(s, 72); /* number of bytes to follow */ + xstream_seek(s, 0x1c); /* freerdp does not use this */ + + /* insert context */ + xstream_wr_u32_le(s, sc->Context_len); + xstream_copyin(s, sc->Context, sc->Context_len); + + xstream_wr_u32_le(s, 36); /* length of mszGroups */ + xstream_wr_u16_le(s, 0x0053); + xstream_wr_u16_le(s, 0x0043); + xstream_wr_u16_le(s, 0x0061); + xstream_wr_u16_le(s, 0x0072); + xstream_wr_u16_le(s, 0x0064); + xstream_wr_u16_le(s, 0x0024); + xstream_wr_u16_le(s, 0x0041); + xstream_wr_u16_le(s, 0x006c); + xstream_wr_u16_le(s, 0x006c); + xstream_wr_u16_le(s, 0x0052); + xstream_wr_u16_le(s, 0x0065); + xstream_wr_u16_le(s, 0x0061); + xstream_wr_u16_le(s, 0x0064); + xstream_wr_u16_le(s, 0x0065); + xstream_wr_u16_le(s, 0x0072); + xstream_wr_u16_le(s, 0x0073); + + xstream_wr_u32_le(s, 0x00); + + /* get stream len */ + bytes = xstream_len(s); + + /* InputBufferLength is number of bytes AFTER 20 byte padding */ + *(s->data + 28) = bytes - 56; + + /* send to client */ + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + xstream_free(s); + + /* + scard_device_control: dumping 120 bytes of data + 0000 00 08 00 00 58 00 00 00 2c 00 09 00 00 00 00 00 ....X...,....... + 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0020 01 10 08 00 cc cc cc cc 48 00 00 00 00 00 00 00 ........H....... + 0030 04 00 00 00 00 00 02 00 24 00 00 00 04 00 02 00 ........$....... + 0040 00 00 00 00 ff ff ff ff 04 00 00 00 84 db 03 01 ................ + 0050 24 00 00 00 53 00 43 00 61 00 72 00 64 00 24 00 $...S.C.a.r.d.$. + 0060 41 00 6c 00 6c 00 52 00 65 00 61 00 64 00 65 00 A.l.l.R.e.a.d.e. + 0070 72 00 73 00 00 00 00 00 r.s..... + scard_device_control: output_len=2048 input_len=88 ioctl_code=0x9002c + */ + + /* + scard_device_control: dumping 120 bytes of data + 0000 00 08 00 00 80 00 00 00 14 00 09 00 00 00 00 00 ................ + 0010 2e 2e 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................ + 0020 01 10 08 00 cc cc cc cc 48 00 00 00 00 00 00 00 ........H....... + 0030 02 00 00 00 00 00 00 00 72 64 00 00 00 00 00 00 ........rd...... + 0040 81 27 00 00 00 00 00 00 04 00 00 00 84 b3 03 01 .'.............. + 0050 24 00 00 00 53 00 43 00 61 00 72 00 64 00 24 00 $...S.C.a.r.d.$. + 0060 41 00 6c 00 6c 00 52 00 65 00 61 00 64 00 65 00 A.l.l.R.e.a.d.e. + 0070 72 00 73 00 00 00 00 00 r.s..... + scard_device_control: output_len=2048 input_len=128 ioctl_code=0x90014 + */ +} + +/** + * Crate 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 * 3); + if (s == NULL) + { + log_error("system out of memory"); + return s; + } + + devredir_insert_DeviceIoRequest(s, + irp->DeviceId, + irp->FileId, + irp->CompletionId, + IRP_MJ_DEVICE_CONTROL, + 0); + + xstream_wr_u32_le(s, 2048); /* OutputBufferLength */ + xstream_wr_u32_le(s, 0); /* InputBufferLength - insert later */ + xstream_wr_u32_le(s, ioctl); /* Ioctl Code */ + xstream_seek(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) + return -1; + + if ((sc = g_malloc(sizeof(SMARTCARD), 1)) == NULL) + { + log_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() +{ + int i; + + for (i = 0; i < MAX_SMARTCARDS; i++) + { + if (smartcards[i] == NULL) + { + log_debug("found free slot at index %d", i); + return i; + } + } + + log_error("too many smart card devices; rejecting this one"); + return -1; +} + +/** + * Release resources prior to shutting down + *****************************************************************************/ + +static void scard_release_resources() +{ + int i; + + for (i = 0; i < MAX_SMARTCARDS; i++) + { + if (smartcards[i] != NULL) + { + g_free(smartcards[i]); + smartcards[i] = NULL; + } + } +} + +/** + * + *****************************************************************************/ |