diff options
Diffstat (limited to 'libfreerdp/utils')
-rw-r--r-- | libfreerdp/utils/CMakeLists.txt | 56 | ||||
-rw-r--r-- | libfreerdp/utils/cliprdr_utils.c | 258 | ||||
-rw-r--r-- | libfreerdp/utils/drdynvc.c | 50 | ||||
-rw-r--r-- | libfreerdp/utils/encoded_types.c | 187 | ||||
-rw-r--r-- | libfreerdp/utils/gfx.c | 95 | ||||
-rw-r--r-- | libfreerdp/utils/http.c | 386 | ||||
-rw-r--r-- | libfreerdp/utils/passphrase.c | 299 | ||||
-rw-r--r-- | libfreerdp/utils/pcap.c | 286 | ||||
-rw-r--r-- | libfreerdp/utils/profiler.c | 98 | ||||
-rw-r--r-- | libfreerdp/utils/rdpdr_utils.c | 598 | ||||
-rw-r--r-- | libfreerdp/utils/ringbuffer.c | 295 | ||||
-rw-r--r-- | libfreerdp/utils/signal.c | 264 | ||||
-rw-r--r-- | libfreerdp/utils/smartcard_call.c | 2019 | ||||
-rw-r--r-- | libfreerdp/utils/smartcard_operations.c | 1048 | ||||
-rw-r--r-- | libfreerdp/utils/smartcard_pack.c | 3649 | ||||
-rw-r--r-- | libfreerdp/utils/stopwatch.c | 98 | ||||
-rw-r--r-- | libfreerdp/utils/string.c | 105 | ||||
-rw-r--r-- | libfreerdp/utils/test/CMakeLists.txt | 28 | ||||
-rw-r--r-- | libfreerdp/utils/test/TestPodArrays.c | 131 | ||||
-rw-r--r-- | libfreerdp/utils/test/TestRingBuffer.c | 226 |
20 files changed, 10176 insertions, 0 deletions
diff --git a/libfreerdp/utils/CMakeLists.txt b/libfreerdp/utils/CMakeLists.txt new file mode 100644 index 0000000..52690bf --- /dev/null +++ b/libfreerdp/utils/CMakeLists.txt @@ -0,0 +1,56 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# libfreerdp-utils cmake build script +# +# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(MODULE_NAME "freerdp-utils") +set(MODULE_PREFIX "FREERDP_UTILS") + +set(${MODULE_PREFIX}_SRCS + encoded_types.c + passphrase.c + cliprdr_utils.c + rdpdr_utils.c + pcap.c + profiler.c + ringbuffer.c + signal.c + string.c + gfx.c + drdynvc.c + smartcard_operations.c + smartcard_pack.c + smartcard_call.c + stopwatch.c + http.c +) + +freerdp_module_add(${${MODULE_PREFIX}_SRCS}) + +freerdp_library_add(${CMAKE_THREAD_LIBS_INIT}) + +if(WIN32) + freerdp_library_add(ws2_32) + freerdp_library_add(credui) + freerdp_library_add(cfgmgr32) +endif() + +if(${CMAKE_SYSTEM_NAME} MATCHES SunOS) + freerdp_library_add(rt) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/libfreerdp/utils/cliprdr_utils.c b/libfreerdp/utils/cliprdr_utils.c new file mode 100644 index 0000000..5835ab6 --- /dev/null +++ b/libfreerdp/utils/cliprdr_utils.c @@ -0,0 +1,258 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Clipboard Virtual Channel Extension + * + * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * Copyright 2022 Armin Novak <anovak@thincast.com + * Copyright 2022 Thincast Technologies GmbH + * + * 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. + */ + +#include <winpr/stream.h> +#include <freerdp/utils/cliprdr_utils.h> +#include <freerdp/channels/cliprdr.h> + +#include <freerdp/log.h> +#define TAG FREERDP_TAG("utils." CLIPRDR_SVC_CHANNEL_NAME) + +#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520) +#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024) + +static UINT64 filetime_to_uint64(FILETIME value) +{ + UINT64 converted = 0; + converted |= (UINT32)value.dwHighDateTime; + converted <<= 32; + converted |= (UINT32)value.dwLowDateTime; + return converted; +} + +static FILETIME uint64_to_filetime(UINT64 value) +{ + FILETIME converted; + converted.dwLowDateTime = (UINT32)(value >> 0); + converted.dwHighDateTime = (UINT32)(value >> 32); + return converted; +} + +/** + * Parse a packed file list. + * + * The resulting array must be freed with the `free()` function. + * + * @param [in] format_data packed `CLIPRDR_FILELIST` to parse. + * @param [in] format_data_length length of `format_data` in bytes. + * @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs. + * @param [out] file_descriptor_count number of elements in `file_descriptor_array`. + * + * @returns 0 on success, otherwise a Win32 error code. + */ +UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length, + FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count) +{ + UINT result = NO_ERROR; + UINT32 count = 0; + wStream sbuffer; + wStream* s = NULL; + + if (!format_data || !file_descriptor_array || !file_descriptor_count) + return ERROR_BAD_ARGUMENTS; + + s = Stream_StaticConstInit(&sbuffer, format_data, format_data_length); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + { + result = ERROR_INCORRECT_SIZE; + goto out; + } + + Stream_Read_UINT32(s, count); /* cItems (4 bytes) */ + + if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, count, CLIPRDR_FILEDESCRIPTOR_SIZE)) + { + result = ERROR_INCORRECT_SIZE; + goto out; + } + + *file_descriptor_count = count; + *file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW)); + if (!*file_descriptor_array) + { + result = ERROR_NOT_ENOUGH_MEMORY; + goto out; + } + + for (UINT32 i = 0; i < count; i++) + { + FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]); + + if (!cliprdr_read_filedescriptor(s, file)) + goto out; + } + + if (Stream_GetRemainingLength(s) > 0) + WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes", + Stream_GetRemainingLength(s)); +out: + + return result; +} + +BOOL cliprdr_read_filedescriptor(wStream* s, FILEDESCRIPTORW* file) +{ + UINT64 tmp = 0; + WINPR_ASSERT(file); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(FILEDESCRIPTORW))) + return FALSE; + + Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */ + Stream_Read_UINT32(s, file->clsid.Data1); + Stream_Read_UINT16(s, file->clsid.Data2); + Stream_Read_UINT16(s, file->clsid.Data3); + Stream_Read(s, &file->clsid.Data4, sizeof(file->clsid.Data4)); + Stream_Read_INT32(s, file->sizel.cx); + Stream_Read_INT32(s, file->sizel.cy); + Stream_Read_INT32(s, file->pointl.x); + Stream_Read_INT32(s, file->pointl.y); + Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */ + Stream_Read_UINT64(s, tmp); /* ftCreationTime (8 bytes) */ + file->ftCreationTime = uint64_to_filetime(tmp); + Stream_Read_UINT64(s, tmp); /* ftLastAccessTime (8 bytes) */ + file->ftLastAccessTime = uint64_to_filetime(tmp); + Stream_Read_UINT64(s, tmp); /* lastWriteTime (8 bytes) */ + file->ftLastWriteTime = uint64_to_filetime(tmp); + Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */ + Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */ + Stream_Read_UTF16_String(s, file->cFileName, + ARRAYSIZE(file->cFileName)); /* cFileName (520 bytes) */ + return TRUE; +} + +BOOL cliprdr_write_filedescriptor(wStream* s, const FILEDESCRIPTORW* file) +{ + WINPR_ASSERT(file); + + if (!Stream_EnsureRemainingCapacity(s, sizeof(FILEDESCRIPTORW))) + return FALSE; + + Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */ + + Stream_Write_UINT32(s, file->clsid.Data1); + Stream_Write_UINT16(s, file->clsid.Data2); + Stream_Write_UINT16(s, file->clsid.Data3); + Stream_Write(s, &file->clsid.Data4, sizeof(file->clsid.Data4)); + Stream_Write_INT32(s, file->sizel.cx); + Stream_Write_INT32(s, file->sizel.cy); + Stream_Write_INT32(s, file->pointl.x); + Stream_Write_INT32(s, file->pointl.y); + Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */ + Stream_Write_UINT64(s, filetime_to_uint64(file->ftCreationTime)); + Stream_Write_UINT64(s, filetime_to_uint64(file->ftLastAccessTime)); + Stream_Write_UINT64(s, filetime_to_uint64(file->ftLastWriteTime)); /* lastWriteTime (8 bytes) */ + Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */ + Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */ + Stream_Write_UTF16_String(s, file->cFileName, + ARRAYSIZE(file->cFileName)); /* cFileName (520 bytes) */ + return TRUE; +} + +/** + * Serialize a packed file list. + * + * The resulting format data must be freed with the `free()` function. + * + * @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize. + * @param [in] file_descriptor_count number of elements in `file_descriptor_array`. + * @param [out] format_data serialized CLIPRDR_FILELIST. + * @param [out] format_data_length length of `format_data` in bytes. + * + * @returns 0 on success, otherwise a Win32 error code. + */ +UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array, + UINT32 file_descriptor_count, BYTE** format_data, + UINT32* format_data_length) +{ + return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array, + file_descriptor_count, format_data, format_data_length); +} + +UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array, + UINT32 file_descriptor_count, BYTE** format_data, + UINT32* format_data_length) +{ + UINT result = NO_ERROR; + size_t len = 0; + wStream* s = NULL; + + if (!file_descriptor_array || !format_data || !format_data_length) + return ERROR_BAD_ARGUMENTS; + + if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0) + { + WLog_WARN(TAG, "No file clipboard support annouonced!"); + return ERROR_BAD_ARGUMENTS; + } + + s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */ + + for (UINT32 i = 0; i < file_descriptor_count; i++) + { + const FILEDESCRIPTORW* file = &file_descriptor_array[i]; + + /* + * There is a known issue with Windows server getting stuck in + * an infinite loop when downloading files that are larger than + * 2 gigabytes. Do not allow clients to send such file lists. + * + * https://support.microsoft.com/en-us/help/2258090 + */ + if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0) + { + if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE)) + { + WLog_ERR(TAG, "cliprdr does not support files over 2 GB"); + result = ERROR_FILE_TOO_LARGE; + goto error; + } + } + + if (!cliprdr_write_filedescriptor(s, file)) + goto error; + } + + Stream_SealLength(s); + + Stream_GetBuffer(s, *format_data); + Stream_GetLength(s, len); + if (len > UINT32_MAX) + goto error; + + *format_data_length = (UINT32)len; + + Stream_Free(s, FALSE); + + return result; + +error: + Stream_Free(s, TRUE); + + return result; +} diff --git a/libfreerdp/utils/drdynvc.c b/libfreerdp/utils/drdynvc.c new file mode 100644 index 0000000..ba9bf5a --- /dev/null +++ b/libfreerdp/utils/drdynvc.c @@ -0,0 +1,50 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * drdynvc Utils - Helper functions converting something to string + * + * Copyright 2023 Armin Novak <armin.novak@thincast.com> + * Copyright 2023 Thincast Technologies GmbH + * + * 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. + */ + +#include <freerdp/utils/drdynvc.h> +#include <freerdp/channels/drdynvc.h> + +const char* drdynvc_get_packet_type(BYTE cmd) +{ + switch (cmd) + { + case CREATE_REQUEST_PDU: + return "CREATE_REQUEST_PDU"; + case DATA_FIRST_PDU: + return "DATA_FIRST_PDU"; + case DATA_PDU: + return "DATA_PDU"; + case CLOSE_REQUEST_PDU: + return "CLOSE_REQUEST_PDU"; + case CAPABILITY_REQUEST_PDU: + return "CAPABILITY_REQUEST_PDU"; + case DATA_FIRST_COMPRESSED_PDU: + return "DATA_FIRST_COMPRESSED_PDU"; + case DATA_COMPRESSED_PDU: + return "DATA_COMPRESSED_PDU"; + case SOFT_SYNC_REQUEST_PDU: + return "SOFT_SYNC_REQUEST_PDU"; + case SOFT_SYNC_RESPONSE_PDU: + return "SOFT_SYNC_RESPONSE_PDU"; + default: + return "UNKNOWN"; + } +} diff --git a/libfreerdp/utils/encoded_types.c b/libfreerdp/utils/encoded_types.c new file mode 100644 index 0000000..68e6e4d --- /dev/null +++ b/libfreerdp/utils/encoded_types.c @@ -0,0 +1,187 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Helper functions to parse encoded types into regular ones + * + * Copyright 2023 Pascal Nowack <Pascal.Nowack@gmx.de> + * + * 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. + */ + +#include <freerdp/config.h> + +#include <freerdp/channels/log.h> +#include <freerdp/utils/encoded_types.h> +#include <math.h> + +#define TAG CHANNELS_TAG("encoded_types") + +typedef enum +{ + ONE_BYTE_VAL, + TWO_BYTE_VAL, + THREE_BYTE_VAL, + FOUR_BYTE_VAL, + FIVE_BYTE_VAL, + SIX_BYTE_VAL, + SEVEN_BYTE_VAL, + EIGHT_BYTE_VAL, +} EncodedTypeByteCount; + +typedef enum +{ + POSITIVE_VAL, + NEGATIVE_VAL, +} EncodedTypeSign; + +typedef struct +{ + EncodedTypeByteCount c; + EncodedTypeSign s; + BYTE val1; + BYTE val2; + BYTE val3; + BYTE val4; +} FOUR_BYTE_SIGNED_INTEGER; + +typedef struct +{ + EncodedTypeByteCount c; + EncodedTypeSign s; + BYTE e; + BYTE val1; + BYTE val2; + BYTE val3; + BYTE val4; +} FOUR_BYTE_FLOAT; + +BOOL freerdp_read_four_byte_signed_integer(wStream* s, INT32* value) +{ + FOUR_BYTE_SIGNED_INTEGER si = { 0 }; + BYTE byte = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(value); + + *value = 0; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 1)) + return FALSE; + + Stream_Read_UINT8(s, byte); + + si.c = (byte & 0xC0) >> 6; + si.s = (byte & 0x20) >> 5; + si.val1 = (byte & 0x1F); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, si.c)) + return FALSE; + + switch (si.c) + { + case ONE_BYTE_VAL: + *value = si.val1; + break; + case TWO_BYTE_VAL: + Stream_Read_UINT8(s, si.val2); + *value = (((INT32)si.val1) << 8) | ((INT32)si.val2); + break; + case THREE_BYTE_VAL: + Stream_Read_UINT8(s, si.val2); + Stream_Read_UINT8(s, si.val3); + *value = (((INT32)si.val1) << 16) | (((INT32)si.val2) << 8) | ((INT32)si.val3); + break; + case FOUR_BYTE_VAL: + Stream_Read_UINT8(s, si.val2); + Stream_Read_UINT8(s, si.val3); + Stream_Read_UINT8(s, si.val4); + *value = (((INT32)si.val1) << 24) | (((INT32)si.val2) << 16) | (((INT32)si.val3) << 8) | + ((INT32)si.val4); + break; + case FIVE_BYTE_VAL: + case SIX_BYTE_VAL: + case SEVEN_BYTE_VAL: + case EIGHT_BYTE_VAL: + default: + WLog_ERR(TAG, "Invalid byte count value in si.c: %u", si.c); + return FALSE; + } + + if (si.s == NEGATIVE_VAL) + *value *= -1; + + return TRUE; +} + +BOOL freerdp_read_four_byte_float(wStream* s, double* value) +{ + FOUR_BYTE_FLOAT f = { 0 }; + UINT32 base = 0; + BYTE byte = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(value); + + *value = 0.0; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 1)) + return FALSE; + + Stream_Read_UINT8(s, byte); + + f.c = (byte & 0xC0) >> 6; + f.s = (byte & 0x20) >> 5; + f.e = (byte & 0x1C) >> 2; + f.val1 = (byte & 0x03); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, f.c)) + return FALSE; + + switch (f.c) + { + case ONE_BYTE_VAL: + base = f.val1; + break; + case TWO_BYTE_VAL: + Stream_Read_UINT8(s, f.val2); + base = (((UINT32)f.val1) << 8) | ((UINT32)f.val2); + break; + case THREE_BYTE_VAL: + Stream_Read_UINT8(s, f.val2); + Stream_Read_UINT8(s, f.val3); + base = (((UINT32)f.val1) << 16) | (((UINT32)f.val2) << 8) | ((UINT32)f.val3); + break; + case FOUR_BYTE_VAL: + Stream_Read_UINT8(s, f.val2); + Stream_Read_UINT8(s, f.val3); + Stream_Read_UINT8(s, f.val4); + base = (((UINT32)f.val1) << 24) | (((UINT32)f.val2) << 16) | (((UINT32)f.val3) << 8) | + ((UINT32)f.val4); + break; + case FIVE_BYTE_VAL: + case SIX_BYTE_VAL: + case SEVEN_BYTE_VAL: + case EIGHT_BYTE_VAL: + default: + WLog_ERR(TAG, "Invalid byte count value in f.c: %u", f.c); + return FALSE; + } + + *value = base; + *value /= pow(10, f.e); + + if (f.s == NEGATIVE_VAL) + *value *= -1.0; + + return TRUE; +} diff --git a/libfreerdp/utils/gfx.c b/libfreerdp/utils/gfx.c new file mode 100644 index 0000000..cf459ae --- /dev/null +++ b/libfreerdp/utils/gfx.c @@ -0,0 +1,95 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * GFX Utils - Helper functions converting something to string + * + * Copyright 2022 Armin Novak <armin.novak@thincast.com> + * Copyright 2022 Thincast Technologies GmbH + * + * 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. + */ + +#include <freerdp/utils/gfx.h> +#include <freerdp/channels/rdpgfx.h> + +static const char* RDPGFX_CMDID_STRINGS[] = { "RDPGFX_CMDID_UNUSED_0000", + "RDPGFX_CMDID_WIRETOSURFACE_1", + "RDPGFX_CMDID_WIRETOSURFACE_2", + "RDPGFX_CMDID_DELETEENCODINGCONTEXT", + "RDPGFX_CMDID_SOLIDFILL", + "RDPGFX_CMDID_SURFACETOSURFACE", + "RDPGFX_CMDID_SURFACETOCACHE", + "RDPGFX_CMDID_CACHETOSURFACE", + "RDPGFX_CMDID_EVICTCACHEENTRY", + "RDPGFX_CMDID_CREATESURFACE", + "RDPGFX_CMDID_DELETESURFACE", + "RDPGFX_CMDID_STARTFRAME", + "RDPGFX_CMDID_ENDFRAME", + "RDPGFX_CMDID_FRAMEACKNOWLEDGE", + "RDPGFX_CMDID_RESETGRAPHICS", + "RDPGFX_CMDID_MAPSURFACETOOUTPUT", + "RDPGFX_CMDID_CACHEIMPORTOFFER", + "RDPGFX_CMDID_CACHEIMPORTREPLY", + "RDPGFX_CMDID_CAPSADVERTISE", + "RDPGFX_CMDID_CAPSCONFIRM", + "RDPGFX_CMDID_UNUSED_0014", + "RDPGFX_CMDID_MAPSURFACETOWINDOW", + "RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE", + "RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT", + "RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW" }; + +const char* rdpgfx_get_cmd_id_string(UINT16 cmdId) +{ + if (cmdId <= RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW) + return RDPGFX_CMDID_STRINGS[cmdId]; + else + return "RDPGFX_CMDID_UNKNOWN"; +} + +const char* rdpgfx_get_codec_id_string(UINT16 codecId) +{ + switch (codecId) + { + case RDPGFX_CODECID_UNCOMPRESSED: + return "RDPGFX_CODECID_UNCOMPRESSED"; + + case RDPGFX_CODECID_CAVIDEO: + return "RDPGFX_CODECID_CAVIDEO"; + + case RDPGFX_CODECID_CLEARCODEC: + return "RDPGFX_CODECID_CLEARCODEC"; + + case RDPGFX_CODECID_PLANAR: + return "RDPGFX_CODECID_PLANAR"; + + case RDPGFX_CODECID_AVC420: + return "RDPGFX_CODECID_AVC420"; + + case RDPGFX_CODECID_AVC444: + return "RDPGFX_CODECID_AVC444"; + + case RDPGFX_CODECID_AVC444v2: + return "RDPGFX_CODECID_AVC444v2"; + + case RDPGFX_CODECID_ALPHA: + return "RDPGFX_CODECID_ALPHA"; + + case RDPGFX_CODECID_CAPROGRESSIVE: + return "RDPGFX_CODECID_CAPROGRESSIVE"; + + case RDPGFX_CODECID_CAPROGRESSIVE_V2: + return "RDPGFX_CODECID_CAPROGRESSIVE_V2"; + } + + return "RDPGFX_CODECID_UNKNOWN"; +} diff --git a/libfreerdp/utils/http.c b/libfreerdp/utils/http.c new file mode 100644 index 0000000..70963f0 --- /dev/null +++ b/libfreerdp/utils/http.c @@ -0,0 +1,386 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Simple HTTP client request utility + * + * Copyright 2023 Isaac Klein <fifthdegree@protonmail.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. + */ + +#include <freerdp/config.h> +#include <freerdp/utils/http.h> + +#include <winpr/assert.h> +#include <winpr/string.h> + +#include <openssl/bio.h> +#include <openssl/ssl.h> +#include <openssl/err.h> + +#include <freerdp/log.h> +#define TAG FREERDP_TAG("utils.http") + +static const char get_header_fmt[] = "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" + "\r\n"; + +static const char post_header_fmt[] = "POST %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: %lu\r\n" + "\r\n"; + +#define log_errors(log, msg) log_errors_(log, msg, __FILE__, __func__, __LINE__) +static void log_errors_(wLog* log, const char* msg, const char* file, const char* fkt, size_t line) +{ + const DWORD level = WLOG_ERROR; + unsigned long ec = 0; + + if (!WLog_IsLevelActive(log, level)) + return; + + BOOL error_logged = FALSE; + while ((ec = ERR_get_error())) + { + error_logged = TRUE; + WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, level, line, file, fkt, "%s: %s", msg, + ERR_error_string(ec, NULL)); + } + if (!error_logged) + WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, level, line, file, fkt, + "%s (no details available)", msg); +} + +static int get_line(BIO* bio, char* buffer, size_t size) +{ +#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3) + if (size <= 1) + return -1; + + size_t pos = 0; + do + { + int rc = BIO_read(bio, &buffer[pos], 1); + if (rc <= 0) + return rc; + char cur = buffer[pos]; + pos += rc; + if ((cur == '\n') || (pos >= size - 1)) + { + buffer[pos] = '\0'; + return (int)pos; + } + } while (1); +#else + return BIO_get_line(bio, buffer, size); +#endif +} + +BOOL freerdp_http_request(const char* url, const char* body, long* status_code, BYTE** response, + size_t* response_length) +{ + BOOL ret = FALSE; + char* hostname = NULL; + const char* path = NULL; + char* headers = NULL; + size_t size = 0; + int status = 0; + char buffer[1024] = { 0 }; + BIO* bio = NULL; + SSL_CTX* ssl_ctx = NULL; + SSL* ssl = NULL; + + WINPR_ASSERT(status_code); + WINPR_ASSERT(response); + WINPR_ASSERT(response_length); + + wLog* log = WLog_Get(TAG); + WINPR_ASSERT(log); + + *response = NULL; + + if (!url || strnlen(url, 8) < 8 || strncmp(url, "https://", 8) != 0 || + !(path = strchr(url + 8, '/'))) + { + WLog_Print(log, WLOG_ERROR, "invalid url provided"); + goto out; + } + + const size_t len = path - (url + 8); + hostname = strndup(&url[8], len); + if (!hostname) + return FALSE; + + size_t blen = 0; + if (body) + { + blen = strlen(body); + if (winpr_asprintf(&headers, &size, post_header_fmt, path, hostname, blen) < 0) + return FALSE; + } + else + { + if (winpr_asprintf(&headers, &size, get_header_fmt, path, hostname) < 0) + return FALSE; + } + + ssl_ctx = SSL_CTX_new(TLS_client_method()); + + if (!ssl_ctx) + { + log_errors(log, "could not set up ssl context"); + goto out; + } + + if (!SSL_CTX_set_default_verify_paths(ssl_ctx)) + { + log_errors(log, "could not set ssl context verify paths"); + goto out; + } + + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + + bio = BIO_new_ssl_connect(ssl_ctx); + if (!bio) + { + log_errors(log, "could not set up connection"); + goto out; + } + + if (BIO_set_conn_port(bio, "https") <= 0) + { + log_errors(log, "could not set port"); + goto out; + } + + if (!BIO_set_conn_hostname(bio, hostname)) + { + log_errors(log, "could not set hostname"); + goto out; + } + + BIO_get_ssl(bio, &ssl); + if (!ssl) + { + log_errors(log, "could not get ssl"); + goto out; + } + + if (!SSL_set_tlsext_host_name(ssl, hostname)) + { + log_errors(log, "could not set sni hostname"); + goto out; + } + + WLog_Print(log, WLOG_DEBUG, "headers:\n%s", headers); + ERR_clear_error(); + if (BIO_write(bio, headers, strnlen(headers, size)) < 0) + { + log_errors(log, "could not write headers"); + goto out; + } + + if (body) + { + WLog_Print(log, WLOG_DEBUG, "body:\n%s", body); + + if (blen > INT_MAX) + { + WLog_Print(log, WLOG_ERROR, "body too long!"); + goto out; + } + + ERR_clear_error(); + if (BIO_write(bio, body, blen) < 0) + { + log_errors(log, "could not write body"); + goto out; + } + } + + status = get_line(bio, buffer, sizeof(buffer)); + if (status <= 0) + { + log_errors(log, "could not read response"); + goto out; + } + + if (sscanf(buffer, "HTTP/1.1 %li %*[^\r\n]\r\n", status_code) < 1) + { + WLog_Print(log, WLOG_ERROR, "invalid HTTP status line"); + goto out; + } + + do + { + status = get_line(bio, buffer, sizeof(buffer)); + if (status <= 0) + { + log_errors(log, "could not read response"); + goto out; + } + + char* val = NULL; + char* name = strtok_s(buffer, ":", &val); + if (name && (_stricmp(name, "content-length") == 0)) + { + errno = 0; + *response_length = strtoul(val, NULL, 10); + if (errno) + { + char ebuffer[256] = { 0 }; + WLog_Print(log, WLOG_ERROR, "could not parse content length (%s): %s [%d]", val, + winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno); + goto out; + } + } + } while (strcmp(buffer, "\r\n") != 0); + + if (*response_length > 0) + { + if (*response_length > INT_MAX) + { + WLog_Print(log, WLOG_ERROR, "response too long!"); + goto out; + } + + *response = calloc(1, *response_length + 1); + if (!*response) + goto out; + + BYTE* p = *response; + int left = *response_length; + while (left > 0) + { + status = BIO_read(bio, p, left); + if (status <= 0) + { + log_errors(log, "could not read response"); + goto out; + } + p += status; + left -= status; + } + } + + ret = TRUE; + +out: + if (!ret) + { + free(*response); + *response = NULL; + *response_length = 0; + } + free(hostname); + free(headers); + BIO_free_all(bio); + SSL_CTX_free(ssl_ctx); + return ret; +} + +const char* freerdp_http_status_string(long status) +{ + switch (status) + { + case HTTP_STATUS_CONTINUE: + return "HTTP_STATUS_CONTINUE"; + case HTTP_STATUS_SWITCH_PROTOCOLS: + return "HTTP_STATUS_SWITCH_PROTOCOLS"; + case HTTP_STATUS_OK: + return "HTTP_STATUS_OK"; + case HTTP_STATUS_CREATED: + return "HTTP_STATUS_CREATED"; + case HTTP_STATUS_ACCEPTED: + return "HTTP_STATUS_ACCEPTED"; + case HTTP_STATUS_PARTIAL: + return "HTTP_STATUS_PARTIAL"; + case HTTP_STATUS_NO_CONTENT: + return "HTTP_STATUS_NO_CONTENT"; + case HTTP_STATUS_RESET_CONTENT: + return "HTTP_STATUS_RESET_CONTENT"; + case HTTP_STATUS_PARTIAL_CONTENT: + return "HTTP_STATUS_PARTIAL_CONTENT"; + case HTTP_STATUS_WEBDAV_MULTI_STATUS: + return "HTTP_STATUS_WEBDAV_MULTI_STATUS"; + case HTTP_STATUS_AMBIGUOUS: + return "HTTP_STATUS_AMBIGUOUS"; + case HTTP_STATUS_MOVED: + return "HTTP_STATUS_MOVED"; + case HTTP_STATUS_REDIRECT: + return "HTTP_STATUS_REDIRECT"; + case HTTP_STATUS_REDIRECT_METHOD: + return "HTTP_STATUS_REDIRECT_METHOD"; + case HTTP_STATUS_NOT_MODIFIED: + return "HTTP_STATUS_NOT_MODIFIED"; + case HTTP_STATUS_USE_PROXY: + return "HTTP_STATUS_USE_PROXY"; + case HTTP_STATUS_REDIRECT_KEEP_VERB: + return "HTTP_STATUS_REDIRECT_KEEP_VERB"; + case HTTP_STATUS_BAD_REQUEST: + return "HTTP_STATUS_BAD_REQUEST"; + case HTTP_STATUS_DENIED: + return "HTTP_STATUS_DENIED"; + case HTTP_STATUS_PAYMENT_REQ: + return "HTTP_STATUS_PAYMENT_REQ"; + case HTTP_STATUS_FORBIDDEN: + return "HTTP_STATUS_FORBIDDEN"; + case HTTP_STATUS_NOT_FOUND: + return "HTTP_STATUS_NOT_FOUND"; + case HTTP_STATUS_BAD_METHOD: + return "HTTP_STATUS_BAD_METHOD"; + case HTTP_STATUS_NONE_ACCEPTABLE: + return "HTTP_STATUS_NONE_ACCEPTABLE"; + case HTTP_STATUS_PROXY_AUTH_REQ: + return "HTTP_STATUS_PROXY_AUTH_REQ"; + case HTTP_STATUS_REQUEST_TIMEOUT: + return "HTTP_STATUS_REQUEST_TIMEOUT"; + case HTTP_STATUS_CONFLICT: + return "HTTP_STATUS_CONFLICT"; + case HTTP_STATUS_GONE: + return "HTTP_STATUS_GONE"; + case HTTP_STATUS_LENGTH_REQUIRED: + return "HTTP_STATUS_LENGTH_REQUIRED"; + case HTTP_STATUS_PRECOND_FAILED: + return "HTTP_STATUS_PRECOND_FAILED"; + case HTTP_STATUS_REQUEST_TOO_LARGE: + return "HTTP_STATUS_REQUEST_TOO_LARGE"; + case HTTP_STATUS_URI_TOO_LONG: + return "HTTP_STATUS_URI_TOO_LONG"; + case HTTP_STATUS_UNSUPPORTED_MEDIA: + return "HTTP_STATUS_UNSUPPORTED_MEDIA"; + case HTTP_STATUS_RETRY_WITH: + return "HTTP_STATUS_RETRY_WITH"; + case HTTP_STATUS_SERVER_ERROR: + return "HTTP_STATUS_SERVER_ERROR"; + case HTTP_STATUS_NOT_SUPPORTED: + return "HTTP_STATUS_NOT_SUPPORTED"; + case HTTP_STATUS_BAD_GATEWAY: + return "HTTP_STATUS_BAD_GATEWAY"; + case HTTP_STATUS_SERVICE_UNAVAIL: + return "HTTP_STATUS_SERVICE_UNAVAIL"; + case HTTP_STATUS_GATEWAY_TIMEOUT: + return "HTTP_STATUS_GATEWAY_TIMEOUT"; + case HTTP_STATUS_VERSION_NOT_SUP: + return "HTTP_STATUS_VERSION_NOT_SUP"; + default: + return "HTTP_STATUS_UNKNOWN"; + } +} + +char* freerdp_http_status_string_format(long status, char* buffer, size_t size) +{ + const char* code = freerdp_http_status_string(status); + _snprintf(buffer, size, "%s [%ld]", code, status); + return buffer; +} diff --git a/libfreerdp/utils/passphrase.c b/libfreerdp/utils/passphrase.c new file mode 100644 index 0000000..016da79 --- /dev/null +++ b/libfreerdp/utils/passphrase.c @@ -0,0 +1,299 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Passphrase Handling Utils + * + * Copyright 2011 Shea Levy <shea@shealevy.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. + */ + +#include <freerdp/config.h> +#include <freerdp/freerdp.h> + +#include <errno.h> +#include <freerdp/utils/passphrase.h> + +#ifdef _WIN32 + +#include <stdio.h> +#include <io.h> +#include <conio.h> +#include <wincred.h> + +static char read_chr(FILE* f) +{ + char chr; + const BOOL isTty = _isatty(_fileno(f)); + if (isTty) + return fgetc(f); + if (fscanf_s(f, "%c", &chr, (UINT32)sizeof(char)) && !feof(f)) + return chr; + return 0; +} + +int freerdp_interruptible_getc(rdpContext* context, FILE* f) +{ + return read_chr(f); +} + +char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf, size_t bufsiz, + int from_stdin) +{ + WCHAR UserNameW[CREDUI_MAX_USERNAME_LENGTH + 1] = { 'p', 'r', 'e', 'f', 'i', + 'l', 'l', 'e', 'd', '\0' }; + WCHAR PasswordW[CREDUI_MAX_PASSWORD_LENGTH + 1] = { 0 }; + BOOL fSave = FALSE; + DWORD dwFlags = 0; + WCHAR* promptW = ConvertUtf8ToWCharAlloc(prompt, NULL); + const DWORD status = + CredUICmdLinePromptForCredentialsW(promptW, NULL, 0, UserNameW, ARRAYSIZE(UserNameW), + PasswordW, ARRAYSIZE(PasswordW), &fSave, dwFlags); + free(promptW); + if (ConvertWCharNToUtf8(PasswordW, ARRAYSIZE(PasswordW), buf, bufsiz) < 0) + return NULL; + return buf; +} + +#elif !defined(ANDROID) + +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <termios.h> +#include <unistd.h> +#include <freerdp/utils/signal.h> + +#if defined(WINPR_HAVE_POLL_H) && !defined(__APPLE__) +#include <poll.h> +#else +#include <time.h> +#include <sys/select.h> +#endif + +static int wait_for_fd(int fd, int timeout) +{ + int status = 0; +#if defined(WINPR_HAVE_POLL_H) && !defined(__APPLE__) + struct pollfd pollset = { 0 }; + pollset.fd = fd; + pollset.events = POLLIN; + pollset.revents = 0; + + do + { + status = poll(&pollset, 1, timeout); + } while ((status < 0) && (errno == EINTR)); + +#else + fd_set rset = { 0 }; + struct timeval tv = { 0 }; + FD_ZERO(&rset); + FD_SET(fd, &rset); + + if (timeout) + { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + } + + do + { + status = select(fd + 1, &rset, NULL, NULL, timeout ? &tv : NULL); + } while ((status < 0) && (errno == EINTR)); + +#endif + return status; +} + +static void replace_char(char* buffer, size_t buffer_len, const char* toreplace) +{ + while (*toreplace != '\0') + { + char* ptr = NULL; + while ((ptr = strrchr(buffer, *toreplace)) != NULL) + *ptr = '\0'; + toreplace++; + } +} + +char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf, size_t bufsiz, + int from_stdin) +{ + BOOL terminal_needs_reset = FALSE; + char term_name[L_ctermid] = { 0 }; + int term_file = 0; + + FILE* fout = NULL; + + if (bufsiz == 0) + { + errno = EINVAL; + return NULL; + } + + ctermid(term_name); + int terminal_fildes = 0; + if (from_stdin || strcmp(term_name, "") == 0 || (term_file = open(term_name, O_RDWR)) == -1) + { + fout = stdout; + terminal_fildes = STDIN_FILENO; + } + else + { + fout = fdopen(term_file, "w"); + terminal_fildes = term_file; + } + + struct termios orig_flags = { 0 }; + if (tcgetattr(terminal_fildes, &orig_flags) != -1) + { + struct termios new_flags = { 0 }; + new_flags = orig_flags; + new_flags.c_lflag &= ~ECHO; + new_flags.c_lflag |= ECHONL; + terminal_needs_reset = TRUE; + if (tcsetattr(terminal_fildes, TCSAFLUSH, &new_flags) == -1) + terminal_needs_reset = FALSE; + } + + FILE* fp = fdopen(terminal_fildes, "r"); + if (!fp) + goto error; + + fprintf(fout, "%s", prompt); + fflush(fout); + + char* ptr = NULL; + size_t ptr_len = 0; + + const SSIZE_T res = freerdp_interruptible_get_line(context, &ptr, &ptr_len, fp); + if (res < 0) + goto error; + replace_char(ptr, ptr_len, "\r\n"); + + strncpy(buf, ptr, MIN(bufsiz, ptr_len)); + free(ptr); + if (terminal_needs_reset) + { + if (tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags) == -1) + goto error; + } + + if (terminal_fildes != STDIN_FILENO) + { + if (fclose(fp) == -1) + goto error; + } + + return buf; + +error: +{ + int saved_errno = errno; + if (terminal_needs_reset) + tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags); + if (terminal_fildes != STDIN_FILENO) + { + if (fp) + fclose(fp); + } + errno = saved_errno; + return NULL; +} +} + +int freerdp_interruptible_getc(rdpContext* context, FILE* f) +{ + int rc = EOF; + const int fd = fileno(f); + + const int orig = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, orig | O_NONBLOCK); + do + { + const int res = wait_for_fd(fd, 10); + if (res != 0) + { + char c = 0; + const ssize_t rd = read(fd, &c, 1); + if (rd == 1) + rc = c; + break; + } + } while (!freerdp_shall_disconnect_context(context)); + + fcntl(fd, F_SETFL, orig); + return rc; +} + +#else + +char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf, size_t bufsiz, + int from_stdin) +{ + return NULL; +} + +int freerdp_interruptible_getc(rdpContext* context, FILE* f) +{ + return EOF; +} +#endif + +SSIZE_T freerdp_interruptible_get_line(rdpContext* context, char** plineptr, size_t* psize, + FILE* stream) +{ + int c = 0; + char* n = NULL; + size_t step = 32; + size_t used = 0; + char* ptr = NULL; + size_t len = 0; + + if (!plineptr || !psize) + { + errno = EINVAL; + return -1; + } + + do + { + if (used + 2 >= len) + { + len += step; + n = realloc(ptr, len); + + if (!n) + { + return -1; + } + + ptr = n; + } + + c = freerdp_interruptible_getc(context, stream); + if (c != EOF) + ptr[used++] = (char)c; + } while ((c != '\n') && (c != '\r') && (c != EOF)); + + ptr[used] = '\0'; + if (c == EOF) + { + free(ptr); + return EOF; + } + *plineptr = ptr; + *psize = used; + return used; +} diff --git a/libfreerdp/utils/pcap.c b/libfreerdp/utils/pcap.c new file mode 100644 index 0000000..db50909 --- /dev/null +++ b/libfreerdp/utils/pcap.c @@ -0,0 +1,286 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * pcap File Format Utils + * + * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <freerdp/config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <winpr/wtypes.h> +#include <winpr/assert.h> +#include <winpr/file.h> +#include <winpr/crt.h> +#include <freerdp/log.h> + +#define TAG FREERDP_TAG("utils") + +#ifndef _WIN32 +#include <sys/time.h> +#else +#include <time.h> +#include <sys/timeb.h> +#include <winpr/windows.h> + +int gettimeofday(struct timeval* tp, void* tz) +{ + struct _timeb timebuffer; + _ftime(&timebuffer); + tp->tv_sec = (long)timebuffer.time; + tp->tv_usec = timebuffer.millitm * 1000; + return 0; +} +#endif + +#include <freerdp/types.h> +#include <freerdp/utils/pcap.h> + +#define PCAP_MAGIC 0xA1B2C3D4 + +struct rdp_pcap +{ + FILE* fp; + char* name; + BOOL write; + INT64 file_size; + size_t record_count; + pcap_header header; + pcap_record* head; + pcap_record* tail; + pcap_record* record; +}; + +static BOOL pcap_read_header(rdpPcap* pcap, pcap_header* header) +{ + WINPR_ASSERT(pcap); + WINPR_ASSERT(header); + + return fread(header, sizeof(pcap_header), 1, pcap->fp) == 1; +} + +static BOOL pcap_write_header(rdpPcap* pcap, const pcap_header* header) +{ + WINPR_ASSERT(pcap); + WINPR_ASSERT(header); + + return fwrite(header, sizeof(pcap_header), 1, pcap->fp) == 1; +} + +static BOOL pcap_read_record_header(rdpPcap* pcap, pcap_record_header* record) +{ + WINPR_ASSERT(pcap); + WINPR_ASSERT(record); + + return fread(record, sizeof(pcap_record_header), 1, pcap->fp) == 1; +} + +static BOOL pcap_write_record_header(rdpPcap* pcap, const pcap_record_header* record) +{ + WINPR_ASSERT(pcap); + WINPR_ASSERT(record); + + return fwrite(record, sizeof(pcap_record_header), 1, pcap->fp) == 1; +} + +static BOOL pcap_read_record(rdpPcap* pcap, pcap_record* record) +{ + WINPR_ASSERT(pcap); + WINPR_ASSERT(record); + + if (!pcap_read_record_header(pcap, &record->header)) + return FALSE; + + record->length = record->header.incl_len; + record->data = malloc(record->length); + if (!record->data) + return FALSE; + + if (fread(record->data, record->length, 1, pcap->fp) != 1) + { + free(record->data); + record->data = NULL; + return FALSE; + } + return TRUE; +} + +static BOOL pcap_write_record(rdpPcap* pcap, const pcap_record* record) +{ + WINPR_ASSERT(pcap); + WINPR_ASSERT(record); + + return pcap_write_record_header(pcap, &record->header) && + (fwrite(record->cdata, record->length, 1, pcap->fp) == 1); +} + +BOOL pcap_add_record(rdpPcap* pcap, const void* data, size_t length) +{ + pcap_record* record = NULL; + struct timeval tp; + + WINPR_ASSERT(pcap); + WINPR_ASSERT(data || (length == 0)); + WINPR_ASSERT(length <= UINT32_MAX); + + record = (pcap_record*)calloc(1, sizeof(pcap_record)); + if (!record) + return FALSE; + + record->cdata = data; + record->length = length; + record->header.incl_len = (UINT32)length; + record->header.orig_len = (UINT32)length; + + gettimeofday(&tp, 0); + record->header.ts_sec = (UINT32)tp.tv_sec; + record->header.ts_usec = (UINT32)tp.tv_usec; + + if (pcap->tail == NULL) + { + pcap->tail = record; + if (!pcap->tail) + return FALSE; + + pcap->head = pcap->tail; + } + else + { + record->next = pcap->tail; + pcap->tail = record; + } + + if (pcap->record == NULL) + pcap->record = record; + + return TRUE; +} + +BOOL pcap_has_next_record(const rdpPcap* pcap) +{ + WINPR_ASSERT(pcap); + + if (pcap->file_size - (_ftelli64(pcap->fp)) <= 16) + return FALSE; + + return TRUE; +} + +BOOL pcap_get_next_record_header(rdpPcap* pcap, pcap_record* record) +{ + WINPR_ASSERT(pcap); + WINPR_ASSERT(record); + + if (pcap_has_next_record(pcap) != TRUE) + return FALSE; + + pcap_read_record_header(pcap, &record->header); + record->length = record->header.incl_len; + + return TRUE; +} + +BOOL pcap_get_next_record_content(rdpPcap* pcap, pcap_record* record) +{ + WINPR_ASSERT(pcap); + WINPR_ASSERT(record); + + return fread(record->data, record->length, 1, pcap->fp) == 1; +} + +BOOL pcap_get_next_record(rdpPcap* pcap, pcap_record* record) +{ + WINPR_ASSERT(pcap); + WINPR_ASSERT(record); + + return pcap_has_next_record(pcap) && pcap_read_record(pcap, record); +} + +rdpPcap* pcap_open(const char* name, BOOL write) +{ + rdpPcap* pcap = NULL; + + WINPR_ASSERT(name); + + pcap = (rdpPcap*)calloc(1, sizeof(rdpPcap)); + if (!pcap) + goto fail; + + pcap->name = _strdup(name); + pcap->write = write; + pcap->record_count = 0; + pcap->fp = winpr_fopen(name, write ? "w+b" : "rb"); + + if (pcap->fp == NULL) + goto fail; + + if (write) + { + pcap->header.magic_number = PCAP_MAGIC; + pcap->header.version_major = 2; + pcap->header.version_minor = 4; + pcap->header.thiszone = 0; + pcap->header.sigfigs = 0; + pcap->header.snaplen = UINT32_MAX; + pcap->header.network = 0; + if (!pcap_write_header(pcap, &pcap->header)) + goto fail; + } + else + { + _fseeki64(pcap->fp, 0, SEEK_END); + pcap->file_size = _ftelli64(pcap->fp); + _fseeki64(pcap->fp, 0, SEEK_SET); + if (!pcap_read_header(pcap, &pcap->header)) + goto fail; + } + + return pcap; + +fail: + pcap_close(pcap); + return NULL; +} + +void pcap_flush(rdpPcap* pcap) +{ + WINPR_ASSERT(pcap); + + while (pcap->record != NULL) + { + pcap_write_record(pcap, pcap->record); + pcap->record = pcap->record->next; + } + + if (pcap->fp != NULL) + fflush(pcap->fp); +} + +void pcap_close(rdpPcap* pcap) +{ + if (!pcap) + return; + + pcap_flush(pcap); + + if (pcap->fp != NULL) + fclose(pcap->fp); + + free(pcap->name); + free(pcap); +} diff --git a/libfreerdp/utils/profiler.c b/libfreerdp/utils/profiler.c new file mode 100644 index 0000000..b1c058d --- /dev/null +++ b/libfreerdp/utils/profiler.c @@ -0,0 +1,98 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Profiler Utils + * + * Copyright 2011 Stephen Erisman + * + * 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. + */ + +#include <freerdp/config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <freerdp/utils/profiler.h> +#include <freerdp/log.h> + +#define TAG FREERDP_TAG("utils") + +struct S_PROFILER +{ + char* name; + STOPWATCH* stopwatch; +}; + +PROFILER* profiler_create(const char* name) +{ + PROFILER* profiler = (PROFILER*)calloc(1, sizeof(PROFILER)); + + if (!profiler) + return NULL; + + profiler->name = _strdup(name); + profiler->stopwatch = stopwatch_create(); + + if (!profiler->name || !profiler->stopwatch) + goto fail; + + return profiler; +fail: + profiler_free(profiler); + return NULL; +} + +void profiler_free(PROFILER* profiler) +{ + if (profiler) + { + free(profiler->name); + stopwatch_free(profiler->stopwatch); + } + + free(profiler); +} + +void profiler_enter(PROFILER* profiler) +{ + stopwatch_start(profiler->stopwatch); +} + +void profiler_exit(PROFILER* profiler) +{ + stopwatch_stop(profiler->stopwatch); +} + +void profiler_print_header(void) +{ + WLog_INFO(TAG, + "-------------------------------+------------+-------------+-----------+-------"); + WLog_INFO(TAG, + "PROFILER NAME | COUNT | TOTAL | AVG | IPS"); + WLog_INFO(TAG, + "-------------------------------+------------+-------------+-----------+-------"); +} + +void profiler_print(PROFILER* profiler) +{ + double s = stopwatch_get_elapsed_time_in_seconds(profiler->stopwatch); + double avg = profiler->stopwatch->count == 0 ? 0 : s / profiler->stopwatch->count; + WLog_INFO(TAG, "%-30s | %10u | %10.4fs | %8.6fs | %6.0f", profiler->name, + profiler->stopwatch->count, s, avg, profiler->stopwatch->count / s); +} + +void profiler_print_footer(void) +{ + WLog_INFO(TAG, + "-------------------------------+------------+-------------+-----------+-------"); +} diff --git a/libfreerdp/utils/rdpdr_utils.c b/libfreerdp/utils/rdpdr_utils.c new file mode 100644 index 0000000..28cc97f --- /dev/null +++ b/libfreerdp/utils/rdpdr_utils.c @@ -0,0 +1,598 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * SCard utility functions + * + * Copyright 2021 Armin Novak <armin.novak@thincast.com> + * Copyright 2021 Thincast Technologies GmbH + * + * 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. + */ + +#include <winpr/wlog.h> +#include <winpr/print.h> +#include <winpr/smartcard.h> + +#include <freerdp/utils/rdpdr_utils.h> +#include <freerdp/channels/scard.h> +#include <freerdp/channels/rdpdr.h> + +#include <freerdp/log.h> +#define TAG FREERDP_TAG("utils.scard") + +LONG scard_log_status_error(const char* tag, const char* what, LONG status) +{ + if (status != SCARD_S_SUCCESS) + { + DWORD level = WLOG_ERROR; + switch (status) + { + case SCARD_E_TIMEOUT: + level = WLOG_DEBUG; + break; + case SCARD_E_NO_READERS_AVAILABLE: + level = WLOG_INFO; + break; + default: + break; + } + WLog_Print(WLog_Get(tag), level, "%s failed with error %s [%" PRId32 "]", what, + SCardGetErrorString(status), status); + } + return status; +} + +const char* scard_get_ioctl_string(UINT32 ioControlCode, BOOL funcName) +{ + switch (ioControlCode) + { + case SCARD_IOCTL_ESTABLISHCONTEXT: + return funcName ? "SCardEstablishContext" : "SCARD_IOCTL_ESTABLISHCONTEXT"; + + case SCARD_IOCTL_RELEASECONTEXT: + return funcName ? "SCardReleaseContext" : "SCARD_IOCTL_RELEASECONTEXT"; + + case SCARD_IOCTL_ISVALIDCONTEXT: + return funcName ? "SCardIsValidContext" : "SCARD_IOCTL_ISVALIDCONTEXT"; + + case SCARD_IOCTL_LISTREADERGROUPSA: + return funcName ? "SCardListReaderGroupsA" : "SCARD_IOCTL_LISTREADERGROUPSA"; + + case SCARD_IOCTL_LISTREADERGROUPSW: + return funcName ? "SCardListReaderGroupsW" : "SCARD_IOCTL_LISTREADERGROUPSW"; + + case SCARD_IOCTL_LISTREADERSA: + return funcName ? "SCardListReadersA" : "SCARD_IOCTL_LISTREADERSA"; + + case SCARD_IOCTL_LISTREADERSW: + return funcName ? "SCardListReadersW" : "SCARD_IOCTL_LISTREADERSW"; + + case SCARD_IOCTL_INTRODUCEREADERGROUPA: + return funcName ? "SCardIntroduceReaderGroupA" : "SCARD_IOCTL_INTRODUCEREADERGROUPA"; + + case SCARD_IOCTL_INTRODUCEREADERGROUPW: + return funcName ? "SCardIntroduceReaderGroupW" : "SCARD_IOCTL_INTRODUCEREADERGROUPW"; + + case SCARD_IOCTL_FORGETREADERGROUPA: + return funcName ? "SCardForgetReaderGroupA" : "SCARD_IOCTL_FORGETREADERGROUPA"; + + case SCARD_IOCTL_FORGETREADERGROUPW: + return funcName ? "SCardForgetReaderGroupW" : "SCARD_IOCTL_FORGETREADERGROUPW"; + + case SCARD_IOCTL_INTRODUCEREADERA: + return funcName ? "SCardIntroduceReaderA" : "SCARD_IOCTL_INTRODUCEREADERA"; + + case SCARD_IOCTL_INTRODUCEREADERW: + return funcName ? "SCardIntroduceReaderW" : "SCARD_IOCTL_INTRODUCEREADERW"; + + case SCARD_IOCTL_FORGETREADERA: + return funcName ? "SCardForgetReaderA" : "SCARD_IOCTL_FORGETREADERA"; + + case SCARD_IOCTL_FORGETREADERW: + return funcName ? "SCardForgetReaderW" : "SCARD_IOCTL_FORGETREADERW"; + + case SCARD_IOCTL_ADDREADERTOGROUPA: + return funcName ? "SCardAddReaderToGroupA" : "SCARD_IOCTL_ADDREADERTOGROUPA"; + + case SCARD_IOCTL_ADDREADERTOGROUPW: + return funcName ? "SCardAddReaderToGroupW" : "SCARD_IOCTL_ADDREADERTOGROUPW"; + + case SCARD_IOCTL_REMOVEREADERFROMGROUPA: + return funcName ? "SCardRemoveReaderFromGroupA" : "SCARD_IOCTL_REMOVEREADERFROMGROUPA"; + + case SCARD_IOCTL_REMOVEREADERFROMGROUPW: + return funcName ? "SCardRemoveReaderFromGroupW" : "SCARD_IOCTL_REMOVEREADERFROMGROUPW"; + + case SCARD_IOCTL_LOCATECARDSA: + return funcName ? "SCardLocateCardsA" : "SCARD_IOCTL_LOCATECARDSA"; + + case SCARD_IOCTL_LOCATECARDSW: + return funcName ? "SCardLocateCardsW" : "SCARD_IOCTL_LOCATECARDSW"; + + case SCARD_IOCTL_GETSTATUSCHANGEA: + return funcName ? "SCardGetStatusChangeA" : "SCARD_IOCTL_GETSTATUSCHANGEA"; + + case SCARD_IOCTL_GETSTATUSCHANGEW: + return funcName ? "SCardGetStatusChangeW" : "SCARD_IOCTL_GETSTATUSCHANGEW"; + + case SCARD_IOCTL_CANCEL: + return funcName ? "SCardCancel" : "SCARD_IOCTL_CANCEL"; + + case SCARD_IOCTL_CONNECTA: + return funcName ? "SCardConnectA" : "SCARD_IOCTL_CONNECTA"; + + case SCARD_IOCTL_CONNECTW: + return funcName ? "SCardConnectW" : "SCARD_IOCTL_CONNECTW"; + + case SCARD_IOCTL_RECONNECT: + return funcName ? "SCardReconnect" : "SCARD_IOCTL_RECONNECT"; + + case SCARD_IOCTL_DISCONNECT: + return funcName ? "SCardDisconnect" : "SCARD_IOCTL_DISCONNECT"; + + case SCARD_IOCTL_BEGINTRANSACTION: + return funcName ? "SCardBeginTransaction" : "SCARD_IOCTL_BEGINTRANSACTION"; + + case SCARD_IOCTL_ENDTRANSACTION: + return funcName ? "SCardEndTransaction" : "SCARD_IOCTL_ENDTRANSACTION"; + + case SCARD_IOCTL_STATE: + return funcName ? "SCardState" : "SCARD_IOCTL_STATE"; + + case SCARD_IOCTL_STATUSA: + return funcName ? "SCardStatusA" : "SCARD_IOCTL_STATUSA"; + + case SCARD_IOCTL_STATUSW: + return funcName ? "SCardStatusW" : "SCARD_IOCTL_STATUSW"; + + case SCARD_IOCTL_TRANSMIT: + return funcName ? "SCardTransmit" : "SCARD_IOCTL_TRANSMIT"; + + case SCARD_IOCTL_CONTROL: + return funcName ? "SCardControl" : "SCARD_IOCTL_CONTROL"; + + case SCARD_IOCTL_GETATTRIB: + return funcName ? "SCardGetAttrib" : "SCARD_IOCTL_GETATTRIB"; + + case SCARD_IOCTL_SETATTRIB: + return funcName ? "SCardSetAttrib" : "SCARD_IOCTL_SETATTRIB"; + + case SCARD_IOCTL_ACCESSSTARTEDEVENT: + return funcName ? "SCardAccessStartedEvent" : "SCARD_IOCTL_ACCESSSTARTEDEVENT"; + + case SCARD_IOCTL_LOCATECARDSBYATRA: + return funcName ? "SCardLocateCardsByATRA" : "SCARD_IOCTL_LOCATECARDSBYATRA"; + + case SCARD_IOCTL_LOCATECARDSBYATRW: + return funcName ? "SCardLocateCardsByATRB" : "SCARD_IOCTL_LOCATECARDSBYATRW"; + + case SCARD_IOCTL_READCACHEA: + return funcName ? "SCardReadCacheA" : "SCARD_IOCTL_READCACHEA"; + + case SCARD_IOCTL_READCACHEW: + return funcName ? "SCardReadCacheW" : "SCARD_IOCTL_READCACHEW"; + + case SCARD_IOCTL_WRITECACHEA: + return funcName ? "SCardWriteCacheA" : "SCARD_IOCTL_WRITECACHEA"; + + case SCARD_IOCTL_WRITECACHEW: + return funcName ? "SCardWriteCacheW" : "SCARD_IOCTL_WRITECACHEW"; + + case SCARD_IOCTL_GETTRANSMITCOUNT: + return funcName ? "SCardGetTransmitCount" : "SCARD_IOCTL_GETTRANSMITCOUNT"; + + case SCARD_IOCTL_RELEASETARTEDEVENT: + return funcName ? "SCardReleaseStartedEvent" : "SCARD_IOCTL_RELEASETARTEDEVENT"; + + case SCARD_IOCTL_GETREADERICON: + return funcName ? "SCardGetReaderIcon" : "SCARD_IOCTL_GETREADERICON"; + + case SCARD_IOCTL_GETDEVICETYPEID: + return funcName ? "SCardGetDeviceTypeId" : "SCARD_IOCTL_GETDEVICETYPEID"; + + default: + return funcName ? "SCardUnknown" : "SCARD_IOCTL_UNKNOWN"; + } +} + +const char* rdpdr_component_string(UINT16 component) +{ + switch (component) + { + case RDPDR_CTYP_PRN: + return "RDPDR_CTYP_PRN"; + case RDPDR_CTYP_CORE: + return "RDPDR_CTYP_CORE"; + default: + return "UNKNOWN"; + } +} + +const char* rdpdr_packetid_string(UINT16 packetid) +{ + switch (packetid) + { + case PAKID_CORE_SERVER_ANNOUNCE: + return "PAKID_CORE_SERVER_ANNOUNCE"; + case PAKID_CORE_CLIENTID_CONFIRM: + return "PAKID_CORE_CLIENTID_CONFIRM"; + case PAKID_CORE_CLIENT_NAME: + return "PAKID_CORE_CLIENT_NAME"; + case PAKID_CORE_DEVICELIST_ANNOUNCE: + return "PAKID_CORE_DEVICELIST_ANNOUNCE"; + case PAKID_CORE_DEVICE_REPLY: + return "PAKID_CORE_DEVICE_REPLY"; + case PAKID_CORE_DEVICE_IOREQUEST: + return "PAKID_CORE_DEVICE_IOREQUEST"; + case PAKID_CORE_DEVICE_IOCOMPLETION: + return "PAKID_CORE_DEVICE_IOCOMPLETION"; + case PAKID_CORE_SERVER_CAPABILITY: + return "PAKID_CORE_SERVER_CAPABILITY"; + case PAKID_CORE_CLIENT_CAPABILITY: + return "PAKID_CORE_CLIENT_CAPABILITY"; + case PAKID_CORE_DEVICELIST_REMOVE: + return "PAKID_CORE_DEVICELIST_REMOVE"; + case PAKID_CORE_USER_LOGGEDON: + return "PAKID_CORE_USER_LOGGEDON"; + case PAKID_PRN_CACHE_DATA: + return "PAKID_PRN_CACHE_DATA"; + case PAKID_PRN_USING_XPS: + return "PAKID_PRN_USING_XPS"; + default: + return "UNKNOWN"; + } +} + +BOOL rdpdr_write_iocompletion_header(wStream* out, UINT32 DeviceId, UINT32 CompletionId, + UINT32 ioStatus) +{ + WINPR_ASSERT(out); + Stream_SetPosition(out, 0); + if (!Stream_EnsureRemainingCapacity(out, 16)) + return FALSE; + Stream_Write_UINT16(out, RDPDR_CTYP_CORE); /* Component (2 bytes) */ + Stream_Write_UINT16(out, PAKID_CORE_DEVICE_IOCOMPLETION); /* PacketId (2 bytes) */ + Stream_Write_UINT32(out, DeviceId); /* DeviceId (4 bytes) */ + Stream_Write_UINT32(out, CompletionId); /* CompletionId (4 bytes) */ + Stream_Write_UINT32(out, ioStatus); /* IoStatus (4 bytes) */ + + return TRUE; +} + +static void rdpdr_dump_packet(wLog* log, DWORD lvl, wStream* s, const char* custom, BOOL send) +{ + if (!WLog_IsLevelActive(log, lvl)) + return; + + const size_t gpos = Stream_GetPosition(s); + const size_t pos = send ? Stream_GetPosition(s) : Stream_Length(s); + + UINT16 component = 0; + UINT16 packetid = 0; + + Stream_SetPosition(s, 0); + + if (pos >= 2) + Stream_Read_UINT16(s, component); + if (pos >= 4) + Stream_Read_UINT16(s, packetid); + + switch (packetid) + { + case PAKID_CORE_SERVER_ANNOUNCE: + case PAKID_CORE_CLIENTID_CONFIRM: + { + UINT16 versionMajor = 0; + UINT16 versionMinor = 0; + UINT32 clientID = 0; + + if (pos >= 6) + Stream_Read_UINT16(s, versionMajor); + if (pos >= 8) + Stream_Read_UINT16(s, versionMinor); + if (pos >= 12) + Stream_Read_UINT32(s, clientID); + WLog_Print(log, lvl, + "%s [%s | %s] [version:%" PRIu16 ".%" PRIu16 "][id:0x%08" PRIx32 + "] -> %" PRIuz, + custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid), + versionMajor, versionMinor, clientID, pos); + } + break; + case PAKID_CORE_CLIENT_NAME: + { + char name[256] = { 0 }; + UINT32 unicodeFlag = 0; + UINT32 codePage = 0; + UINT32 computerNameLen = 0; + if (pos >= 8) + Stream_Read_UINT32(s, unicodeFlag); + if (pos >= 12) + Stream_Read_UINT32(s, codePage); + if (pos >= 16) + Stream_Read_UINT32(s, computerNameLen); + if (pos >= 16 + computerNameLen) + { + if (unicodeFlag == 0) + Stream_Read(s, name, MIN(sizeof(name), computerNameLen)); + else + ConvertWCharNToUtf8(Stream_ConstPointer(s), computerNameLen / sizeof(WCHAR), + name, sizeof(name)); + } + WLog_Print(log, lvl, + "%s [%s | %s] [ucs:%" PRIu32 "|cp:%" PRIu32 "][len:0x%08" PRIx32 + "] '%s' -> %" PRIuz, + custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid), + unicodeFlag, codePage, computerNameLen, name, pos); + } + break; + + case PAKID_CORE_DEVICE_IOREQUEST: + { + UINT32 CompletionId = 0; + UINT32 deviceID = 0; + UINT32 FileId = 0; + UINT32 MajorFunction = 0; + UINT32 MinorFunction = 0; + + if (pos >= 8) + Stream_Read_UINT32(s, deviceID); + if (pos >= 12) + Stream_Read_UINT32(s, FileId); + if (pos >= 16) + Stream_Read_UINT32(s, CompletionId); + if (pos >= 20) + Stream_Read_UINT32(s, MajorFunction); + if (pos >= 24) + Stream_Read_UINT32(s, MinorFunction); + WLog_Print(log, lvl, + "%s [%s | %s] [0x%08" PRIx32 "] FileId=0x%08" PRIx32 + ", CompletionId=0x%08" PRIx32 ", MajorFunction=0x%08" PRIx32 + ", MinorFunction=0x%08" PRIx32 " -> %" PRIuz, + custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid), + deviceID, FileId, CompletionId, MajorFunction, MinorFunction, pos); + } + break; + case PAKID_CORE_DEVICE_IOCOMPLETION: + { + UINT32 completionID = 0; + UINT32 ioStatus = 0; + UINT32 deviceID = 0; + if (pos >= 8) + Stream_Read_UINT32(s, deviceID); + if (pos >= 12) + Stream_Read_UINT32(s, completionID); + if (pos >= 16) + Stream_Read_UINT32(s, ioStatus); + + WLog_Print(log, lvl, + "%s [%s | %s] [0x%08" PRIx32 "] completionID=0x%08" PRIx32 + ", ioStatus=0x%08" PRIx32 " -> %" PRIuz, + custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid), + deviceID, completionID, ioStatus, pos); + } + break; + case PAKID_CORE_DEVICE_REPLY: + { + UINT32 deviceID = 0; + UINT32 status = 0; + + if (pos >= 8) + Stream_Read_UINT32(s, deviceID); + if (pos >= 12) + Stream_Read_UINT32(s, status); + WLog_Print(log, lvl, + "%s [%s | %s] [id:0x%08" PRIx32 ",status=0x%08" PRIx32 "] -> %" PRIuz, + custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid), + deviceID, status, pos); + } + break; + case PAKID_CORE_CLIENT_CAPABILITY: + case PAKID_CORE_SERVER_CAPABILITY: + { + UINT16 numCapabilities = 0; + if (pos >= 6) + Stream_Read_UINT16(s, numCapabilities); + if (pos >= 8) + Stream_Seek_UINT16(s); /* padding */ + WLog_Print(log, lvl, "%s [%s | %s] [caps:%" PRIu16 "] -> %" PRIuz, custom, + rdpdr_component_string(component), rdpdr_packetid_string(packetid), + numCapabilities, pos); + for (UINT16 x = 0; x < numCapabilities; x++) + { + RDPDR_CAPABILITY_HEADER header = { 0 }; + const UINT error = rdpdr_read_capset_header(log, s, &header); + if (error == CHANNEL_RC_OK) + Stream_Seek(s, header.CapabilityLength); + } + } + break; + case PAKID_CORE_DEVICELIST_ANNOUNCE: + { + size_t offset = 8; + UINT32 count = 0; + + if (pos >= offset) + Stream_Read_UINT32(s, count); + + WLog_Print(log, lvl, "%s [%s | %s] [%" PRIu32 "] -> %" PRIuz, custom, + rdpdr_component_string(component), rdpdr_packetid_string(packetid), count, + pos); + + for (UINT32 x = 0; x < count; x++) + { + RdpdrDevice device = { 0 }; + + offset += 20; + if (pos >= offset) + { + Stream_Read_UINT32(s, device.DeviceType); /* DeviceType (4 bytes) */ + Stream_Read_UINT32(s, device.DeviceId); /* DeviceId (4 bytes) */ + Stream_Read(s, device.PreferredDosName, 8); /* PreferredDosName (8 bytes) */ + Stream_Read_UINT32(s, device.DeviceDataLength); /* DeviceDataLength (4 bytes) */ + device.DeviceData = Stream_Pointer(s); + } + offset += device.DeviceDataLength; + + WLog_Print(log, lvl, + "%s [announce][%" PRIu32 "] %s [0x%08" PRIx32 + "] '%s' [DeviceDataLength=%" PRIu32 "]", + custom, x, freerdp_rdpdr_dtyp_string(device.DeviceType), device.DeviceId, + device.PreferredDosName, device.DeviceDataLength); + } + } + break; + case PAKID_CORE_DEVICELIST_REMOVE: + { + size_t offset = 8; + UINT32 count = 0; + + if (pos >= offset) + Stream_Read_UINT32(s, count); + + WLog_Print(log, lvl, "%s [%s | %s] [%" PRIu32 "] -> %" PRIuz, custom, + rdpdr_component_string(component), rdpdr_packetid_string(packetid), count, + pos); + + for (UINT32 x = 0; x < count; x++) + { + UINT32 id = 0; + + offset += 4; + if (pos >= offset) + Stream_Read_UINT32(s, id); + + WLog_Print(log, lvl, "%s [remove][%" PRIu32 "] id=%" PRIu32, custom, x, id); + } + } + break; + case PAKID_CORE_USER_LOGGEDON: + WLog_Print(log, lvl, "%s [%s | %s] -> %" PRIuz, custom, + rdpdr_component_string(component), rdpdr_packetid_string(packetid), pos); + break; + default: + { + WLog_Print(log, lvl, "%s [%s | %s] -> %" PRIuz, custom, + rdpdr_component_string(component), rdpdr_packetid_string(packetid), pos); + } + break; + } + + // winpr_HexLogDump(log, lvl, Stream_Buffer(s), pos); + Stream_SetPosition(s, gpos); +} + +void rdpdr_dump_received_packet(wLog* log, DWORD lvl, wStream* s, const char* custom) +{ + rdpdr_dump_packet(log, lvl, s, custom, FALSE); +} + +void rdpdr_dump_send_packet(wLog* log, DWORD lvl, wStream* s, const char* custom) +{ + rdpdr_dump_packet(log, lvl, s, custom, TRUE); +} + +const char* rdpdr_irp_string(UINT32 major) +{ + switch (major) + { + case IRP_MJ_CREATE: + return "IRP_MJ_CREATE"; + case IRP_MJ_CLOSE: + return "IRP_MJ_CLOSE"; + case IRP_MJ_READ: + return "IRP_MJ_READ"; + case IRP_MJ_WRITE: + return "IRP_MJ_WRITE"; + case IRP_MJ_DEVICE_CONTROL: + return "IRP_MJ_DEVICE_CONTROL"; + case IRP_MJ_QUERY_VOLUME_INFORMATION: + return "IRP_MJ_QUERY_VOLUME_INFORMATION"; + case IRP_MJ_SET_VOLUME_INFORMATION: + return "IRP_MJ_SET_VOLUME_INFORMATION"; + case IRP_MJ_QUERY_INFORMATION: + return "IRP_MJ_QUERY_INFORMATION"; + case IRP_MJ_SET_INFORMATION: + return "IRP_MJ_SET_INFORMATION"; + case IRP_MJ_DIRECTORY_CONTROL: + return "IRP_MJ_DIRECTORY_CONTROL"; + case IRP_MJ_LOCK_CONTROL: + return "IRP_MJ_LOCK_CONTROL"; + default: + return "IRP_UNKNOWN"; + } +} + +const char* rdpdr_cap_type_string(UINT16 capability) +{ + switch (capability) + { + case CAP_GENERAL_TYPE: + return "CAP_GENERAL_TYPE"; + case CAP_PRINTER_TYPE: + return "CAP_PRINTER_TYPE"; + case CAP_PORT_TYPE: + return "CAP_PORT_TYPE"; + case CAP_DRIVE_TYPE: + return "CAP_DRIVE_TYPE"; + case CAP_SMARTCARD_TYPE: + return "CAP_SMARTCARD_TYPE"; + default: + return "CAP_UNKNOWN"; + } +} + +UINT rdpdr_read_capset_header(wLog* log, wStream* s, RDPDR_CAPABILITY_HEADER* header) +{ + WINPR_ASSERT(header); + if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 8)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT16(s, header->CapabilityType); /* CapabilityType (2 bytes) */ + Stream_Read_UINT16(s, header->CapabilityLength); /* CapabilityLength (2 bytes) */ + Stream_Read_UINT32(s, header->Version); /* Version (4 bytes) */ + + WLog_Print(log, WLOG_TRACE, + "capability %s [0x%04" PRIx16 "] got version %" PRIu32 ", length %" PRIu16, + rdpdr_cap_type_string(header->CapabilityType), header->CapabilityType, + header->Version, header->CapabilityLength); + if (header->CapabilityLength < 8) + { + WLog_Print(log, WLOG_ERROR, "capability %s got short length %" PRIu32, + rdpdr_cap_type_string(header->CapabilityType), header->CapabilityLength); + return ERROR_INVALID_DATA; + } + header->CapabilityLength -= 8; + if (!Stream_CheckAndLogRequiredLengthWLog(log, s, header->CapabilityLength)) + return ERROR_INVALID_DATA; + return CHANNEL_RC_OK; +} + +UINT rdpdr_write_capset_header(wLog* log, wStream* s, const RDPDR_CAPABILITY_HEADER* header) +{ + WINPR_ASSERT(header); + WINPR_ASSERT(header->CapabilityLength >= 8); + + if (!Stream_EnsureRemainingCapacity(s, header->CapabilityLength)) + { + WLog_Print(log, WLOG_ERROR, "not enough data in stream!"); + return ERROR_INVALID_DATA; + } + + WLog_Print(log, WLOG_TRACE, "writing capability %s version %" PRIu32 ", length %" PRIu16, + rdpdr_cap_type_string(header->CapabilityType), header->Version, + header->CapabilityLength); + Stream_Write_UINT16(s, header->CapabilityType); /* CapabilityType (2 bytes) */ + Stream_Write_UINT16(s, header->CapabilityLength); /* CapabilityLength (2 bytes) */ + Stream_Write_UINT32(s, header->Version); /* Version (4 bytes) */ + return CHANNEL_RC_OK; +} diff --git a/libfreerdp/utils/ringbuffer.c b/libfreerdp/utils/ringbuffer.c new file mode 100644 index 0000000..d8390de --- /dev/null +++ b/libfreerdp/utils/ringbuffer.c @@ -0,0 +1,295 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2014 Thincast Technologies GmbH + * Copyright 2014 Hardening <contact@hardening-consulting.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. + */ + +#include <freerdp/config.h> + +#include <freerdp/utils/ringbuffer.h> + +#include <stdlib.h> +#include <string.h> +#include <winpr/assert.h> + +#include <winpr/crt.h> +#include <freerdp/log.h> + +#define TAG FREERDP_TAG("utils.ringbuffer") + +#ifdef WITH_DEBUG_RINGBUFFER +#define DEBUG_RINGBUFFER(...) WLog_DBG(TAG, __VA_ARGS__) +#else +#define DEBUG_RINGBUFFER(...) \ + do \ + { \ + } while (0) +#endif + +BOOL ringbuffer_init(RingBuffer* rb, size_t initialSize) +{ + rb->buffer = malloc(initialSize); + + if (!rb->buffer) + return FALSE; + + rb->readPtr = rb->writePtr = 0; + rb->initialSize = rb->size = rb->freeSize = initialSize; + DEBUG_RINGBUFFER("ringbuffer_init(%p)", (void*)rb); + return TRUE; +} + +size_t ringbuffer_used(const RingBuffer* rb) +{ + return rb->size - rb->freeSize; +} + +size_t ringbuffer_capacity(const RingBuffer* rb) +{ + return rb->size; +} + +void ringbuffer_destroy(RingBuffer* rb) +{ + DEBUG_RINGBUFFER("ringbuffer_destroy(%p)", (void*)rb); + free(rb->buffer); + rb->buffer = NULL; +} + +static BOOL ringbuffer_realloc(RingBuffer* rb, size_t targetSize) +{ + BYTE* newData = NULL; + DEBUG_RINGBUFFER("ringbuffer_realloc(%p): targetSize: %" PRIdz "", (void*)rb, targetSize); + + if (rb->writePtr == rb->readPtr) + { + /* when no size is used we can realloc() and set the heads at the + * beginning of the buffer + */ + newData = (BYTE*)realloc(rb->buffer, targetSize); + + if (!newData) + return FALSE; + + rb->readPtr = rb->writePtr = 0; + rb->buffer = newData; + } + else if ((rb->writePtr >= rb->readPtr) && (rb->writePtr < targetSize)) + { + /* we reallocate only if we're in that case, realloc don't touch read + * and write heads + * + * readPtr writePtr + * | | + * v v + * [............|XXXXXXXXXXXXXX|..........] + */ + newData = (BYTE*)realloc(rb->buffer, targetSize); + + if (!newData) + return FALSE; + + rb->buffer = newData; + } + else + { + /* in case of malloc the read head is moved at the beginning of the new buffer + * and the write head is set accordingly + */ + newData = (BYTE*)malloc(targetSize); + + if (!newData) + return FALSE; + + if (rb->readPtr < rb->writePtr) + { + /* readPtr writePtr + * | | + * v v + * [............|XXXXXXXXXXXXXX|..........] + */ + memcpy(newData, rb->buffer + rb->readPtr, ringbuffer_used(rb)); + } + else + { + /* writePtr readPtr + * | | + * v v + * [XXXXXXXXXXXX|..............|XXXXXXXXXX] + */ + BYTE* dst = newData; + memcpy(dst, rb->buffer + rb->readPtr, rb->size - rb->readPtr); + dst += (rb->size - rb->readPtr); + + if (rb->writePtr) + memcpy(dst, rb->buffer, rb->writePtr); + } + + rb->writePtr = rb->size - rb->freeSize; + rb->readPtr = 0; + free(rb->buffer); + rb->buffer = newData; + } + + rb->freeSize += (targetSize - rb->size); + rb->size = targetSize; + return TRUE; +} + +/** + * Write to a ringbuffer + * + * @param rb A pointer to the ringbuffer + * @param ptr A pointer to the data to write + * @param sz The number of bytes to write + * + * @return \b TRUE for success, \b FALSE for failure + */ +BOOL ringbuffer_write(RingBuffer* rb, const BYTE* ptr, size_t sz) +{ + size_t toWrite = 0; + size_t remaining = 0; + DEBUG_RINGBUFFER("ringbuffer_write(%p): sz: %" PRIdz "", (void*)rb, sz); + + if ((rb->freeSize <= sz) && !ringbuffer_realloc(rb, rb->size + sz)) + return FALSE; + + /* the write could be split in two + * readHead writeHead + * | | + * v v + * [ ################ ] + */ + toWrite = sz; + remaining = sz; + + if (rb->size - rb->writePtr < sz) + toWrite = rb->size - rb->writePtr; + + if (toWrite) + { + memcpy(rb->buffer + rb->writePtr, ptr, toWrite); + remaining -= toWrite; + ptr += toWrite; + } + + if (remaining) + memcpy(rb->buffer, ptr, remaining); + + rb->writePtr = (rb->writePtr + sz) % rb->size; + rb->freeSize -= sz; + return TRUE; +} + +BYTE* ringbuffer_ensure_linear_write(RingBuffer* rb, size_t sz) +{ + DEBUG_RINGBUFFER("ringbuffer_ensure_linear_write(%p): sz: %" PRIdz "", (void*)rb, sz); + + if (rb->freeSize < sz) + { + if (!ringbuffer_realloc(rb, rb->size + sz - rb->freeSize + 32)) + return NULL; + } + + if (rb->writePtr == rb->readPtr) + { + rb->writePtr = rb->readPtr = 0; + } + + if (rb->writePtr + sz < rb->size) + return rb->buffer + rb->writePtr; + + /* + * to add: ....... + * [ XXXXXXXXX ] + * + * result: + * [XXXXXXXXX....... ] + */ + memmove(rb->buffer, rb->buffer + rb->readPtr, rb->writePtr - rb->readPtr); + rb->readPtr = 0; + rb->writePtr = rb->size - rb->freeSize; + return rb->buffer + rb->writePtr; +} + +BOOL ringbuffer_commit_written_bytes(RingBuffer* rb, size_t sz) +{ + DEBUG_RINGBUFFER("ringbuffer_commit_written_bytes(%p): sz: %" PRIdz "", (void*)rb, sz); + + if (sz < 1) + return TRUE; + + if (rb->writePtr + sz > rb->size) + return FALSE; + + rb->writePtr = (rb->writePtr + sz) % rb->size; + rb->freeSize -= sz; + return TRUE; +} + +int ringbuffer_peek(const RingBuffer* rb, DataChunk chunks[2], size_t sz) +{ + size_t remaining = sz; + size_t toRead = 0; + int chunkIndex = 0; + int status = 0; + DEBUG_RINGBUFFER("ringbuffer_peek(%p): sz: %" PRIdz "", (void*)rb, sz); + + if (sz < 1) + return 0; + + if ((rb->size - rb->freeSize) < sz) + remaining = rb->size - rb->freeSize; + + toRead = remaining; + + if ((rb->readPtr + remaining) > rb->size) + toRead = rb->size - rb->readPtr; + + if (toRead) + { + chunks[0].data = rb->buffer + rb->readPtr; + chunks[0].size = toRead; + remaining -= toRead; + chunkIndex++; + status++; + } + + if (remaining) + { + chunks[chunkIndex].data = rb->buffer; + chunks[chunkIndex].size = remaining; + status++; + } + + return status; +} + +void ringbuffer_commit_read_bytes(RingBuffer* rb, size_t sz) +{ + DEBUG_RINGBUFFER("ringbuffer_commit_read_bytes(%p): sz: %" PRIdz "", (void*)rb, sz); + + if (sz < 1) + return; + + WINPR_ASSERT(rb->size - rb->freeSize >= sz); + rb->readPtr = (rb->readPtr + sz) % rb->size; + rb->freeSize += sz; + + /* when we reach a reasonable free size, we can go back to the original size */ + if ((rb->size != rb->initialSize) && (ringbuffer_used(rb) < rb->initialSize / 2)) + ringbuffer_realloc(rb, rb->initialSize); +} diff --git a/libfreerdp/utils/signal.c b/libfreerdp/utils/signal.c new file mode 100644 index 0000000..487b9b0 --- /dev/null +++ b/libfreerdp/utils/signal.c @@ -0,0 +1,264 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Signal handling + * + * Copyright 2011 Shea Levy <shea@shealevy.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. + */ + +#include <freerdp/config.h> + +#include <stddef.h> +#include <errno.h> +#include <string.h> + +#include <winpr/crt.h> + +#include <freerdp/utils/signal.h> +#include <freerdp/log.h> + +#ifndef _WIN32 +#include <signal.h> +#include <termios.h> +#endif + +#define TAG FREERDP_TAG("utils.signal") + +#ifdef _WIN32 + +int freerdp_handle_signals(void) +{ + errno = ENOSYS; + return -1; +} + +BOOL freerdp_add_signal_cleanup_handler(void* context, freerdp_signal_handler_t fkt) +{ + return FALSE; +} + +BOOL freerdp_del_signal_cleanup_handler(void* context, freerdp_signal_handler_t fkt) +{ + return FALSE; +} +#else + +#include <pthread.h> +#include <winpr/debug.h> + +static BOOL handlers_registered = FALSE; +static pthread_mutex_t signal_handler_lock = PTHREAD_MUTEX_INITIALIZER; + +typedef struct +{ + void* context; + freerdp_signal_handler_t handler; +} cleanup_handler_t; + +static size_t cleanup_handler_count = 0; +static cleanup_handler_t cleanup_handlers[20] = { 0 }; + +static void lock(void) +{ + const int rc = pthread_mutex_lock(&signal_handler_lock); + if (rc != 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "[pthread_mutex_lock] failed with %s [%d]", + winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc); + } +} + +static void unlock(void) +{ + const int rc = pthread_mutex_unlock(&signal_handler_lock); + if (rc != 0) + { + char ebuffer[256] = { 0 }; + WLog_ERR(TAG, "[pthread_mutex_lock] failed with %s [%d]", + winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc); + } +} + +static void term_handler(int signum) +{ + static BOOL recursive = FALSE; + + if (!recursive) + { + recursive = TRUE; + WLog_ERR(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum); + } + + lock(); + for (size_t x = 0; x < cleanup_handler_count; x++) + { + const cleanup_handler_t empty = { 0 }; + cleanup_handler_t* cur = &cleanup_handlers[x]; + if (cur->handler) + cur->handler(signum, strsignal(signum), cur->context); + *cur = empty; + } + cleanup_handler_count = 0; + unlock(); +} + +static void fatal_handler(int signum) +{ + struct sigaction default_sigaction; + sigset_t this_mask; + static BOOL recursive = FALSE; + + if (!recursive) + { + recursive = TRUE; + WLog_ERR(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum); + + winpr_log_backtrace(TAG, WLOG_ERROR, 20); + } + + default_sigaction.sa_handler = SIG_DFL; + sigfillset(&(default_sigaction.sa_mask)); + default_sigaction.sa_flags = 0; + sigaction(signum, &default_sigaction, NULL); + sigemptyset(&this_mask); + sigaddset(&this_mask, signum); + pthread_sigmask(SIG_UNBLOCK, &this_mask, NULL); + raise(signum); +} + +static const int term_signals[] = { SIGINT, SIGKILL, SIGQUIT, SIGSTOP, SIGTERM }; + +static const int fatal_signals[] = { SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGHUP, SIGILL, + SIGSEGV, SIGTSTP, SIGTTIN, SIGTTOU, SIGUSR1, SIGUSR2, +#ifdef SIGPOLL + SIGPOLL, +#endif +#ifdef SIGPROF + SIGPROF, +#endif +#ifdef SIGSYS + SIGSYS, +#endif + SIGTRAP, +#ifdef SIGVTALRM + SIGVTALRM, +#endif + SIGXCPU, SIGXFSZ }; + +static BOOL register_handlers(const int* signals, size_t count, void (*handler)(int)) +{ + WINPR_ASSERT(signals || (count == 0)); + WINPR_ASSERT(handler); + + sigset_t orig_set = { 0 }; + struct sigaction saction = { 0 }; + + pthread_sigmask(SIG_BLOCK, &(saction.sa_mask), &orig_set); + + sigfillset(&(saction.sa_mask)); + sigdelset(&(saction.sa_mask), SIGCONT); + + saction.sa_handler = handler; + saction.sa_flags = 0; + + for (size_t x = 0; x < count; x++) + { + struct sigaction orig_sigaction = { 0 }; + if (sigaction(signals[x], NULL, &orig_sigaction) == 0) + { + if (orig_sigaction.sa_handler != SIG_IGN) + { + sigaction(signals[x], &saction, NULL); + } + } + } + + pthread_sigmask(SIG_SETMASK, &orig_set, NULL); + + return TRUE; +} + +int freerdp_handle_signals(void) +{ + int rc = -1; + + lock(); + + WLog_DBG(TAG, "Registering signal hook..."); + + if (!register_handlers(fatal_signals, ARRAYSIZE(fatal_signals), fatal_handler)) + goto fail; + if (!register_handlers(term_signals, ARRAYSIZE(term_signals), term_handler)) + goto fail; + + /* Ignore SIGPIPE signal. */ + signal(SIGPIPE, SIG_IGN); + handlers_registered = TRUE; + rc = 0; +fail: + unlock(); + return rc; +} + +BOOL freerdp_add_signal_cleanup_handler(void* context, freerdp_signal_handler_t handler) +{ + BOOL rc = FALSE; + lock(); + if (handlers_registered) + { + if (cleanup_handler_count < ARRAYSIZE(cleanup_handlers)) + { + cleanup_handler_t* cur = &cleanup_handlers[cleanup_handler_count++]; + cur->context = context; + cur->handler = handler; + } + else + WLog_WARN(TAG, "Failed to register cleanup handler, only %" PRIuz " handlers supported", + ARRAYSIZE(cleanup_handlers)); + } + rc = TRUE; + unlock(); + return rc; +} + +BOOL freerdp_del_signal_cleanup_handler(void* context, freerdp_signal_handler_t handler) +{ + BOOL rc = FALSE; + lock(); + if (handlers_registered) + { + for (size_t x = 0; x < cleanup_handler_count; x++) + { + cleanup_handler_t* cur = &cleanup_handlers[x]; + if ((cur->context == context) && (cur->handler == handler)) + { + const cleanup_handler_t empty = { 0 }; + for (size_t y = x + 1; y < cleanup_handler_count - 1; y++) + { + *cur++ = cleanup_handlers[y]; + } + + *cur = empty; + cleanup_handler_count--; + break; + } + } + } + rc = TRUE; + unlock(); + return rc; +} + +#endif diff --git a/libfreerdp/utils/smartcard_call.c b/libfreerdp/utils/smartcard_call.c new file mode 100644 index 0000000..a957e08 --- /dev/null +++ b/libfreerdp/utils/smartcard_call.c @@ -0,0 +1,2019 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Smartcard Device Service Virtual Channel + * + * Copyright (C) Alexi Volkov <alexi@myrealbox.com> 2006 + * Copyright 2011 O.S. Systems Software Ltda. + * Copyright 2011 Anthony Tong <atong@trustedcs.com> + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com> + * Copyright 2017 Armin Novak <armin.novak@thincast.com> + * Copyright 2017 Thincast Technologies GmbH + * + * 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. + */ + +#include <freerdp/config.h> + +#include <winpr/assert.h> + +#include <winpr/crt.h> +#include <winpr/print.h> +#include <winpr/stream.h> +#include <winpr/library.h> +#include <winpr/smartcard.h> + +#include <freerdp/freerdp.h> +#include <freerdp/channels/rdpdr.h> +#include <freerdp/channels/scard.h> + +#include <freerdp/utils/rdpdr_utils.h> +#include <freerdp/utils/smartcard_pack.h> +#include <freerdp/utils/smartcard_call.h> + +#include <freerdp/log.h> +#define TAG FREERDP_TAG("utils.smartcard.call") + +#if defined(WITH_SMARTCARD_EMULATE) +#include <freerdp/emulate/scard/smartcard_emulate.h> + +#define str(x) #x +#define wrap(ctx, fkt, ...) \ + ctx->useEmulatedCard ? Emulate_##fkt(ctx->emulation, ##__VA_ARGS__) \ + : ctx->pWinSCardApi->pfn##fkt(__VA_ARGS__) +#define wrap_ptr(ctx, fkt, ...) wrap(ctx, fkt, ##__VA_ARGS__) +#else +#define wrap(ctx, fkt, ...) \ + ctx->useEmulatedCard ? SCARD_F_INTERNAL_ERROR : ctx->pWinSCardApi->pfn##fkt(__VA_ARGS__) +#define wrap_ptr(ctx, fkt, ...) \ + ctx->useEmulatedCard ? NULL : ctx->pWinSCardApi->pfn##fkt(__VA_ARGS__) +#endif + +struct s_scard_call_context +{ + BOOL useEmulatedCard; + HANDLE StartedEvent; + wLinkedList* names; + wHashTable* rgSCardContextList; +#if defined(WITH_SMARTCARD_EMULATE) + SmartcardEmulationContext* emulation; +#endif + HANDLE hWinSCardLibrary; + SCardApiFunctionTable WinSCardApi; + const SCardApiFunctionTable* pWinSCardApi; + HANDLE stopEvent; + void* userdata; + + void* (*fn_new)(void*, SCARDCONTEXT); + void (*fn_free)(void*); +}; + +struct s_scard_context_element +{ + void* context; + void (*fn_free)(void*); +}; + +static void context_free(void* arg); + +static LONG smartcard_EstablishContext_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + SCARDCONTEXT hContext = { 0 }; + EstablishContext_Return ret = { 0 }; + EstablishContext_Call* call = &operation->call.establishContext; + status = ret.ReturnCode = + wrap(smartcard, SCardEstablishContext, call->dwScope, NULL, NULL, &hContext); + + if (ret.ReturnCode == SCARD_S_SUCCESS) + { + const void* key = (void*)(size_t)hContext; + struct s_scard_context_element* pContext = + calloc(1, sizeof(struct s_scard_context_element)); + if (!pContext) + return STATUS_NO_MEMORY; + + pContext->fn_free = smartcard->fn_free; + + if (smartcard->fn_new) + { + pContext->context = smartcard->fn_new(smartcard->userdata, hContext); + if (!pContext->context) + { + free(pContext); + return STATUS_NO_MEMORY; + } + } + + if (!HashTable_Insert(smartcard->rgSCardContextList, key, (void*)pContext)) + { + WLog_ERR(TAG, "ListDictionary_Add failed!"); + context_free(pContext); + return STATUS_INTERNAL_ERROR; + } + } + else + { + return scard_log_status_error(TAG, "SCardEstablishContext", status); + } + + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert takes ownership of pContext + smartcard_scard_context_native_to_redir(&(ret.hContext), hContext); + + status = smartcard_pack_establish_context_return(out, &ret); + if (status != SCARD_S_SUCCESS) + { + return scard_log_status_error(TAG, "smartcard_pack_establish_context_return", status); + } + + return ret.ReturnCode; +} + +static LONG smartcard_ReleaseContext_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + ret.ReturnCode = wrap(smartcard, SCardReleaseContext, operation->hContext); + + if (ret.ReturnCode == SCARD_S_SUCCESS) + HashTable_Remove(smartcard->rgSCardContextList, (void*)operation->hContext); + else + { + return scard_log_status_error(TAG, "SCardReleaseContext", ret.ReturnCode); + } + + smartcard_trace_long_return(&ret, "ReleaseContext"); + return ret.ReturnCode; +} + +static LONG smartcard_IsValidContext_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + ret.ReturnCode = wrap(smartcard, SCardIsValidContext, operation->hContext); + smartcard_trace_long_return(&ret, "IsValidContext"); + return ret.ReturnCode; +} + +static LONG smartcard_ListReaderGroupsA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + ListReaderGroups_Return ret = { 0 }; + LPSTR mszGroups = NULL; + DWORD cchGroups = 0; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + cchGroups = SCARD_AUTOALLOCATE; + ret.ReturnCode = + wrap(smartcard, SCardListReaderGroupsA, operation->hContext, (LPSTR)&mszGroups, &cchGroups); + ret.msz = (BYTE*)mszGroups; + ret.cBytes = cchGroups; + + status = smartcard_pack_list_reader_groups_return(out, &ret, FALSE); + + if (status != SCARD_S_SUCCESS) + return status; + + if (mszGroups) + wrap(smartcard, SCardFreeMemory, operation->hContext, mszGroups); + + return ret.ReturnCode; +} + +static LONG smartcard_ListReaderGroupsW_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + ListReaderGroups_Return ret = { 0 }; + LPWSTR mszGroups = NULL; + DWORD cchGroups = 0; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + cchGroups = SCARD_AUTOALLOCATE; + status = ret.ReturnCode = wrap(smartcard, SCardListReaderGroupsW, operation->hContext, + (LPWSTR)&mszGroups, &cchGroups); + ret.msz = (BYTE*)mszGroups; + ret.cBytes = cchGroups * sizeof(WCHAR); + + if (status != SCARD_S_SUCCESS) + return status; + + status = smartcard_pack_list_reader_groups_return(out, &ret, TRUE); + + if (status != SCARD_S_SUCCESS) + return status; + + if (mszGroups) + wrap(smartcard, SCardFreeMemory, operation->hContext, mszGroups); + + return ret.ReturnCode; +} + +static BOOL filter_match(wLinkedList* list, LPCSTR reader, size_t readerLen) +{ + if (readerLen < 1) + return FALSE; + + LinkedList_Enumerator_Reset(list); + + while (LinkedList_Enumerator_MoveNext(list)) + { + const char* filter = LinkedList_Enumerator_Current(list); + + if (filter) + { + if (strstr(reader, filter) != NULL) + return TRUE; + } + } + + return FALSE; +} + +static DWORD filter_device_by_name_a(wLinkedList* list, LPSTR* mszReaders, DWORD cchReaders) +{ + size_t rpos = 0; + size_t wpos = 0; + + if (*mszReaders == NULL || LinkedList_Count(list) < 1) + return cchReaders; + + do + { + LPCSTR rreader = &(*mszReaders)[rpos]; + LPSTR wreader = &(*mszReaders)[wpos]; + size_t readerLen = strnlen(rreader, cchReaders - rpos); + + rpos += readerLen + 1; + + if (filter_match(list, rreader, readerLen)) + { + if (rreader != wreader) + memmove(wreader, rreader, readerLen + 1); + + wpos += readerLen + 1; + } + } while (rpos < cchReaders); + + /* this string must be double 0 terminated */ + if (rpos != wpos) + { + if (wpos >= cchReaders) + return 0; + + (*mszReaders)[wpos++] = '\0'; + } + + return (DWORD)wpos; +} + +static DWORD filter_device_by_name_w(wLinkedList* list, LPWSTR* mszReaders, DWORD cchReaders) +{ + DWORD rc = 0; + LPSTR readers = NULL; + + if (LinkedList_Count(list) < 1) + return cchReaders; + + readers = ConvertMszWCharNToUtf8Alloc(*mszReaders, cchReaders, NULL); + + if (!readers) + { + free(readers); + return 0; + } + + free(*mszReaders); + *mszReaders = NULL; + rc = filter_device_by_name_a(list, &readers, cchReaders); + + *mszReaders = ConvertMszUtf8NToWCharAlloc(readers, rc, NULL); + if (!*mszReaders) + rc = 0; + + free(readers); + return rc; +} + +static LONG smartcard_ListReadersA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + ListReaders_Return ret = { 0 }; + LPSTR mszReaders = NULL; + DWORD cchReaders = 0; + ListReaders_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.listReaders; + cchReaders = SCARD_AUTOALLOCATE; + status = ret.ReturnCode = wrap(smartcard, SCardListReadersA, operation->hContext, + (LPCSTR)call->mszGroups, (LPSTR)&mszReaders, &cchReaders); + + if (status != SCARD_S_SUCCESS) + { + return scard_log_status_error(TAG, "SCardListReadersA", status); + } + + cchReaders = filter_device_by_name_a(smartcard->names, &mszReaders, cchReaders); + ret.msz = (BYTE*)mszReaders; + ret.cBytes = cchReaders; + + status = smartcard_pack_list_readers_return(out, &ret, FALSE); + if (status != SCARD_S_SUCCESS) + { + return scard_log_status_error(TAG, "smartcard_pack_list_readers_return", status); + } + + if (mszReaders) + wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaders); + + if (status != SCARD_S_SUCCESS) + return status; + + return ret.ReturnCode; +} + +static LONG smartcard_ListReadersW_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + ListReaders_Return ret = { 0 }; + DWORD cchReaders = 0; + ListReaders_Call* call = NULL; + union + { + const BYTE* bp; + const char* sz; + const WCHAR* wz; + } string; + union + { + WCHAR** ppw; + WCHAR* pw; + CHAR* pc; + BYTE* pb; + } mszReaders; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(operation); + + call = &operation->call.listReaders; + + string.bp = call->mszGroups; + cchReaders = SCARD_AUTOALLOCATE; + status = ret.ReturnCode = wrap(smartcard, SCardListReadersW, operation->hContext, string.wz, + (LPWSTR)&mszReaders.pw, &cchReaders); + + if (status != SCARD_S_SUCCESS) + return scard_log_status_error(TAG, "SCardListReadersW", status); + + cchReaders = filter_device_by_name_w(smartcard->names, &mszReaders.pw, cchReaders); + ret.msz = mszReaders.pb; + ret.cBytes = cchReaders * sizeof(WCHAR); + status = smartcard_pack_list_readers_return(out, &ret, TRUE); + + if (mszReaders.pb) + wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaders.pb); + + if (status != SCARD_S_SUCCESS) + return status; + + return ret.ReturnCode; +} + +static LONG smartcard_IntroduceReaderGroupA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + ContextAndStringA_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.contextAndStringA; + ret.ReturnCode = wrap(smartcard, SCardIntroduceReaderGroupA, operation->hContext, call->sz); + scard_log_status_error(TAG, "SCardIntroduceReaderGroupA", ret.ReturnCode); + smartcard_trace_long_return(&ret, "IntroduceReaderGroupA"); + return ret.ReturnCode; +} + +static LONG smartcard_IntroduceReaderGroupW_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + ContextAndStringW_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.contextAndStringW; + ret.ReturnCode = wrap(smartcard, SCardIntroduceReaderGroupW, operation->hContext, call->sz); + scard_log_status_error(TAG, "SCardIntroduceReaderGroupW", ret.ReturnCode); + smartcard_trace_long_return(&ret, "IntroduceReaderGroupW"); + return ret.ReturnCode; +} + +static LONG smartcard_IntroduceReaderA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + ContextAndTwoStringA_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.contextAndTwoStringA; + ret.ReturnCode = + wrap(smartcard, SCardIntroduceReaderA, operation->hContext, call->sz1, call->sz2); + scard_log_status_error(TAG, "SCardIntroduceReaderA", ret.ReturnCode); + smartcard_trace_long_return(&ret, "IntroduceReaderA"); + return ret.ReturnCode; +} + +static LONG smartcard_IntroduceReaderW_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + ContextAndTwoStringW_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.contextAndTwoStringW; + ret.ReturnCode = + wrap(smartcard, SCardIntroduceReaderW, operation->hContext, call->sz1, call->sz2); + scard_log_status_error(TAG, "SCardIntroduceReaderW", ret.ReturnCode); + smartcard_trace_long_return(&ret, "IntroduceReaderW"); + return ret.ReturnCode; +} + +static LONG smartcard_ForgetReaderA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + ContextAndStringA_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.contextAndStringA; + ret.ReturnCode = wrap(smartcard, SCardForgetReaderA, operation->hContext, call->sz); + scard_log_status_error(TAG, "SCardForgetReaderA", ret.ReturnCode); + smartcard_trace_long_return(&ret, "SCardForgetReaderA"); + return ret.ReturnCode; +} + +static LONG smartcard_ForgetReaderW_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + ContextAndStringW_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.contextAndStringW; + ret.ReturnCode = wrap(smartcard, SCardForgetReaderW, operation->hContext, call->sz); + scard_log_status_error(TAG, "SCardForgetReaderW", ret.ReturnCode); + smartcard_trace_long_return(&ret, "SCardForgetReaderW"); + return ret.ReturnCode; +} + +static LONG smartcard_AddReaderToGroupA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + ContextAndTwoStringA_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.contextAndTwoStringA; + ret.ReturnCode = + wrap(smartcard, SCardAddReaderToGroupA, operation->hContext, call->sz1, call->sz2); + scard_log_status_error(TAG, "SCardAddReaderToGroupA", ret.ReturnCode); + smartcard_trace_long_return(&ret, "SCardAddReaderToGroupA"); + return ret.ReturnCode; +} + +static LONG smartcard_AddReaderToGroupW_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + ContextAndTwoStringW_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.contextAndTwoStringW; + ret.ReturnCode = + wrap(smartcard, SCardAddReaderToGroupW, operation->hContext, call->sz1, call->sz2); + scard_log_status_error(TAG, "SCardAddReaderToGroupW", ret.ReturnCode); + smartcard_trace_long_return(&ret, "SCardAddReaderToGroupA"); + return ret.ReturnCode; +} + +static LONG smartcard_RemoveReaderFromGroupA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + ContextAndTwoStringA_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.contextAndTwoStringA; + ret.ReturnCode = + wrap(smartcard, SCardRemoveReaderFromGroupA, operation->hContext, call->sz1, call->sz2); + scard_log_status_error(TAG, "SCardRemoveReaderFromGroupA", ret.ReturnCode); + smartcard_trace_long_return(&ret, "SCardRemoveReaderFromGroupA"); + return ret.ReturnCode; +} + +static LONG smartcard_RemoveReaderFromGroupW_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + ContextAndTwoStringW_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.contextAndTwoStringW; + ret.ReturnCode = + wrap(smartcard, SCardRemoveReaderFromGroupW, operation->hContext, call->sz1, call->sz2); + scard_log_status_error(TAG, "SCardRemoveReaderFromGroupW", ret.ReturnCode); + smartcard_trace_long_return(&ret, "SCardRemoveReaderFromGroupW"); + return ret.ReturnCode; +} + +static LONG smartcard_LocateCardsA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + LocateCards_Return ret = { 0 }; + LocateCardsA_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(operation); + + call = &operation->call.locateCardsA; + + ret.ReturnCode = wrap(smartcard, SCardLocateCardsA, operation->hContext, call->mszCards, + call->rgReaderStates, call->cReaders); + scard_log_status_error(TAG, "SCardLocateCardsA", ret.ReturnCode); + ret.cReaders = call->cReaders; + ret.rgReaderStates = NULL; + + if (ret.cReaders > 0) + { + ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return)); + + if (!ret.rgReaderStates) + return STATUS_NO_MEMORY; + } + + for (UINT32 x = 0; x < ret.cReaders; x++) + { + ret.rgReaderStates[x].dwCurrentState = call->rgReaderStates[x].dwCurrentState; + ret.rgReaderStates[x].dwEventState = call->rgReaderStates[x].dwEventState; + ret.rgReaderStates[x].cbAtr = call->rgReaderStates[x].cbAtr; + CopyMemory(&(ret.rgReaderStates[x].rgbAtr), &(call->rgReaderStates[x].rgbAtr), + sizeof(ret.rgReaderStates[x].rgbAtr)); + } + + status = smartcard_pack_locate_cards_return(out, &ret); + + if (status != SCARD_S_SUCCESS) + return status; + + return ret.ReturnCode; +} + +static LONG smartcard_LocateCardsW_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + LocateCards_Return ret = { 0 }; + LocateCardsW_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(operation); + + call = &operation->call.locateCardsW; + + ret.ReturnCode = wrap(smartcard, SCardLocateCardsW, operation->hContext, call->mszCards, + call->rgReaderStates, call->cReaders); + scard_log_status_error(TAG, "SCardLocateCardsW", ret.ReturnCode); + ret.cReaders = call->cReaders; + ret.rgReaderStates = NULL; + + if (ret.cReaders > 0) + { + ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return)); + + if (!ret.rgReaderStates) + return STATUS_NO_MEMORY; + } + + for (UINT32 x = 0; x < ret.cReaders; x++) + { + ret.rgReaderStates[x].dwCurrentState = call->rgReaderStates[x].dwCurrentState; + ret.rgReaderStates[x].dwEventState = call->rgReaderStates[x].dwEventState; + ret.rgReaderStates[x].cbAtr = call->rgReaderStates[x].cbAtr; + CopyMemory(&(ret.rgReaderStates[x].rgbAtr), &(call->rgReaderStates[x].rgbAtr), + sizeof(ret.rgReaderStates[x].rgbAtr)); + } + + status = smartcard_pack_locate_cards_return(out, &ret); + + if (status != SCARD_S_SUCCESS) + return status; + + return ret.ReturnCode; +} + +static LONG smartcard_ReadCacheA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + BOOL autoalloc = 0; + ReadCache_Return ret = { 0 }; + ReadCacheA_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.readCacheA; + autoalloc = (call->Common.cbDataLen == SCARD_AUTOALLOCATE); + + if (!call->Common.fPbDataIsNULL) + { + ret.cbDataLen = call->Common.cbDataLen; + if (!autoalloc) + { + ret.pbData = malloc(ret.cbDataLen); + if (!ret.pbData) + return SCARD_F_INTERNAL_ERROR; + } + } + + if (autoalloc) + ret.ReturnCode = wrap(smartcard, SCardReadCacheA, operation->hContext, + call->Common.CardIdentifier, call->Common.FreshnessCounter, + call->szLookupName, (BYTE*)&ret.pbData, &ret.cbDataLen); + else + ret.ReturnCode = + wrap(smartcard, SCardReadCacheA, operation->hContext, call->Common.CardIdentifier, + call->Common.FreshnessCounter, call->szLookupName, ret.pbData, &ret.cbDataLen); + if ((ret.ReturnCode != SCARD_W_CACHE_ITEM_NOT_FOUND) && + (ret.ReturnCode != SCARD_W_CACHE_ITEM_STALE)) + { + scard_log_status_error(TAG, "SCardReadCacheA", ret.ReturnCode); + } + + status = smartcard_pack_read_cache_return(out, &ret); + if (autoalloc) + wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData); + else + free(ret.pbData); + if (status != SCARD_S_SUCCESS) + return status; + + return ret.ReturnCode; +} + +static LONG smartcard_ReadCacheW_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + ReadCache_Return ret = { 0 }; + ReadCacheW_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.readCacheW; + + if (!call->Common.fPbDataIsNULL) + ret.cbDataLen = SCARD_AUTOALLOCATE; + + ret.ReturnCode = + wrap(smartcard, SCardReadCacheW, operation->hContext, call->Common.CardIdentifier, + call->Common.FreshnessCounter, call->szLookupName, (BYTE*)&ret.pbData, &ret.cbDataLen); + + if ((ret.ReturnCode != SCARD_W_CACHE_ITEM_NOT_FOUND) && + (ret.ReturnCode != SCARD_W_CACHE_ITEM_STALE)) + { + scard_log_status_error(TAG, "SCardReadCacheA", ret.ReturnCode); + } + + status = smartcard_pack_read_cache_return(out, &ret); + + wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData); + + if (status != SCARD_S_SUCCESS) + return status; + + return ret.ReturnCode; +} + +static LONG smartcard_WriteCacheA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + WriteCacheA_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.writeCacheA; + + ret.ReturnCode = wrap(smartcard, SCardWriteCacheA, operation->hContext, + call->Common.CardIdentifier, call->Common.FreshnessCounter, + call->szLookupName, call->Common.pbData, call->Common.cbDataLen); + scard_log_status_error(TAG, "SCardWriteCacheA", ret.ReturnCode); + smartcard_trace_long_return(&ret, "SCardWriteCacheA"); + return ret.ReturnCode; +} + +static LONG smartcard_WriteCacheW_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + WriteCacheW_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.writeCacheW; + + ret.ReturnCode = wrap(smartcard, SCardWriteCacheW, operation->hContext, + call->Common.CardIdentifier, call->Common.FreshnessCounter, + call->szLookupName, call->Common.pbData, call->Common.cbDataLen); + scard_log_status_error(TAG, "SCardWriteCacheW", ret.ReturnCode); + smartcard_trace_long_return(&ret, "SCardWriteCacheW"); + return ret.ReturnCode; +} + +static LONG smartcard_GetTransmitCount_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + GetTransmitCount_Return ret = { 0 }; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + ret.ReturnCode = wrap(smartcard, SCardGetTransmitCount, operation->hCard, &ret.cTransmitCount); + scard_log_status_error(TAG, "SCardGetTransmitCount", ret.ReturnCode); + status = smartcard_pack_get_transmit_count_return(out, &ret); + if (status != SCARD_S_SUCCESS) + return status; + + return ret.ReturnCode; +} + +static LONG smartcard_ReleaseStartedEvent_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + WINPR_UNUSED(smartcard); + WINPR_UNUSED(out); + WINPR_UNUSED(operation); + + WLog_WARN(TAG, "According to [MS-RDPESC] 3.1.4 Message Processing Events and Sequencing Rules " + "this is not supported?!?"); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG smartcard_GetReaderIcon_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + GetReaderIcon_Return ret = { 0 }; + GetReaderIcon_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.getReaderIcon; + + ret.cbDataLen = SCARD_AUTOALLOCATE; + ret.ReturnCode = wrap(smartcard, SCardGetReaderIconW, operation->hContext, call->szReaderName, + (LPBYTE)&ret.pbData, &ret.cbDataLen); + scard_log_status_error(TAG, "SCardGetReaderIconW", ret.ReturnCode); + + status = smartcard_pack_get_reader_icon_return(out, &ret); + wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbData); + if (status != SCARD_S_SUCCESS) + return status; + + return ret.ReturnCode; +} + +static LONG smartcard_GetDeviceTypeId_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + GetDeviceTypeId_Return ret = { 0 }; + GetDeviceTypeId_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.getDeviceTypeId; + + ret.ReturnCode = wrap(smartcard, SCardGetDeviceTypeIdW, operation->hContext, call->szReaderName, + &ret.dwDeviceId); + scard_log_status_error(TAG, "SCardGetDeviceTypeIdW", ret.ReturnCode); + + status = smartcard_pack_device_type_id_return(out, &ret); + if (status != SCARD_S_SUCCESS) + return status; + + return ret.ReturnCode; +} + +static LONG smartcard_GetStatusChangeA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = STATUS_NO_MEMORY; + DWORD dwTimeOut = 0; + const DWORD dwTimeStep = 100; + GetStatusChange_Return ret = { 0 }; + GetStatusChangeA_Call* call = NULL; + LPSCARD_READERSTATEA rgReaderStates = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.getStatusChangeA; + dwTimeOut = call->dwTimeOut; + + if (call->cReaders > 0) + { + ret.cReaders = call->cReaders; + rgReaderStates = calloc(ret.cReaders, sizeof(SCARD_READERSTATEA)); + ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return)); + if (!rgReaderStates || !ret.rgReaderStates) + goto fail; + } + + for (UINT32 x = 0; x < MAX(1, dwTimeOut);) + { + if (call->cReaders > 0) + memcpy(rgReaderStates, call->rgReaderStates, + call->cReaders * sizeof(SCARD_READERSTATEA)); + ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeA, operation->hContext, + MIN(dwTimeOut, dwTimeStep), rgReaderStates, call->cReaders); + if (ret.ReturnCode != SCARD_E_TIMEOUT) + break; + if (WaitForSingleObject(smartcard->stopEvent, 0) == WAIT_OBJECT_0) + break; + if (dwTimeOut != INFINITE) + x += dwTimeStep; + } + scard_log_status_error(TAG, "SCardGetStatusChangeA", ret.ReturnCode); + + for (UINT32 index = 0; index < ret.cReaders; index++) + { + const SCARD_READERSTATEA* cur = &rgReaderStates[index]; + ReaderState_Return* rout = &ret.rgReaderStates[index]; + + rout->dwCurrentState = cur->dwCurrentState; + rout->dwEventState = cur->dwEventState; + rout->cbAtr = cur->cbAtr; + CopyMemory(&(rout->rgbAtr), cur->rgbAtr, sizeof(rout->rgbAtr)); + } + + status = smartcard_pack_get_status_change_return(out, &ret, TRUE); +fail: + free(ret.rgReaderStates); + free(rgReaderStates); + if (status != SCARD_S_SUCCESS) + return status; + return ret.ReturnCode; +} + +static LONG smartcard_GetStatusChangeW_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = STATUS_NO_MEMORY; + DWORD dwTimeOut = 0; + const DWORD dwTimeStep = 100; + GetStatusChange_Return ret = { 0 }; + GetStatusChangeW_Call* call = NULL; + LPSCARD_READERSTATEW rgReaderStates = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.getStatusChangeW; + dwTimeOut = call->dwTimeOut; + + if (call->cReaders > 0) + { + ret.cReaders = call->cReaders; + rgReaderStates = calloc(ret.cReaders, sizeof(SCARD_READERSTATEW)); + ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return)); + if (!rgReaderStates || !ret.rgReaderStates) + goto fail; + } + + for (UINT32 x = 0; x < MAX(1, dwTimeOut);) + { + if (call->cReaders > 0) + memcpy(rgReaderStates, call->rgReaderStates, + call->cReaders * sizeof(SCARD_READERSTATEW)); + { + ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeW, operation->hContext, + MIN(dwTimeOut, dwTimeStep), rgReaderStates, call->cReaders); + } + if (ret.ReturnCode != SCARD_E_TIMEOUT) + break; + if (WaitForSingleObject(smartcard->stopEvent, 0) == WAIT_OBJECT_0) + break; + if (dwTimeOut != INFINITE) + x += dwTimeStep; + } + scard_log_status_error(TAG, "SCardGetStatusChangeW", ret.ReturnCode); + + for (UINT32 index = 0; index < ret.cReaders; index++) + { + const SCARD_READERSTATEW* cur = &rgReaderStates[index]; + ReaderState_Return* rout = &ret.rgReaderStates[index]; + + rout->dwCurrentState = cur->dwCurrentState; + rout->dwEventState = cur->dwEventState; + rout->cbAtr = cur->cbAtr; + CopyMemory(&(rout->rgbAtr), cur->rgbAtr, sizeof(rout->rgbAtr)); + } + + status = smartcard_pack_get_status_change_return(out, &ret, TRUE); +fail: + free(ret.rgReaderStates); + free(rgReaderStates); + if (status != SCARD_S_SUCCESS) + return status; + return ret.ReturnCode; +} + +static LONG smartcard_Cancel_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + ret.ReturnCode = wrap(smartcard, SCardCancel, operation->hContext); + scard_log_status_error(TAG, "SCardCancel", ret.ReturnCode); + smartcard_trace_long_return(&ret, "Cancel"); + return ret.ReturnCode; +} + +static LONG smartcard_ConnectA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + SCARDHANDLE hCard = 0; + Connect_Return ret = { 0 }; + ConnectA_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.connectA; + + if ((call->Common.dwPreferredProtocols == SCARD_PROTOCOL_UNDEFINED) && + (call->Common.dwShareMode != SCARD_SHARE_DIRECT)) + { + call->Common.dwPreferredProtocols = SCARD_PROTOCOL_Tx; + } + + ret.ReturnCode = wrap(smartcard, SCardConnectA, operation->hContext, (char*)call->szReader, + call->Common.dwShareMode, call->Common.dwPreferredProtocols, &hCard, + &ret.dwActiveProtocol); + smartcard_scard_context_native_to_redir(&(ret.hContext), operation->hContext); + smartcard_scard_handle_native_to_redir(&(ret.hCard), hCard); + + status = smartcard_pack_connect_return(out, &ret); + if (status != SCARD_S_SUCCESS) + goto out_fail; + + status = ret.ReturnCode; +out_fail: + + return status; +} + +static LONG smartcard_ConnectW_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + SCARDHANDLE hCard = 0; + Connect_Return ret = { 0 }; + ConnectW_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.connectW; + + if ((call->Common.dwPreferredProtocols == SCARD_PROTOCOL_UNDEFINED) && + (call->Common.dwShareMode != SCARD_SHARE_DIRECT)) + { + call->Common.dwPreferredProtocols = SCARD_PROTOCOL_Tx; + } + + ret.ReturnCode = wrap(smartcard, SCardConnectW, operation->hContext, (WCHAR*)call->szReader, + call->Common.dwShareMode, call->Common.dwPreferredProtocols, &hCard, + &ret.dwActiveProtocol); + smartcard_scard_context_native_to_redir(&(ret.hContext), operation->hContext); + smartcard_scard_handle_native_to_redir(&(ret.hCard), hCard); + + status = smartcard_pack_connect_return(out, &ret); + if (status != SCARD_S_SUCCESS) + goto out_fail; + + status = ret.ReturnCode; +out_fail: + + return status; +} + +static LONG smartcard_Reconnect_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + Reconnect_Return ret = { 0 }; + Reconnect_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.reconnect; + ret.ReturnCode = + wrap(smartcard, SCardReconnect, operation->hCard, call->dwShareMode, + call->dwPreferredProtocols, call->dwInitialization, &ret.dwActiveProtocol); + scard_log_status_error(TAG, "SCardReconnect", ret.ReturnCode); + status = smartcard_pack_reconnect_return(out, &ret); + if (status != SCARD_S_SUCCESS) + return status; + + return ret.ReturnCode; +} + +static LONG smartcard_Disconnect_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + HCardAndDisposition_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.hCardAndDisposition; + + ret.ReturnCode = wrap(smartcard, SCardDisconnect, operation->hCard, call->dwDisposition); + scard_log_status_error(TAG, "SCardDisconnect", ret.ReturnCode); + smartcard_trace_long_return(&ret, "Disconnect"); + + return ret.ReturnCode; +} + +static LONG smartcard_BeginTransaction_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + ret.ReturnCode = wrap(smartcard, SCardBeginTransaction, operation->hCard); + scard_log_status_error(TAG, "SCardBeginTransaction", ret.ReturnCode); + smartcard_trace_long_return(&ret, "BeginTransaction"); + return ret.ReturnCode; +} + +static LONG smartcard_EndTransaction_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + HCardAndDisposition_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.hCardAndDisposition; + + ret.ReturnCode = wrap(smartcard, SCardEndTransaction, operation->hCard, call->dwDisposition); + scard_log_status_error(TAG, "SCardEndTransaction", ret.ReturnCode); + smartcard_trace_long_return(&ret, "EndTransaction"); + return ret.ReturnCode; +} + +static LONG smartcard_State_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + State_Return ret = { 0 }; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + ret.cbAtrLen = SCARD_ATR_LENGTH; + ret.ReturnCode = wrap(smartcard, SCardState, operation->hCard, &ret.dwState, &ret.dwProtocol, + (BYTE*)&ret.rgAtr, &ret.cbAtrLen); + + scard_log_status_error(TAG, "SCardState", ret.ReturnCode); + status = smartcard_pack_state_return(out, &ret); + if (status != SCARD_S_SUCCESS) + return status; + + return ret.ReturnCode; +} + +static LONG smartcard_StatusA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + Status_Return ret = { 0 }; + DWORD cchReaderLen = 0; + DWORD cbAtrLen = 0; + LPSTR mszReaderNames = NULL; + Status_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.status; + + call->cbAtrLen = 32; + cbAtrLen = call->cbAtrLen; + + if (call->fmszReaderNamesIsNULL) + cchReaderLen = 0; + else + cchReaderLen = SCARD_AUTOALLOCATE; + + status = ret.ReturnCode = + wrap(smartcard, SCardStatusA, operation->hCard, + call->fmszReaderNamesIsNULL ? NULL : (LPSTR)&mszReaderNames, &cchReaderLen, + &ret.dwState, &ret.dwProtocol, cbAtrLen ? (BYTE*)&ret.pbAtr : NULL, &cbAtrLen); + + scard_log_status_error(TAG, "SCardStatusA", status); + if (status == SCARD_S_SUCCESS) + { + if (!call->fmszReaderNamesIsNULL) + ret.mszReaderNames = (BYTE*)mszReaderNames; + + ret.cBytes = cchReaderLen; + + if (call->cbAtrLen) + ret.cbAtrLen = cbAtrLen; + } + + status = smartcard_pack_status_return(out, &ret, FALSE); + + if (mszReaderNames) + wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaderNames); + + if (status != SCARD_S_SUCCESS) + return status; + return ret.ReturnCode; +} + +static LONG smartcard_StatusW_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + Status_Return ret = { 0 }; + LPWSTR mszReaderNames = NULL; + Status_Call* call = NULL; + DWORD cbAtrLen = 0; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.status; + + /** + * [MS-RDPESC] + * According to 2.2.2.18 Status_Call cbAtrLen is unused an must be ignored upon receipt. + */ + cbAtrLen = call->cbAtrLen = 32; + + if (call->fmszReaderNamesIsNULL) + ret.cBytes = 0; + else + ret.cBytes = SCARD_AUTOALLOCATE; + + status = ret.ReturnCode = + wrap(smartcard, SCardStatusW, operation->hCard, + call->fmszReaderNamesIsNULL ? NULL : (LPWSTR)&mszReaderNames, &ret.cBytes, + &ret.dwState, &ret.dwProtocol, (BYTE*)&ret.pbAtr, &cbAtrLen); + scard_log_status_error(TAG, "SCardStatusW", status); + if (status == SCARD_S_SUCCESS) + { + if (!call->fmszReaderNamesIsNULL) + ret.mszReaderNames = (BYTE*)mszReaderNames; + + ret.cbAtrLen = cbAtrLen; + } + + /* SCardStatusW returns number of characters, we need number of bytes */ + ret.cBytes *= sizeof(WCHAR); + + status = smartcard_pack_status_return(out, &ret, TRUE); + if (status != SCARD_S_SUCCESS) + return status; + + if (mszReaderNames) + wrap(smartcard, SCardFreeMemory, operation->hContext, mszReaderNames); + + return ret.ReturnCode; +} + +static LONG smartcard_Transmit_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + Transmit_Return ret = { 0 }; + Transmit_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.transmit; + ret.cbRecvLength = 0; + ret.pbRecvBuffer = NULL; + + if (call->cbRecvLength && !call->fpbRecvBufferIsNULL) + { + if (call->cbRecvLength >= 66560) + call->cbRecvLength = 66560; + + ret.cbRecvLength = call->cbRecvLength; + ret.pbRecvBuffer = (BYTE*)malloc(ret.cbRecvLength); + + if (!ret.pbRecvBuffer) + return STATUS_NO_MEMORY; + } + + ret.pioRecvPci = call->pioRecvPci; + ret.ReturnCode = + wrap(smartcard, SCardTransmit, operation->hCard, call->pioSendPci, call->pbSendBuffer, + call->cbSendLength, ret.pioRecvPci, ret.pbRecvBuffer, &(ret.cbRecvLength)); + + scard_log_status_error(TAG, "SCardTransmit", ret.ReturnCode); + + status = smartcard_pack_transmit_return(out, &ret); + free(ret.pbRecvBuffer); + + if (status != SCARD_S_SUCCESS) + return status; + return ret.ReturnCode; +} + +static LONG smartcard_Control_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + Control_Return ret = { 0 }; + Control_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.control; + ret.cbOutBufferSize = call->cbOutBufferSize; + ret.pvOutBuffer = (BYTE*)malloc(call->cbOutBufferSize); + + if (!ret.pvOutBuffer) + return SCARD_E_NO_MEMORY; + + ret.ReturnCode = + wrap(smartcard, SCardControl, operation->hCard, call->dwControlCode, call->pvInBuffer, + call->cbInBufferSize, ret.pvOutBuffer, call->cbOutBufferSize, &ret.cbOutBufferSize); + scard_log_status_error(TAG, "SCardControl", ret.ReturnCode); + status = smartcard_pack_control_return(out, &ret); + + free(ret.pvOutBuffer); + if (status != SCARD_S_SUCCESS) + return status; + return ret.ReturnCode; +} + +static LONG smartcard_GetAttrib_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + BOOL autoAllocate = FALSE; + LONG status = 0; + DWORD cbAttrLen = 0; + LPBYTE pbAttr = NULL; + GetAttrib_Return ret = { 0 }; + const GetAttrib_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(operation); + + call = &operation->call.getAttrib; + + if (!call->fpbAttrIsNULL) + { + autoAllocate = (call->cbAttrLen == SCARD_AUTOALLOCATE) ? TRUE : FALSE; + cbAttrLen = call->cbAttrLen; + if (cbAttrLen && !autoAllocate) + { + ret.pbAttr = (BYTE*)malloc(cbAttrLen); + + if (!ret.pbAttr) + return SCARD_E_NO_MEMORY; + } + + pbAttr = autoAllocate ? (LPBYTE) & (ret.pbAttr) : ret.pbAttr; + } + + ret.ReturnCode = + wrap(smartcard, SCardGetAttrib, operation->hCard, call->dwAttrId, pbAttr, &cbAttrLen); + scard_log_status_error(TAG, "SCardGetAttrib", ret.ReturnCode); + ret.cbAttrLen = cbAttrLen; + + status = smartcard_pack_get_attrib_return(out, &ret, call->dwAttrId, call->cbAttrLen); + + if (autoAllocate) + wrap(smartcard, SCardFreeMemory, operation->hContext, ret.pbAttr); + else + free(ret.pbAttr); + return status; +} + +static LONG smartcard_SetAttrib_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + Long_Return ret = { 0 }; + SetAttrib_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(operation); + + call = &operation->call.setAttrib; + + ret.ReturnCode = wrap(smartcard, SCardSetAttrib, operation->hCard, call->dwAttrId, call->pbAttr, + call->cbAttrLen); + scard_log_status_error(TAG, "SCardSetAttrib", ret.ReturnCode); + smartcard_trace_long_return(&ret, "SetAttrib"); + + return ret.ReturnCode; +} + +static LONG smartcard_AccessStartedEvent_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = SCARD_S_SUCCESS; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_UNUSED(operation); + + if (!smartcard->StartedEvent) + smartcard->StartedEvent = wrap_ptr(smartcard, SCardAccessStartedEvent); + + if (!smartcard->StartedEvent) + status = SCARD_E_NO_SERVICE; + + return status; +} + +static LONG smartcard_LocateCardsByATRA_Call(scard_call_context* smartcard, wStream* out, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + GetStatusChange_Return ret = { 0 }; + LPSCARD_READERSTATEA state = NULL; + LPSCARD_READERSTATEA states = NULL; + LocateCardsByATRA_Call* call = NULL; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(operation); + + call = &operation->call.locateCardsByATRA; + states = (LPSCARD_READERSTATEA)calloc(call->cReaders, sizeof(SCARD_READERSTATEA)); + + if (!states) + return STATUS_NO_MEMORY; + + for (UINT32 i = 0; i < call->cReaders; i++) + { + states[i].szReader = (LPSTR)call->rgReaderStates[i].szReader; + states[i].dwCurrentState = call->rgReaderStates[i].dwCurrentState; + states[i].dwEventState = call->rgReaderStates[i].dwEventState; + states[i].cbAtr = call->rgReaderStates[i].cbAtr; + CopyMemory(&(states[i].rgbAtr), &(call->rgReaderStates[i].rgbAtr), 36); + } + + status = ret.ReturnCode = wrap(smartcard, SCardGetStatusChangeA, operation->hContext, + 0x000001F4, states, call->cReaders); + + scard_log_status_error(TAG, "SCardGetStatusChangeA", status); + for (UINT32 i = 0; i < call->cAtrs; i++) + { + for (UINT32 j = 0; j < call->cReaders; j++) + { + for (UINT32 k = 0; k < call->rgAtrMasks[i].cbAtr; k++) + { + if ((call->rgAtrMasks[i].rgbAtr[k] & call->rgAtrMasks[i].rgbMask[k]) != + (states[j].rgbAtr[k] & call->rgAtrMasks[i].rgbMask[k])) + { + break; + } + + states[j].dwEventState |= SCARD_STATE_ATRMATCH; + } + } + } + + ret.cReaders = call->cReaders; + ret.rgReaderStates = NULL; + + if (ret.cReaders > 0) + ret.rgReaderStates = (ReaderState_Return*)calloc(ret.cReaders, sizeof(ReaderState_Return)); + + if (!ret.rgReaderStates) + { + free(states); + return STATUS_NO_MEMORY; + } + + for (UINT32 i = 0; i < ret.cReaders; i++) + { + state = &states[i]; + ret.rgReaderStates[i].dwCurrentState = state->dwCurrentState; + ret.rgReaderStates[i].dwEventState = state->dwEventState; + ret.rgReaderStates[i].cbAtr = state->cbAtr; + CopyMemory(&(ret.rgReaderStates[i].rgbAtr), &(state->rgbAtr), + sizeof(ret.rgReaderStates[i].rgbAtr)); + } + + free(states); + + status = smartcard_pack_get_status_change_return(out, &ret, FALSE); + + free(ret.rgReaderStates); + if (status != SCARD_S_SUCCESS) + return status; + return ret.ReturnCode; +} + +LONG smartcard_irp_device_control_call(scard_call_context* smartcard, wStream* out, + UINT32* pIoStatus, SMARTCARD_OPERATION* operation) +{ + LONG result = 0; + UINT32 offset = 0; + UINT32 ioControlCode = 0; + size_t outputBufferLength = 0; + size_t objectBufferLength = 0; + + WINPR_ASSERT(smartcard); + WINPR_ASSERT(out); + WINPR_ASSERT(pIoStatus); + WINPR_ASSERT(operation); + + ioControlCode = operation->ioControlCode; + /** + * [MS-RDPESC] 3.2.5.1: Sending Outgoing Messages: + * the output buffer length SHOULD be set to 2048 + * + * Since it's a SHOULD and not a MUST, we don't care + * about it, but we still reserve at least 2048 bytes. + */ + if (!Stream_EnsureRemainingCapacity(out, 2048)) + return SCARD_E_NO_MEMORY; + + /* Device Control Response */ + Stream_Write_UINT32(out, 0); /* OutputBufferLength (4 bytes) */ + Stream_Zero(out, SMARTCARD_COMMON_TYPE_HEADER_LENGTH); /* CommonTypeHeader (8 bytes) */ + Stream_Zero(out, SMARTCARD_PRIVATE_TYPE_HEADER_LENGTH); /* PrivateTypeHeader (8 bytes) */ + Stream_Write_UINT32(out, 0); /* Result (4 bytes) */ + + /* Call */ + switch (ioControlCode) + { + case SCARD_IOCTL_ESTABLISHCONTEXT: + result = smartcard_EstablishContext_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_RELEASECONTEXT: + result = smartcard_ReleaseContext_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_ISVALIDCONTEXT: + result = smartcard_IsValidContext_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_LISTREADERGROUPSA: + result = smartcard_ListReaderGroupsA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_LISTREADERGROUPSW: + result = smartcard_ListReaderGroupsW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_LISTREADERSA: + result = smartcard_ListReadersA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_LISTREADERSW: + result = smartcard_ListReadersW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_INTRODUCEREADERGROUPA: + result = smartcard_IntroduceReaderGroupA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_INTRODUCEREADERGROUPW: + result = smartcard_IntroduceReaderGroupW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_FORGETREADERGROUPA: + result = smartcard_ForgetReaderA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_FORGETREADERGROUPW: + result = smartcard_ForgetReaderW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_INTRODUCEREADERA: + result = smartcard_IntroduceReaderA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_INTRODUCEREADERW: + result = smartcard_IntroduceReaderW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_FORGETREADERA: + result = smartcard_ForgetReaderA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_FORGETREADERW: + result = smartcard_ForgetReaderW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_ADDREADERTOGROUPA: + result = smartcard_AddReaderToGroupA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_ADDREADERTOGROUPW: + result = smartcard_AddReaderToGroupW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_REMOVEREADERFROMGROUPA: + result = smartcard_RemoveReaderFromGroupA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_REMOVEREADERFROMGROUPW: + result = smartcard_RemoveReaderFromGroupW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_LOCATECARDSA: + result = smartcard_LocateCardsA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_LOCATECARDSW: + result = smartcard_LocateCardsW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_GETSTATUSCHANGEA: + result = smartcard_GetStatusChangeA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_GETSTATUSCHANGEW: + result = smartcard_GetStatusChangeW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_CANCEL: + result = smartcard_Cancel_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_CONNECTA: + result = smartcard_ConnectA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_CONNECTW: + result = smartcard_ConnectW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_RECONNECT: + result = smartcard_Reconnect_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_DISCONNECT: + result = smartcard_Disconnect_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_BEGINTRANSACTION: + result = smartcard_BeginTransaction_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_ENDTRANSACTION: + result = smartcard_EndTransaction_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_STATE: + result = smartcard_State_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_STATUSA: + result = smartcard_StatusA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_STATUSW: + result = smartcard_StatusW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_TRANSMIT: + result = smartcard_Transmit_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_CONTROL: + result = smartcard_Control_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_GETATTRIB: + result = smartcard_GetAttrib_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_SETATTRIB: + result = smartcard_SetAttrib_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_ACCESSSTARTEDEVENT: + result = smartcard_AccessStartedEvent_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_LOCATECARDSBYATRA: + result = smartcard_LocateCardsByATRA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_LOCATECARDSBYATRW: + result = smartcard_LocateCardsW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_READCACHEA: + result = smartcard_ReadCacheA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_READCACHEW: + result = smartcard_ReadCacheW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_WRITECACHEA: + result = smartcard_WriteCacheA_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_WRITECACHEW: + result = smartcard_WriteCacheW_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_GETTRANSMITCOUNT: + result = smartcard_GetTransmitCount_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_RELEASETARTEDEVENT: + result = smartcard_ReleaseStartedEvent_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_GETREADERICON: + result = smartcard_GetReaderIcon_Call(smartcard, out, operation); + break; + + case SCARD_IOCTL_GETDEVICETYPEID: + result = smartcard_GetDeviceTypeId_Call(smartcard, out, operation); + break; + + default: + result = STATUS_UNSUCCESSFUL; + break; + } + + /** + * [MS-RPCE] 2.2.6.3 Primitive Type Serialization + * The type MUST be aligned on an 8-byte boundary. If the size of the + * primitive type is not a multiple of 8 bytes, the data MUST be padded. + */ + + if ((ioControlCode != SCARD_IOCTL_ACCESSSTARTEDEVENT) && + (ioControlCode != SCARD_IOCTL_RELEASETARTEDEVENT)) + { + offset = (RDPDR_DEVICE_IO_RESPONSE_LENGTH + RDPDR_DEVICE_IO_CONTROL_RSP_HDR_LENGTH); + smartcard_pack_write_size_align(out, Stream_GetPosition(out) - offset, 8); + } + + if ((result != SCARD_S_SUCCESS) && (result != SCARD_E_TIMEOUT) && + (result != SCARD_E_NO_READERS_AVAILABLE) && (result != SCARD_E_NO_SERVICE) && + (result != SCARD_W_CACHE_ITEM_NOT_FOUND) && (result != SCARD_W_CACHE_ITEM_STALE)) + { + WLog_WARN(TAG, "IRP failure: %s (0x%08" PRIX32 "), status: %s (0x%08" PRIX32 ")", + scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode, + SCardGetErrorString(result), result); + } + + *pIoStatus = STATUS_SUCCESS; + + if ((result & 0xC0000000L) == 0xC0000000L) + { + /* NTSTATUS error */ + *pIoStatus = (UINT32)result; + WLog_WARN(TAG, "IRP failure: %s (0x%08" PRIX32 "), ntstatus: 0x%08" PRIX32 "", + scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode, result); + } + + Stream_SealLength(out); + outputBufferLength = Stream_Length(out); + WINPR_ASSERT(outputBufferLength >= RDPDR_DEVICE_IO_RESPONSE_LENGTH - 4U); + outputBufferLength -= (RDPDR_DEVICE_IO_RESPONSE_LENGTH + 4U); + WINPR_ASSERT(outputBufferLength >= RDPDR_DEVICE_IO_RESPONSE_LENGTH); + objectBufferLength = outputBufferLength - RDPDR_DEVICE_IO_RESPONSE_LENGTH; + WINPR_ASSERT(outputBufferLength <= UINT32_MAX); + WINPR_ASSERT(objectBufferLength <= UINT32_MAX); + Stream_SetPosition(out, RDPDR_DEVICE_IO_RESPONSE_LENGTH); + /* Device Control Response */ + Stream_Write_UINT32(out, (UINT32)outputBufferLength); /* OutputBufferLength (4 bytes) */ + smartcard_pack_common_type_header(out); /* CommonTypeHeader (8 bytes) */ + smartcard_pack_private_type_header( + out, (UINT32)objectBufferLength); /* PrivateTypeHeader (8 bytes) */ + Stream_Write_INT32(out, result); /* Result (4 bytes) */ + Stream_SetPosition(out, Stream_Length(out)); + return SCARD_S_SUCCESS; +} + +void context_free(void* arg) +{ + struct s_scard_context_element* element = arg; + if (!arg) + return; + + if (element->fn_free) + element->fn_free(element->context); + free(element); +} + +scard_call_context* smartcard_call_context_new(const rdpSettings* settings) +{ + wObject* obj = NULL; + scard_call_context* ctx = NULL; + + WINPR_ASSERT(settings); + ctx = calloc(1, sizeof(scard_call_context)); + if (!ctx) + goto fail; + + ctx->stopEvent = CreateEventA(NULL, TRUE, FALSE, NULL); + if (!ctx->stopEvent) + goto fail; + + ctx->names = LinkedList_New(); + if (!ctx->names) + goto fail; + +#if defined(WITH_SMARTCARD_EMULATE) + ctx->useEmulatedCard = freerdp_settings_get_bool(settings, FreeRDP_SmartcardEmulation); +#endif + + if (ctx->useEmulatedCard) + { +#if defined(WITH_SMARTCARD_EMULATE) + ctx->emulation = Emulate_New(settings); + if (!ctx->emulation) + goto fail; +#else + WLog_ERR(TAG, "Smartcard emulation requested, but not supported!"); + goto fail; +#endif + } + else + { + const char* WinSCardModule = freerdp_settings_get_string(settings, FreeRDP_WinSCardModule); + if (WinSCardModule) + { + ctx->hWinSCardLibrary = LoadLibraryX(WinSCardModule); + + if (!ctx->hWinSCardLibrary) + { + WLog_ERR(TAG, "Failed to load WinSCard library: '%s'", WinSCardModule); + goto fail; + } + + if (!WinSCard_LoadApiTableFunctions(&ctx->WinSCardApi, ctx->hWinSCardLibrary)) + goto fail; + ctx->pWinSCardApi = &ctx->WinSCardApi; + } + else + { + ctx->pWinSCardApi = WinPR_GetSCardApiFunctionTable(); + } + + if (!ctx->pWinSCardApi) + { + WLog_ERR(TAG, "Failed to load WinSCard API!"); + goto fail; + } + } + + ctx->rgSCardContextList = HashTable_New(FALSE); + if (!ctx->rgSCardContextList) + goto fail; + + obj = HashTable_ValueObject(ctx->rgSCardContextList); + WINPR_ASSERT(obj); + obj->fnObjectFree = context_free; + + return ctx; +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + smartcard_call_context_free(ctx); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +void smartcard_call_context_free(scard_call_context* ctx) +{ + if (!ctx) + return; + + smartcard_call_context_signal_stop(ctx, FALSE); + + LinkedList_Free(ctx->names); + if (ctx->StartedEvent) + { + WINPR_ASSERT(ctx->useEmulatedCard || ctx->pWinSCardApi); + wrap(ctx, SCardReleaseStartedEvent); + } + + if (ctx->useEmulatedCard) + { +#ifdef WITH_SMARTCARD_EMULATE + if (ctx->emulation) + { + Emulate_Free(ctx->emulation); + ctx->emulation = NULL; + } +#endif + } + + if (ctx->hWinSCardLibrary) + { + ZeroMemory(&ctx->WinSCardApi, sizeof(SCardApiFunctionTable)); + FreeLibrary(ctx->hWinSCardLibrary); + ctx->hWinSCardLibrary = NULL; + } + + ctx->pWinSCardApi = NULL; + + HashTable_Free(ctx->rgSCardContextList); + CloseHandle(ctx->stopEvent); + free(ctx); +} + +BOOL smartcard_call_context_add(scard_call_context* ctx, const char* name) +{ + WINPR_ASSERT(ctx); + WINPR_ASSERT(name); + return LinkedList_AddLast(ctx->names, name); +} + +BOOL smartcard_call_cancel_context(scard_call_context* ctx, SCARDCONTEXT hContext) +{ + WINPR_ASSERT(ctx); + if (wrap(ctx, SCardIsValidContext, hContext) == SCARD_S_SUCCESS) + { + wrap(ctx, SCardCancel, hContext); + } + return TRUE; +} + +BOOL smartcard_call_release_context(scard_call_context* ctx, SCARDCONTEXT hContext) +{ + WINPR_ASSERT(ctx); + wrap(ctx, SCardReleaseContext, hContext); + return TRUE; +} + +BOOL smartcard_call_cancel_all_context(scard_call_context* ctx) +{ + WINPR_ASSERT(ctx); + + HashTable_Clear(ctx->rgSCardContextList); + return TRUE; +} + +BOOL smarcard_call_set_callbacks(scard_call_context* ctx, void* userdata, + void* (*fn_new)(void*, SCARDCONTEXT), void (*fn_free)(void*)) +{ + WINPR_ASSERT(ctx); + ctx->userdata = userdata; + ctx->fn_new = fn_new; + ctx->fn_free = fn_free; + return TRUE; +} + +void* smartcard_call_get_context(scard_call_context* ctx, SCARDCONTEXT hContext) +{ + struct s_scard_context_element* element = NULL; + + WINPR_ASSERT(ctx); + element = HashTable_GetItemValue(ctx->rgSCardContextList, (void*)hContext); + if (!element) + return NULL; + return element->context; +} + +BOOL smartcard_call_is_configured(scard_call_context* ctx) +{ + WINPR_ASSERT(ctx); + +#if defined(WITH_SMARTCARD_EMULATE) + if (ctx->useEmulatedCard) + return Emulate_IsConfigured(ctx->emulation); +#endif + + return FALSE; +} + +BOOL smartcard_call_context_signal_stop(scard_call_context* ctx, BOOL reset) +{ + WINPR_ASSERT(ctx); + if (!ctx->stopEvent) + return TRUE; + + if (reset) + return ResetEvent(ctx->stopEvent); + else + return SetEvent(ctx->stopEvent); +} diff --git a/libfreerdp/utils/smartcard_operations.c b/libfreerdp/utils/smartcard_operations.c new file mode 100644 index 0000000..fd7be82 --- /dev/null +++ b/libfreerdp/utils/smartcard_operations.c @@ -0,0 +1,1048 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Smartcard Device Service Virtual Channel + * + * Copyright (C) Alexi Volkov <alexi@myrealbox.com> 2006 + * Copyright 2011 O.S. Systems Software Ltda. + * Copyright 2011 Anthony Tong <atong@trustedcs.com> + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com> + * Copyright 2017 Armin Novak <armin.novak@thincast.com> + * Copyright 2017 Thincast Technologies GmbH + * + * 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. + */ + +#include <freerdp/config.h> + +#include <winpr/assert.h> + +#include <winpr/crt.h> +#include <winpr/print.h> +#include <winpr/stream.h> +#include <winpr/smartcard.h> + +#include <freerdp/freerdp.h> +#include <freerdp/channels/rdpdr.h> +#include <freerdp/channels/scard.h> + +#include <freerdp/utils/rdpdr_utils.h> + +#include <freerdp/utils/smartcard_operations.h> +#include <freerdp/utils/smartcard_pack.h> + +#include <freerdp/log.h> +#define TAG FREERDP_TAG("utils.smartcard.ops") + +static LONG smartcard_call_to_operation_handle(SMARTCARD_OPERATION* operation) +{ + WINPR_ASSERT(operation); + + operation->hContext = + smartcard_scard_context_native_from_redir(&(operation->call.handles.hContext)); + operation->hCard = smartcard_scard_handle_native_from_redir(&(operation->call.handles.hCard)); + + return SCARD_S_SUCCESS; +} + +static LONG smartcard_EstablishContext_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_establish_context_call(s, &operation->call.establishContext); + if (status != SCARD_S_SUCCESS) + { + return scard_log_status_error(TAG, "smartcard_unpack_establish_context_call", status); + } + + return SCARD_S_SUCCESS; +} + +static LONG smartcard_ReleaseContext_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_context_call(s, &operation->call.context, "ReleaseContext"); + if (status != SCARD_S_SUCCESS) + scard_log_status_error(TAG, "smartcard_unpack_context_call", status); + + return status; +} + +static LONG smartcard_IsValidContext_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_context_call(s, &operation->call.context, "IsValidContext"); + + return status; +} + +static LONG smartcard_ListReaderGroupsA_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_list_reader_groups_call(s, &operation->call.listReaderGroups, FALSE); + + return status; +} + +static LONG smartcard_ListReaderGroupsW_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_list_reader_groups_call(s, &operation->call.listReaderGroups, TRUE); + + return status; +} + +static LONG smartcard_ListReadersA_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_list_readers_call(s, &operation->call.listReaders, FALSE); + + return status; +} + +static LONG smartcard_ListReadersW_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_list_readers_call(s, &operation->call.listReaders, TRUE); + + return status; +} + +static LONG smartcard_context_and_two_strings_a_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = + smartcard_unpack_context_and_two_strings_a_call(s, &operation->call.contextAndTwoStringA); + + return status; +} + +static LONG smartcard_context_and_two_strings_w_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = + smartcard_unpack_context_and_two_strings_w_call(s, &operation->call.contextAndTwoStringW); + + return status; +} + +static LONG smartcard_context_and_string_a_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_context_and_string_a_call(s, &operation->call.contextAndStringA); + + return status; +} + +static LONG smartcard_context_and_string_w_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_context_and_string_w_call(s, &operation->call.contextAndStringW); + + return status; +} + +static LONG smartcard_LocateCardsA_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_locate_cards_a_call(s, &operation->call.locateCardsA); + + return status; +} + +static LONG smartcard_LocateCardsW_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_locate_cards_w_call(s, &operation->call.locateCardsW); + + return status; +} + +static LONG smartcard_GetStatusChangeA_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_get_status_change_a_call(s, &operation->call.getStatusChangeA); + + return status; +} + +static LONG smartcard_GetStatusChangeW_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_get_status_change_w_call(s, &operation->call.getStatusChangeW); + + return status; +} + +static LONG smartcard_Cancel_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_context_call(s, &operation->call.context, "Cancel"); + + return status; +} + +static LONG smartcard_ConnectA_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_connect_a_call(s, &operation->call.connectA); + + return status; +} + +static LONG smartcard_ConnectW_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_connect_w_call(s, &operation->call.connectW); + + return status; +} + +static LONG smartcard_Reconnect_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_reconnect_call(s, &operation->call.reconnect); + + return status; +} + +static LONG smartcard_Disconnect_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_hcard_and_disposition_call(s, &operation->call.hCardAndDisposition, + "Disconnect"); + + return status; +} + +static LONG smartcard_BeginTransaction_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_hcard_and_disposition_call(s, &operation->call.hCardAndDisposition, + "BeginTransaction"); + + return status; +} + +static LONG smartcard_EndTransaction_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_hcard_and_disposition_call(s, &operation->call.hCardAndDisposition, + "EndTransaction"); + + return status; +} + +static LONG smartcard_State_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_state_call(s, &operation->call.state); + + return status; +} + +static LONG smartcard_StatusA_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_status_call(s, &operation->call.status, FALSE); + + return status; +} + +static LONG smartcard_StatusW_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_status_call(s, &operation->call.status, TRUE); + + return status; +} + +static LONG smartcard_Transmit_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_transmit_call(s, &operation->call.transmit); + + return status; +} + +static LONG smartcard_Control_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_control_call(s, &operation->call.control); + + return status; +} + +static LONG smartcard_GetAttrib_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_get_attrib_call(s, &operation->call.getAttrib); + + return status; +} + +static LONG smartcard_SetAttrib_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_set_attrib_call(s, &operation->call.setAttrib); + + return status; +} + +static LONG smartcard_AccessStartedEvent_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return SCARD_F_INTERNAL_ERROR; + + Stream_Read_INT32(s, operation->call.lng.LongValue); /* Unused (4 bytes) */ + + return SCARD_S_SUCCESS; +} + +static LONG smartcard_LocateCardsByATRA_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_locate_cards_by_atr_a_call(s, &operation->call.locateCardsByATRA); + + return status; +} + +static LONG smartcard_LocateCardsByATRW_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_locate_cards_by_atr_w_call(s, &operation->call.locateCardsByATRW); + + return status; +} + +static LONG smartcard_ReadCacheA_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_read_cache_a_call(s, &operation->call.readCacheA); + + return status; +} + +static LONG smartcard_ReadCacheW_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_read_cache_w_call(s, &operation->call.readCacheW); + + return status; +} + +static LONG smartcard_WriteCacheA_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_write_cache_a_call(s, &operation->call.writeCacheA); + + return status; +} + +static LONG smartcard_WriteCacheW_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_write_cache_w_call(s, &operation->call.writeCacheW); + + return status; +} + +static LONG smartcard_GetTransmitCount_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_get_transmit_count_call(s, &operation->call.getTransmitCount); + + return status; +} + +static LONG smartcard_ReleaseStartedEvent_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + WINPR_UNUSED(s); + WINPR_UNUSED(operation); + WLog_WARN(TAG, "According to [MS-RDPESC] 3.1.4 Message Processing Events and Sequencing Rules " + "SCARD_IOCTL_RELEASETARTEDEVENT is not supported"); + return SCARD_E_UNSUPPORTED_FEATURE; +} + +static LONG smartcard_GetReaderIcon_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_get_reader_icon_call(s, &operation->call.getReaderIcon); + + return status; +} + +static LONG smartcard_GetDeviceTypeId_Decode(wStream* s, SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + status = smartcard_unpack_get_device_type_id_call(s, &operation->call.getDeviceTypeId); + + return status; +} + +LONG smartcard_irp_device_control_decode(wStream* s, UINT32 CompletionId, UINT32 FileId, + SMARTCARD_OPERATION* operation) +{ + LONG status = 0; + UINT32 offset = 0; + UINT32 ioControlCode = 0; + UINT32 outputBufferLength = 0; + UINT32 inputBufferLength = 0; + + WINPR_ASSERT(s); + WINPR_ASSERT(operation); + + /* Device Control Request */ + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 32)) + return SCARD_F_INTERNAL_ERROR; + + Stream_Read_UINT32(s, outputBufferLength); /* OutputBufferLength (4 bytes) */ + Stream_Read_UINT32(s, inputBufferLength); /* InputBufferLength (4 bytes) */ + Stream_Read_UINT32(s, ioControlCode); /* IoControlCode (4 bytes) */ + Stream_Seek(s, 20); /* Padding (20 bytes) */ + operation->ioControlCode = ioControlCode; + operation->ioControlCodeName = scard_get_ioctl_string(ioControlCode, FALSE); + + if (Stream_Length(s) != (Stream_GetPosition(s) + inputBufferLength)) + { + WLog_WARN(TAG, "InputBufferLength mismatch: Actual: %" PRIuz " Expected: %" PRIuz "", + Stream_Length(s), Stream_GetPosition(s) + inputBufferLength); + return SCARD_F_INTERNAL_ERROR; + } + + WLog_DBG(TAG, "%s (0x%08" PRIX32 ") FileId: %" PRIu32 " CompletionId: %" PRIu32 "", + scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode, FileId, CompletionId); + + if ((ioControlCode != SCARD_IOCTL_ACCESSSTARTEDEVENT) && + (ioControlCode != SCARD_IOCTL_RELEASETARTEDEVENT)) + { + status = smartcard_unpack_common_type_header(s); + if (status != SCARD_S_SUCCESS) + return status; + + status = smartcard_unpack_private_type_header(s); + if (status != SCARD_S_SUCCESS) + return status; + } + + /* Decode */ + switch (ioControlCode) + { + case SCARD_IOCTL_ESTABLISHCONTEXT: + status = smartcard_EstablishContext_Decode(s, operation); + break; + + case SCARD_IOCTL_RELEASECONTEXT: + status = smartcard_ReleaseContext_Decode(s, operation); + break; + + case SCARD_IOCTL_ISVALIDCONTEXT: + status = smartcard_IsValidContext_Decode(s, operation); + break; + + case SCARD_IOCTL_LISTREADERGROUPSA: + status = smartcard_ListReaderGroupsA_Decode(s, operation); + break; + + case SCARD_IOCTL_LISTREADERGROUPSW: + status = smartcard_ListReaderGroupsW_Decode(s, operation); + break; + + case SCARD_IOCTL_LISTREADERSA: + status = smartcard_ListReadersA_Decode(s, operation); + break; + + case SCARD_IOCTL_LISTREADERSW: + status = smartcard_ListReadersW_Decode(s, operation); + break; + + case SCARD_IOCTL_INTRODUCEREADERGROUPA: + status = smartcard_context_and_string_a_Decode(s, operation); + break; + + case SCARD_IOCTL_INTRODUCEREADERGROUPW: + status = smartcard_context_and_string_w_Decode(s, operation); + break; + + case SCARD_IOCTL_FORGETREADERGROUPA: + status = smartcard_context_and_string_a_Decode(s, operation); + break; + + case SCARD_IOCTL_FORGETREADERGROUPW: + status = smartcard_context_and_string_w_Decode(s, operation); + break; + + case SCARD_IOCTL_INTRODUCEREADERA: + status = smartcard_context_and_two_strings_a_Decode(s, operation); + break; + + case SCARD_IOCTL_INTRODUCEREADERW: + status = smartcard_context_and_two_strings_w_Decode(s, operation); + break; + + case SCARD_IOCTL_FORGETREADERA: + status = smartcard_context_and_string_a_Decode(s, operation); + break; + + case SCARD_IOCTL_FORGETREADERW: + status = smartcard_context_and_string_w_Decode(s, operation); + break; + + case SCARD_IOCTL_ADDREADERTOGROUPA: + status = smartcard_context_and_two_strings_a_Decode(s, operation); + break; + + case SCARD_IOCTL_ADDREADERTOGROUPW: + status = smartcard_context_and_two_strings_w_Decode(s, operation); + break; + + case SCARD_IOCTL_REMOVEREADERFROMGROUPA: + status = smartcard_context_and_two_strings_a_Decode(s, operation); + break; + + case SCARD_IOCTL_REMOVEREADERFROMGROUPW: + status = smartcard_context_and_two_strings_w_Decode(s, operation); + break; + + case SCARD_IOCTL_LOCATECARDSA: + status = smartcard_LocateCardsA_Decode(s, operation); + break; + + case SCARD_IOCTL_LOCATECARDSW: + status = smartcard_LocateCardsW_Decode(s, operation); + break; + + case SCARD_IOCTL_GETSTATUSCHANGEA: + status = smartcard_GetStatusChangeA_Decode(s, operation); + break; + + case SCARD_IOCTL_GETSTATUSCHANGEW: + status = smartcard_GetStatusChangeW_Decode(s, operation); + break; + + case SCARD_IOCTL_CANCEL: + status = smartcard_Cancel_Decode(s, operation); + break; + + case SCARD_IOCTL_CONNECTA: + status = smartcard_ConnectA_Decode(s, operation); + break; + + case SCARD_IOCTL_CONNECTW: + status = smartcard_ConnectW_Decode(s, operation); + break; + + case SCARD_IOCTL_RECONNECT: + status = smartcard_Reconnect_Decode(s, operation); + break; + + case SCARD_IOCTL_DISCONNECT: + status = smartcard_Disconnect_Decode(s, operation); + break; + + case SCARD_IOCTL_BEGINTRANSACTION: + status = smartcard_BeginTransaction_Decode(s, operation); + break; + + case SCARD_IOCTL_ENDTRANSACTION: + status = smartcard_EndTransaction_Decode(s, operation); + break; + + case SCARD_IOCTL_STATE: + status = smartcard_State_Decode(s, operation); + break; + + case SCARD_IOCTL_STATUSA: + status = smartcard_StatusA_Decode(s, operation); + break; + + case SCARD_IOCTL_STATUSW: + status = smartcard_StatusW_Decode(s, operation); + break; + + case SCARD_IOCTL_TRANSMIT: + status = smartcard_Transmit_Decode(s, operation); + break; + + case SCARD_IOCTL_CONTROL: + status = smartcard_Control_Decode(s, operation); + break; + + case SCARD_IOCTL_GETATTRIB: + status = smartcard_GetAttrib_Decode(s, operation); + break; + + case SCARD_IOCTL_SETATTRIB: + status = smartcard_SetAttrib_Decode(s, operation); + break; + + case SCARD_IOCTL_ACCESSSTARTEDEVENT: + status = smartcard_AccessStartedEvent_Decode(s, operation); + break; + + case SCARD_IOCTL_LOCATECARDSBYATRA: + status = smartcard_LocateCardsByATRA_Decode(s, operation); + break; + + case SCARD_IOCTL_LOCATECARDSBYATRW: + status = smartcard_LocateCardsByATRW_Decode(s, operation); + break; + + case SCARD_IOCTL_READCACHEA: + status = smartcard_ReadCacheA_Decode(s, operation); + break; + + case SCARD_IOCTL_READCACHEW: + status = smartcard_ReadCacheW_Decode(s, operation); + break; + + case SCARD_IOCTL_WRITECACHEA: + status = smartcard_WriteCacheA_Decode(s, operation); + break; + + case SCARD_IOCTL_WRITECACHEW: + status = smartcard_WriteCacheW_Decode(s, operation); + break; + + case SCARD_IOCTL_GETTRANSMITCOUNT: + status = smartcard_GetTransmitCount_Decode(s, operation); + break; + + case SCARD_IOCTL_RELEASETARTEDEVENT: + status = smartcard_ReleaseStartedEvent_Decode(s, operation); + break; + + case SCARD_IOCTL_GETREADERICON: + status = smartcard_GetReaderIcon_Decode(s, operation); + break; + + case SCARD_IOCTL_GETDEVICETYPEID: + status = smartcard_GetDeviceTypeId_Decode(s, operation); + break; + + default: + status = SCARD_F_INTERNAL_ERROR; + break; + } + + smartcard_call_to_operation_handle(operation); + + if ((ioControlCode != SCARD_IOCTL_ACCESSSTARTEDEVENT) && + (ioControlCode != SCARD_IOCTL_RELEASETARTEDEVENT)) + { + offset = (RDPDR_DEVICE_IO_REQUEST_LENGTH + RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH); + smartcard_unpack_read_size_align(s, Stream_GetPosition(s) - offset, 8); + } + + if (Stream_GetPosition(s) < Stream_Length(s)) + { + SIZE_T difference = 0; + difference = Stream_Length(s) - Stream_GetPosition(s); + WLog_WARN(TAG, + "IRP was not fully parsed %s (%s [0x%08" PRIX32 "]): Actual: %" PRIuz + ", Expected: %" PRIuz ", Difference: %" PRIuz "", + scard_get_ioctl_string(ioControlCode, TRUE), + scard_get_ioctl_string(ioControlCode, FALSE), ioControlCode, + Stream_GetPosition(s), Stream_Length(s), difference); + winpr_HexDump(TAG, WLOG_WARN, Stream_ConstPointer(s), difference); + } + + if (Stream_GetPosition(s) > Stream_Length(s)) + { + SIZE_T difference = 0; + difference = Stream_GetPosition(s) - Stream_Length(s); + WLog_WARN(TAG, + "IRP was parsed beyond its end %s (0x%08" PRIX32 "): Actual: %" PRIuz + ", Expected: %" PRIuz ", Difference: %" PRIuz "", + scard_get_ioctl_string(ioControlCode, TRUE), ioControlCode, Stream_GetPosition(s), + Stream_Length(s), difference); + } + + return status; +} + +static void free_reader_states_a(LPSCARD_READERSTATEA rgReaderStates, UINT32 cReaders) +{ + for (UINT32 x = 0; x < cReaders; x++) + { + SCARD_READERSTATEA* state = &rgReaderStates[x]; + free(state->szReader); + } + + free(rgReaderStates); +} + +static void free_reader_states_w(LPSCARD_READERSTATEW rgReaderStates, UINT32 cReaders) +{ + for (UINT32 x = 0; x < cReaders; x++) + { + SCARD_READERSTATEW* state = &rgReaderStates[x]; + free(state->szReader); + } + + free(rgReaderStates); +} + +void smartcard_operation_free(SMARTCARD_OPERATION* op, BOOL allocated) +{ + if (!op) + return; + switch (op->ioControlCode) + { + case SCARD_IOCTL_CANCEL: + case SCARD_IOCTL_ACCESSSTARTEDEVENT: + case SCARD_IOCTL_RELEASETARTEDEVENT: + case SCARD_IOCTL_LISTREADERGROUPSA: + case SCARD_IOCTL_LISTREADERGROUPSW: + case SCARD_IOCTL_RECONNECT: + case SCARD_IOCTL_DISCONNECT: + case SCARD_IOCTL_BEGINTRANSACTION: + case SCARD_IOCTL_ENDTRANSACTION: + case SCARD_IOCTL_STATE: + case SCARD_IOCTL_STATUSA: + case SCARD_IOCTL_STATUSW: + case SCARD_IOCTL_ESTABLISHCONTEXT: + case SCARD_IOCTL_RELEASECONTEXT: + case SCARD_IOCTL_ISVALIDCONTEXT: + case SCARD_IOCTL_GETATTRIB: + case SCARD_IOCTL_GETTRANSMITCOUNT: + break; + + case SCARD_IOCTL_LOCATECARDSA: + { + LocateCardsA_Call* call = &op->call.locateCardsA; + free(call->mszCards); + + free_reader_states_a(call->rgReaderStates, call->cReaders); + } + break; + case SCARD_IOCTL_LOCATECARDSW: + { + LocateCardsW_Call* call = &op->call.locateCardsW; + free(call->mszCards); + + free_reader_states_w(call->rgReaderStates, call->cReaders); + } + break; + + case SCARD_IOCTL_LOCATECARDSBYATRA: + { + LocateCardsByATRA_Call* call = &op->call.locateCardsByATRA; + + free_reader_states_a(call->rgReaderStates, call->cReaders); + } + break; + case SCARD_IOCTL_LOCATECARDSBYATRW: + { + LocateCardsByATRW_Call* call = &op->call.locateCardsByATRW; + free_reader_states_w(call->rgReaderStates, call->cReaders); + } + break; + case SCARD_IOCTL_FORGETREADERA: + case SCARD_IOCTL_INTRODUCEREADERGROUPA: + case SCARD_IOCTL_FORGETREADERGROUPA: + { + ContextAndStringA_Call* call = &op->call.contextAndStringA; + free(call->sz); + } + break; + + case SCARD_IOCTL_FORGETREADERW: + case SCARD_IOCTL_INTRODUCEREADERGROUPW: + case SCARD_IOCTL_FORGETREADERGROUPW: + { + ContextAndStringW_Call* call = &op->call.contextAndStringW; + free(call->sz); + } + break; + + case SCARD_IOCTL_INTRODUCEREADERA: + case SCARD_IOCTL_REMOVEREADERFROMGROUPA: + case SCARD_IOCTL_ADDREADERTOGROUPA: + + { + ContextAndTwoStringA_Call* call = &op->call.contextAndTwoStringA; + free(call->sz1); + free(call->sz2); + } + break; + + case SCARD_IOCTL_INTRODUCEREADERW: + case SCARD_IOCTL_REMOVEREADERFROMGROUPW: + case SCARD_IOCTL_ADDREADERTOGROUPW: + + { + ContextAndTwoStringW_Call* call = &op->call.contextAndTwoStringW; + free(call->sz1); + free(call->sz2); + } + break; + + case SCARD_IOCTL_LISTREADERSA: + case SCARD_IOCTL_LISTREADERSW: + { + ListReaders_Call* call = &op->call.listReaders; + free(call->mszGroups); + } + break; + case SCARD_IOCTL_GETSTATUSCHANGEA: + { + GetStatusChangeA_Call* call = &op->call.getStatusChangeA; + free_reader_states_a(call->rgReaderStates, call->cReaders); + } + break; + + case SCARD_IOCTL_GETSTATUSCHANGEW: + { + GetStatusChangeW_Call* call = &op->call.getStatusChangeW; + free_reader_states_w(call->rgReaderStates, call->cReaders); + } + break; + case SCARD_IOCTL_GETREADERICON: + { + GetReaderIcon_Call* call = &op->call.getReaderIcon; + free(call->szReaderName); + } + break; + case SCARD_IOCTL_GETDEVICETYPEID: + { + GetDeviceTypeId_Call* call = &op->call.getDeviceTypeId; + free(call->szReaderName); + } + break; + case SCARD_IOCTL_CONNECTA: + { + ConnectA_Call* call = &op->call.connectA; + free(call->szReader); + } + break; + case SCARD_IOCTL_CONNECTW: + { + ConnectW_Call* call = &op->call.connectW; + free(call->szReader); + } + break; + case SCARD_IOCTL_SETATTRIB: + free(op->call.setAttrib.pbAttr); + break; + case SCARD_IOCTL_TRANSMIT: + { + Transmit_Call* call = &op->call.transmit; + free(call->pbSendBuffer); + free(call->pioSendPci); + free(call->pioRecvPci); + } + break; + case SCARD_IOCTL_CONTROL: + { + Control_Call* call = &op->call.control; + free(call->pvInBuffer); + } + break; + case SCARD_IOCTL_READCACHEA: + { + ReadCacheA_Call* call = &op->call.readCacheA; + free(call->szLookupName); + free(call->Common.CardIdentifier); + } + break; + case SCARD_IOCTL_READCACHEW: + { + ReadCacheW_Call* call = &op->call.readCacheW; + free(call->szLookupName); + free(call->Common.CardIdentifier); + } + break; + case SCARD_IOCTL_WRITECACHEA: + { + WriteCacheA_Call* call = &op->call.writeCacheA; + free(call->szLookupName); + free(call->Common.CardIdentifier); + free(call->Common.pbData); + } + break; + case SCARD_IOCTL_WRITECACHEW: + { + WriteCacheW_Call* call = &op->call.writeCacheW; + free(call->szLookupName); + free(call->Common.CardIdentifier); + free(call->Common.pbData); + } + break; + default: + break; + } + + { + SMARTCARD_OPERATION empty = { 0 }; + *op = empty; + } + + if (allocated) + free(op); +} diff --git a/libfreerdp/utils/smartcard_pack.c b/libfreerdp/utils/smartcard_pack.c new file mode 100644 index 0000000..58536ca --- /dev/null +++ b/libfreerdp/utils/smartcard_pack.c @@ -0,0 +1,3649 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Smart Card Structure Packing + * + * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com> + * Copyright 2020 Armin Novak <armin.novak@thincast.com> + * Copyright 2020 Thincast Technologies GmbH + * + * 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. + */ + +#include <freerdp/config.h> + +#include <winpr/crt.h> +#include <winpr/print.h> + +#include <freerdp/channels/scard.h> +#include <freerdp/utils/smartcard_pack.h> + +#include <freerdp/log.h> +#define TAG FREERDP_TAG("scard.pack") + +static const DWORD g_LogLevel = WLOG_DEBUG; + +#define smartcard_unpack_redir_scard_context(s, context, index, ndr) \ + smartcard_unpack_redir_scard_context_((s), (context), (index), (ndr), __FILE__, __func__, \ + __LINE__) +#define smartcard_unpack_redir_scard_handle(s, context, index) \ + smartcard_unpack_redir_scard_handle_((s), (context), (index), __FILE__, __func__, __LINE__) + +static LONG smartcard_unpack_redir_scard_context_(wStream* s, REDIR_SCARDCONTEXT* context, + UINT32* index, UINT32* ppbContextNdrPtr, + const char* file, const char* function, int line); +static LONG smartcard_pack_redir_scard_context(wStream* s, const REDIR_SCARDCONTEXT* context, + DWORD* index); +static LONG smartcard_unpack_redir_scard_handle_(wStream* s, REDIR_SCARDHANDLE* handle, + UINT32* index, const char* file, + const char* function, int line); +static LONG smartcard_pack_redir_scard_handle(wStream* s, const REDIR_SCARDHANDLE* handle, + DWORD* index); +static LONG smartcard_unpack_redir_scard_context_ref(wStream* s, UINT32 pbContextNdrPtr, + REDIR_SCARDCONTEXT* context); +static LONG smartcard_pack_redir_scard_context_ref(wStream* s, const REDIR_SCARDCONTEXT* context); + +static LONG smartcard_unpack_redir_scard_handle_ref(wStream* s, REDIR_SCARDHANDLE* handle); +static LONG smartcard_pack_redir_scard_handle_ref(wStream* s, const REDIR_SCARDHANDLE* handle); + +typedef enum +{ + NDR_PTR_FULL, + NDR_PTR_SIMPLE, + NDR_PTR_FIXED +} ndr_ptr_t; + +/* Reads a NDR pointer and checks if the value read has the expected relative + * addressing */ +#define smartcard_ndr_pointer_read(s, index, ptr) \ + smartcard_ndr_pointer_read_((s), (index), (ptr), __FILE__, __func__, __LINE__) +static BOOL smartcard_ndr_pointer_read_(wStream* s, UINT32* index, UINT32* ptr, const char* file, + const char* fkt, size_t line) +{ + const UINT32 expect = 0x20000 + (*index) * 4; + UINT32 ndrPtr = 0; + WINPR_UNUSED(file); + if (!s) + return FALSE; + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return FALSE; + + Stream_Read_UINT32(s, ndrPtr); /* mszGroupsNdrPtr (4 bytes) */ + if (ptr) + *ptr = ndrPtr; + if (expect != ndrPtr) + { + /* Allow NULL pointer if we read the result */ + if (ptr && (ndrPtr == 0)) + return TRUE; + WLog_WARN(TAG, + "[%s:%" PRIuz "] Read context pointer 0x%08" PRIx32 ", expected 0x%08" PRIx32, + fkt, line, ndrPtr, expect); + return FALSE; + } + + (*index) = (*index) + 1; + return TRUE; +} + +static LONG smartcard_ndr_read(wStream* s, BYTE** data, size_t min, size_t elementSize, + ndr_ptr_t type) +{ + size_t len = 0; + size_t offset = 0; + size_t len2 = 0; + void* r = NULL; + size_t required = 0; + + switch (type) + { + case NDR_PTR_FULL: + required = 12; + break; + case NDR_PTR_SIMPLE: + required = 4; + break; + case NDR_PTR_FIXED: + required = min; + break; + } + + if (!Stream_CheckAndLogRequiredLength(TAG, s, required)) + return STATUS_BUFFER_TOO_SMALL; + + switch (type) + { + case NDR_PTR_FULL: + Stream_Read_UINT32(s, len); + Stream_Read_UINT32(s, offset); + Stream_Read_UINT32(s, len2); + if (len != offset + len2) + { + WLog_ERR(TAG, + "Invalid data when reading full NDR pointer: total=%" PRIu32 + ", offset=%" PRIu32 ", remaining=%" PRIu32, + len, offset, len2); + return STATUS_BUFFER_TOO_SMALL; + } + break; + case NDR_PTR_SIMPLE: + Stream_Read_UINT32(s, len); + + if ((len != min) && (min > 0)) + { + WLog_ERR(TAG, + "Invalid data when reading simple NDR pointer: total=%" PRIu32 + ", expected=%" PRIu32, + len, min); + return STATUS_BUFFER_TOO_SMALL; + } + break; + case NDR_PTR_FIXED: + len = (UINT32)min; + break; + } + + if (min > len) + { + WLog_ERR(TAG, "Invalid length read from NDR pointer, minimum %" PRIu32 ", got %" PRIu32, + min, len); + return STATUS_DATA_ERROR; + } + + if (len > SIZE_MAX / 2) + return STATUS_BUFFER_TOO_SMALL; + + if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, len, elementSize)) + return STATUS_BUFFER_TOO_SMALL; + + len *= elementSize; + + r = calloc(len + 1, sizeof(CHAR)); + if (!r) + return SCARD_E_NO_MEMORY; + Stream_Read(s, r, len); + smartcard_unpack_read_size_align(s, len, 4); + *data = r; + return STATUS_SUCCESS; +} + +static BOOL smartcard_ndr_pointer_write(wStream* s, UINT32* index, DWORD length) +{ + const UINT32 ndrPtr = 0x20000 + (*index) * 4; + + if (!s) + return FALSE; + if (!Stream_EnsureRemainingCapacity(s, 4)) + return FALSE; + + if (length > 0) + { + Stream_Write_UINT32(s, ndrPtr); /* mszGroupsNdrPtr (4 bytes) */ + (*index) = (*index) + 1; + } + else + Stream_Write_UINT32(s, 0); + return TRUE; +} + +static LONG smartcard_ndr_write(wStream* s, const BYTE* data, UINT32 size, UINT32 elementSize, + ndr_ptr_t type) +{ + const UINT32 offset = 0; + const UINT32 len = size; + const UINT32 dataLen = size * elementSize; + size_t required = 0; + + if (size == 0) + return SCARD_S_SUCCESS; + + switch (type) + { + case NDR_PTR_FULL: + required = 12; + break; + case NDR_PTR_SIMPLE: + required = 4; + break; + case NDR_PTR_FIXED: + required = 0; + break; + } + + if (!Stream_EnsureRemainingCapacity(s, required + dataLen + 4)) + return STATUS_BUFFER_TOO_SMALL; + + switch (type) + { + case NDR_PTR_FULL: + Stream_Write_UINT32(s, len); + Stream_Write_UINT32(s, offset); + Stream_Write_UINT32(s, len); + break; + case NDR_PTR_SIMPLE: + Stream_Write_UINT32(s, len); + break; + case NDR_PTR_FIXED: + break; + } + + if (data) + Stream_Write(s, data, dataLen); + else + Stream_Zero(s, dataLen); + return smartcard_pack_write_size_align(s, len, 4); +} + +static LONG smartcard_ndr_write_state(wStream* s, const ReaderState_Return* data, UINT32 size, + ndr_ptr_t type) +{ + union + { + const ReaderState_Return* reader; + const BYTE* data; + } cnv; + + cnv.reader = data; + return smartcard_ndr_write(s, cnv.data, size, sizeof(ReaderState_Return), type); +} + +static LONG smartcard_ndr_read_atrmask(wStream* s, LocateCards_ATRMask** data, size_t min, + ndr_ptr_t type) +{ + union + { + LocateCards_ATRMask** ppc; + BYTE** ppv; + } u; + u.ppc = data; + return smartcard_ndr_read(s, u.ppv, min, sizeof(LocateCards_ATRMask), type); +} + +static LONG smartcard_ndr_read_fixed_string_a(wStream* s, CHAR** data, size_t min, ndr_ptr_t type) +{ + union + { + CHAR** ppc; + BYTE** ppv; + } u; + u.ppc = data; + return smartcard_ndr_read(s, u.ppv, min, sizeof(CHAR), type); +} + +static LONG smartcard_ndr_read_fixed_string_w(wStream* s, WCHAR** data, size_t min, ndr_ptr_t type) +{ + union + { + WCHAR** ppc; + BYTE** ppv; + } u; + u.ppc = data; + return smartcard_ndr_read(s, u.ppv, min, sizeof(WCHAR), type); +} + +static LONG smartcard_ndr_read_a(wStream* s, CHAR** data, ndr_ptr_t type) +{ + union + { + CHAR** ppc; + BYTE** ppv; + } u; + u.ppc = data; + return smartcard_ndr_read(s, u.ppv, 0, sizeof(CHAR), type); +} + +static LONG smartcard_ndr_read_w(wStream* s, WCHAR** data, ndr_ptr_t type) +{ + union + { + WCHAR** ppc; + BYTE** ppv; + } u; + u.ppc = data; + return smartcard_ndr_read(s, u.ppv, 0, sizeof(WCHAR), type); +} + +static LONG smartcard_ndr_read_u(wStream* s, UUID** data) +{ + union + { + UUID** ppc; + BYTE** ppv; + } u; + u.ppc = data; + return smartcard_ndr_read(s, u.ppv, 1, sizeof(UUID), NDR_PTR_FIXED); +} + +static char* smartcard_convert_string_list(const void* in, size_t bytes, BOOL unicode) +{ + size_t length = 0; + union + { + const void* pv; + const char* sz; + const WCHAR* wz; + } string; + char* mszA = NULL; + + string.pv = in; + + if (bytes < 1) + return NULL; + + if (in == NULL) + return NULL; + + if (unicode) + { + mszA = ConvertMszWCharNToUtf8Alloc(string.wz, bytes / sizeof(WCHAR), &length); + if (!mszA) + return NULL; + } + else + { + mszA = (char*)calloc(bytes, sizeof(char)); + if (!mszA) + return NULL; + CopyMemory(mszA, string.sz, bytes - 1); + length = bytes; + } + + if (length < 1) + { + free(mszA); + return NULL; + } + for (size_t index = 0; index < length - 1; index++) + { + if (mszA[index] == '\0') + mszA[index] = ','; + } + + return mszA; +} + +static char* smartcard_msz_dump_a(const char* msz, size_t len, char* buffer, size_t bufferLen) +{ + char* buf = buffer; + const char* cur = msz; + + while ((len > 0) && cur && cur[0] != '\0' && (bufferLen > 0)) + { + size_t clen = strnlen(cur, len); + int rc = _snprintf(buf, bufferLen, "%s", cur); + bufferLen -= (size_t)rc; + buf += rc; + + cur += clen; + } + + return buffer; +} + +static char* smartcard_msz_dump_w(const WCHAR* msz, size_t len, char* buffer, size_t bufferLen) +{ + size_t szlen = 0; + if (!msz) + return NULL; + char* sz = ConvertMszWCharNToUtf8Alloc(msz, len, &szlen); + if (!sz) + return NULL; + + smartcard_msz_dump_a(sz, szlen, buffer, bufferLen); + free(sz); + return buffer; +} + +static char* smartcard_array_dump(const void* pd, size_t len, char* buffer, size_t bufferLen) +{ + const BYTE* data = pd; + int rc = 0; + char* start = buffer; + + /* Ensure '\0' termination */ + if (bufferLen > 0) + { + buffer[bufferLen - 1] = '\0'; + bufferLen--; + } + + rc = _snprintf(buffer, bufferLen, "{ "); + if ((rc < 0) || ((size_t)rc > bufferLen)) + goto fail; + buffer += rc; + bufferLen -= (size_t)rc; + + for (size_t x = 0; x < len; x++) + { + rc = _snprintf(buffer, bufferLen, "%02X", data[x]); + if ((rc < 0) || ((size_t)rc > bufferLen)) + goto fail; + buffer += rc; + bufferLen -= (size_t)rc; + } + + rc = _snprintf(buffer, bufferLen, " }"); + if ((rc < 0) || ((size_t)rc > bufferLen)) + goto fail; + +fail: + return start; +} +static void smartcard_log_redir_handle(const char* tag, const REDIR_SCARDHANDLE* pHandle) +{ + char buffer[128]; + + WLog_LVL(tag, g_LogLevel, " hContext: %s", + smartcard_array_dump(pHandle->pbHandle, pHandle->cbHandle, buffer, sizeof(buffer))); +} + +static void smartcard_log_context(const char* tag, const REDIR_SCARDCONTEXT* phContext) +{ + char buffer[128]; + WLog_DBG( + tag, "hContext: %s", + smartcard_array_dump(phContext->pbContext, phContext->cbContext, buffer, sizeof(buffer))); +} + +static void smartcard_trace_context_and_string_call_a(const char* name, + const REDIR_SCARDCONTEXT* phContext, + const CHAR* sz) +{ + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "%s {", name); + smartcard_log_context(TAG, phContext); + WLog_LVL(TAG, g_LogLevel, " sz=%s", sz); + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_context_and_string_call_w(const char* name, + const REDIR_SCARDCONTEXT* phContext, + const WCHAR* sz) +{ + char tmp[1024] = { 0 }; + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + if (sz) + ConvertWCharToUtf8(sz, tmp, ARRAYSIZE(tmp)); + + WLog_LVL(TAG, g_LogLevel, "%s {", name); + smartcard_log_context(TAG, phContext); + WLog_LVL(TAG, g_LogLevel, " sz=%s", tmp); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_context_call(const Context_Call* call, const char* name) +{ + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "%s_Call {", name); + smartcard_log_context(TAG, &call->handles.hContext); + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_list_reader_groups_call(const ListReaderGroups_Call* call, BOOL unicode) +{ + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "ListReaderGroups%S_Call {", unicode ? "W" : "A"); + smartcard_log_context(TAG, &call->handles.hContext); + + WLog_LVL(TAG, g_LogLevel, "fmszGroupsIsNULL: %" PRId32 " cchGroups: 0x%08" PRIx32, + call->fmszGroupsIsNULL, call->cchGroups); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_get_status_change_w_call(const GetStatusChangeW_Call* call) +{ + char* szEventState = NULL; + char* szCurrentState = NULL; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetStatusChangeW_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + + WLog_LVL(TAG, g_LogLevel, "dwTimeOut: 0x%08" PRIX32 " cReaders: %" PRIu32 "", call->dwTimeOut, + call->cReaders); + + for (UINT32 index = 0; index < call->cReaders; index++) + { + const LPSCARD_READERSTATEW readerState = &call->rgReaderStates[index]; + char szReaderA[1024] = { 0 }; + + ConvertWCharToUtf8(readerState->szReader, szReaderA, ARRAYSIZE(szReaderA)); + + WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: szReader: %s cbAtr: %" PRIu32 "", index, + szReaderA, readerState->cbAtr); + szCurrentState = SCardGetReaderStateString(readerState->dwCurrentState); + szEventState = SCardGetReaderStateString(readerState->dwEventState); + WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwCurrentState: %s (0x%08" PRIX32 ")", index, + szCurrentState, readerState->dwCurrentState); + WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwEventState: %s (0x%08" PRIX32 ")", index, + szEventState, readerState->dwEventState); + free(szCurrentState); + free(szEventState); + } + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_list_reader_groups_return(const ListReaderGroups_Return* ret, + BOOL unicode) +{ + char* mszA = NULL; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + mszA = smartcard_convert_string_list(ret->msz, ret->cBytes, unicode); + + WLog_LVL(TAG, g_LogLevel, "ListReaderGroups%s_Return {", unicode ? "W" : "A"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIx32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + WLog_LVL(TAG, g_LogLevel, " cBytes: %" PRIu32 " msz: %s", ret->cBytes, mszA); + WLog_LVL(TAG, g_LogLevel, "}"); + free(mszA); +} + +static void smartcard_trace_list_readers_call(const ListReaders_Call* call, BOOL unicode) +{ + char* mszGroupsA = NULL; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + mszGroupsA = smartcard_convert_string_list(call->mszGroups, call->cBytes, unicode); + + WLog_LVL(TAG, g_LogLevel, "ListReaders%s_Call {", unicode ? "W" : "A"); + smartcard_log_context(TAG, &call->handles.hContext); + + WLog_LVL(TAG, g_LogLevel, + "cBytes: %" PRIu32 " mszGroups: %s fmszReadersIsNULL: %" PRId32 + " cchReaders: 0x%08" PRIX32 "", + call->cBytes, mszGroupsA, call->fmszReadersIsNULL, call->cchReaders); + WLog_LVL(TAG, g_LogLevel, "}"); + + free(mszGroupsA); +} + +static void smartcard_trace_locate_cards_by_atr_a_call(const LocateCardsByATRA_Call* call) +{ + char* szEventState = NULL; + char* szCurrentState = NULL; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "LocateCardsByATRA_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + + for (UINT32 index = 0; index < call->cReaders; index++) + { + char buffer[1024]; + const LPSCARD_READERSTATEA readerState = &call->rgReaderStates[index]; + + WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: szReader: %s cbAtr: %" PRIu32 "", index, + readerState->szReader, readerState->cbAtr); + szCurrentState = SCardGetReaderStateString(readerState->dwCurrentState); + szEventState = SCardGetReaderStateString(readerState->dwEventState); + WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwCurrentState: %s (0x%08" PRIX32 ")", index, + szCurrentState, readerState->dwCurrentState); + WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwEventState: %s (0x%08" PRIX32 ")", index, + szEventState, readerState->dwEventState); + + WLog_DBG( + TAG, "\t[%" PRIu32 "]: cbAtr: %" PRIu32 " rgbAtr: %s", index, readerState->cbAtr, + smartcard_array_dump(readerState->rgbAtr, readerState->cbAtr, buffer, sizeof(buffer))); + + free(szCurrentState); + free(szEventState); + } + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_locate_cards_a_call(const LocateCardsA_Call* call) +{ + char buffer[8192]; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "LocateCardsA_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + WLog_LVL(TAG, g_LogLevel, " cBytes=%" PRId32, call->cBytes); + WLog_LVL(TAG, g_LogLevel, " mszCards=%s", + smartcard_msz_dump_a(call->mszCards, call->cBytes, buffer, sizeof(buffer))); + WLog_LVL(TAG, g_LogLevel, " cReaders=%" PRId32, call->cReaders); + // WLog_LVL(TAG, g_LogLevel, " cReaders=%" PRId32, call->rgReaderStates); + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_locate_cards_return(const LocateCards_Return* ret) +{ + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "LocateCards_Return {"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + + if (ret->ReturnCode == SCARD_S_SUCCESS) + { + WLog_LVL(TAG, g_LogLevel, " cReaders=%" PRId32, ret->cReaders); + } + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_get_reader_icon_return(const GetReaderIcon_Return* ret) +{ + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetReaderIcon_Return {"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + + if (ret->ReturnCode == SCARD_S_SUCCESS) + { + WLog_LVL(TAG, g_LogLevel, " cbDataLen=%" PRId32, ret->cbDataLen); + } + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_get_transmit_count_return(const GetTransmitCount_Return* ret) +{ + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetTransmitCount_Return {"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + + WLog_LVL(TAG, g_LogLevel, " cTransmitCount=%" PRIu32, ret->cTransmitCount); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_read_cache_return(const ReadCache_Return* ret) +{ + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "ReadCache_Return {"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + + if (ret->ReturnCode == SCARD_S_SUCCESS) + { + char buffer[1024]; + WLog_LVL(TAG, g_LogLevel, " cbDataLen=%" PRId32, ret->cbDataLen); + WLog_LVL(TAG, g_LogLevel, " cbData: %s", + smartcard_array_dump(ret->pbData, ret->cbDataLen, buffer, sizeof(buffer))); + } + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_locate_cards_w_call(const LocateCardsW_Call* call) +{ + char buffer[8192]; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "LocateCardsW_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + WLog_LVL(TAG, g_LogLevel, " cBytes=%" PRId32, call->cBytes); + WLog_LVL(TAG, g_LogLevel, " sz2=%s", + smartcard_msz_dump_w(call->mszCards, call->cBytes, buffer, sizeof(buffer))); + WLog_LVL(TAG, g_LogLevel, " cReaders=%" PRId32, call->cReaders); + // WLog_LVL(TAG, g_LogLevel, " sz2=%s", call->rgReaderStates); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_list_readers_return(const ListReaders_Return* ret, BOOL unicode) +{ + char* mszA = NULL; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "ListReaders%s_Return {", unicode ? "W" : "A"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + + if (ret->ReturnCode != SCARD_S_SUCCESS) + { + WLog_LVL(TAG, g_LogLevel, "}"); + return; + } + + mszA = smartcard_convert_string_list(ret->msz, ret->cBytes, unicode); + + WLog_LVL(TAG, g_LogLevel, " cBytes: %" PRIu32 " msz: %s", ret->cBytes, mszA); + WLog_LVL(TAG, g_LogLevel, "}"); + free(mszA); +} + +static void smartcard_trace_get_status_change_return(const GetStatusChange_Return* ret, + BOOL unicode) +{ + char* szEventState = NULL; + char* szCurrentState = NULL; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetStatusChange%s_Return {", unicode ? "W" : "A"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + WLog_LVL(TAG, g_LogLevel, " cReaders: %" PRIu32 "", ret->cReaders); + + for (UINT32 index = 0; index < ret->cReaders; index++) + { + char buffer[1024]; + const ReaderState_Return* rgReaderState = &(ret->rgReaderStates[index]); + szCurrentState = SCardGetReaderStateString(rgReaderState->dwCurrentState); + szEventState = SCardGetReaderStateString(rgReaderState->dwEventState); + WLog_LVL(TAG, g_LogLevel, " [%" PRIu32 "]: dwCurrentState: %s (0x%08" PRIX32 ")", index, + szCurrentState, rgReaderState->dwCurrentState); + WLog_LVL(TAG, g_LogLevel, " [%" PRIu32 "]: dwEventState: %s (0x%08" PRIX32 ")", index, + szEventState, rgReaderState->dwEventState); + WLog_LVL(TAG, g_LogLevel, " [%" PRIu32 "]: cbAtr: %" PRIu32 " rgbAtr: %s", index, + rgReaderState->cbAtr, + smartcard_array_dump(rgReaderState->rgbAtr, rgReaderState->cbAtr, buffer, + sizeof(buffer))); + free(szCurrentState); + free(szEventState); + } + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_context_and_two_strings_a_call(const ContextAndTwoStringA_Call* call) +{ + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "ContextAndTwoStringW_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + WLog_LVL(TAG, g_LogLevel, " sz1=%s", call->sz1); + WLog_LVL(TAG, g_LogLevel, " sz2=%s", call->sz2); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_context_and_two_strings_w_call(const ContextAndTwoStringW_Call* call) +{ + char sz1[1024] = { 0 }; + char sz2[1024] = { 0 }; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + if (call->sz1) + ConvertWCharToUtf8(call->sz1, sz1, ARRAYSIZE(sz1)); + if (call->sz2) + ConvertWCharToUtf8(call->sz2, sz2, ARRAYSIZE(sz2)); + + WLog_LVL(TAG, g_LogLevel, "ContextAndTwoStringW_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + WLog_LVL(TAG, g_LogLevel, " sz1=%s", sz1); + WLog_LVL(TAG, g_LogLevel, " sz2=%s", sz2); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_get_transmit_count_call(const GetTransmitCount_Call* call) +{ + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetTransmitCount_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + smartcard_log_redir_handle(TAG, &call->handles.hCard); + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_write_cache_a_call(const WriteCacheA_Call* call) +{ + char buffer[1024]; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetTransmitCount_Call {"); + + WLog_LVL(TAG, g_LogLevel, " szLookupName=%s", call->szLookupName); + + smartcard_log_context(TAG, &call->Common.handles.hContext); + WLog_DBG( + TAG, "..CardIdentifier=%s", + smartcard_array_dump(call->Common.CardIdentifier, sizeof(UUID), buffer, sizeof(buffer))); + WLog_LVL(TAG, g_LogLevel, " FreshnessCounter=%" PRIu32, call->Common.FreshnessCounter); + WLog_LVL(TAG, g_LogLevel, " cbDataLen=%" PRIu32, call->Common.cbDataLen); + WLog_DBG( + TAG, " pbData=%s", + smartcard_array_dump(call->Common.pbData, call->Common.cbDataLen, buffer, sizeof(buffer))); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_write_cache_w_call(const WriteCacheW_Call* call) +{ + char tmp[1024] = { 0 }; + char buffer[1024] = { 0 }; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetTransmitCount_Call {"); + + if (call->szLookupName) + ConvertWCharToUtf8(call->szLookupName, tmp, ARRAYSIZE(tmp)); + WLog_LVL(TAG, g_LogLevel, " szLookupName=%s", tmp); + + smartcard_log_context(TAG, &call->Common.handles.hContext); + WLog_DBG( + TAG, "..CardIdentifier=%s", + smartcard_array_dump(call->Common.CardIdentifier, sizeof(UUID), buffer, sizeof(buffer))); + WLog_LVL(TAG, g_LogLevel, " FreshnessCounter=%" PRIu32, call->Common.FreshnessCounter); + WLog_LVL(TAG, g_LogLevel, " cbDataLen=%" PRIu32, call->Common.cbDataLen); + WLog_DBG( + TAG, " pbData=%s", + smartcard_array_dump(call->Common.pbData, call->Common.cbDataLen, buffer, sizeof(buffer))); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_read_cache_a_call(const ReadCacheA_Call* call) +{ + char buffer[1024]; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetTransmitCount_Call {"); + + WLog_LVL(TAG, g_LogLevel, " szLookupName=%s", call->szLookupName); + smartcard_log_context(TAG, &call->Common.handles.hContext); + WLog_DBG( + TAG, "..CardIdentifier=%s", + smartcard_array_dump(call->Common.CardIdentifier, sizeof(UUID), buffer, sizeof(buffer))); + WLog_LVL(TAG, g_LogLevel, " FreshnessCounter=%" PRIu32, call->Common.FreshnessCounter); + WLog_LVL(TAG, g_LogLevel, " fPbDataIsNULL=%" PRId32, call->Common.fPbDataIsNULL); + WLog_LVL(TAG, g_LogLevel, " cbDataLen=%" PRIu32, call->Common.cbDataLen); + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_read_cache_w_call(const ReadCacheW_Call* call) +{ + char tmp[1024] = { 0 }; + char buffer[1024] = { 0 }; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetTransmitCount_Call {"); + if (call->szLookupName) + ConvertWCharToUtf8(call->szLookupName, tmp, ARRAYSIZE(tmp)); + WLog_LVL(TAG, g_LogLevel, " szLookupName=%s", tmp); + + smartcard_log_context(TAG, &call->Common.handles.hContext); + WLog_DBG( + TAG, "..CardIdentifier=%s", + smartcard_array_dump(call->Common.CardIdentifier, sizeof(UUID), buffer, sizeof(buffer))); + WLog_LVL(TAG, g_LogLevel, " FreshnessCounter=%" PRIu32, call->Common.FreshnessCounter); + WLog_LVL(TAG, g_LogLevel, " fPbDataIsNULL=%" PRId32, call->Common.fPbDataIsNULL); + WLog_LVL(TAG, g_LogLevel, " cbDataLen=%" PRIu32, call->Common.cbDataLen); + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_transmit_call(const Transmit_Call* call) +{ + UINT32 cbExtraBytes = 0; + BYTE* pbExtraBytes = NULL; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "Transmit_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + smartcard_log_redir_handle(TAG, &call->handles.hCard); + + if (call->pioSendPci) + { + cbExtraBytes = (UINT32)(call->pioSendPci->cbPciLength - sizeof(SCARD_IO_REQUEST)); + pbExtraBytes = &((BYTE*)call->pioSendPci)[sizeof(SCARD_IO_REQUEST)]; + WLog_LVL(TAG, g_LogLevel, "pioSendPci: dwProtocol: %" PRIu32 " cbExtraBytes: %" PRIu32 "", + call->pioSendPci->dwProtocol, cbExtraBytes); + + if (cbExtraBytes) + { + char buffer[1024]; + WLog_LVL(TAG, g_LogLevel, "pbExtraBytes: %s", + smartcard_array_dump(pbExtraBytes, cbExtraBytes, buffer, sizeof(buffer))); + } + } + else + { + WLog_LVL(TAG, g_LogLevel, "pioSendPci: null"); + } + + WLog_LVL(TAG, g_LogLevel, "cbSendLength: %" PRIu32 "", call->cbSendLength); + + if (call->pbSendBuffer) + { + char buffer[1024]; + WLog_DBG( + TAG, "pbSendBuffer: %s", + smartcard_array_dump(call->pbSendBuffer, call->cbSendLength, buffer, sizeof(buffer))); + } + else + { + WLog_LVL(TAG, g_LogLevel, "pbSendBuffer: null"); + } + + if (call->pioRecvPci) + { + cbExtraBytes = (UINT32)(call->pioRecvPci->cbPciLength - sizeof(SCARD_IO_REQUEST)); + pbExtraBytes = &((BYTE*)call->pioRecvPci)[sizeof(SCARD_IO_REQUEST)]; + WLog_LVL(TAG, g_LogLevel, "pioRecvPci: dwProtocol: %" PRIu32 " cbExtraBytes: %" PRIu32 "", + call->pioRecvPci->dwProtocol, cbExtraBytes); + + if (cbExtraBytes) + { + char buffer[1024]; + WLog_LVL(TAG, g_LogLevel, "pbExtraBytes: %s", + smartcard_array_dump(pbExtraBytes, cbExtraBytes, buffer, sizeof(buffer))); + } + } + else + { + WLog_LVL(TAG, g_LogLevel, "pioRecvPci: null"); + } + + WLog_LVL(TAG, g_LogLevel, "fpbRecvBufferIsNULL: %" PRId32 " cbRecvLength: %" PRIu32 "", + call->fpbRecvBufferIsNULL, call->cbRecvLength); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_locate_cards_by_atr_w_call(const LocateCardsByATRW_Call* call) +{ + char* szEventState = NULL; + char* szCurrentState = NULL; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "LocateCardsByATRW_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + + for (UINT32 index = 0; index < call->cReaders; index++) + { + char buffer[1024] = { 0 }; + char tmp[1024] = { 0 }; + const LPSCARD_READERSTATEW readerState = + (const LPSCARD_READERSTATEW)&call->rgReaderStates[index]; + + if (readerState->szReader) + ConvertWCharToUtf8(readerState->szReader, tmp, ARRAYSIZE(tmp)); + WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: szReader: %s cbAtr: %" PRIu32 "", index, tmp, + readerState->cbAtr); + szCurrentState = SCardGetReaderStateString(readerState->dwCurrentState); + szEventState = SCardGetReaderStateString(readerState->dwEventState); + WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwCurrentState: %s (0x%08" PRIX32 ")", index, + szCurrentState, readerState->dwCurrentState); + WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwEventState: %s (0x%08" PRIX32 ")", index, + szEventState, readerState->dwEventState); + + WLog_DBG( + TAG, "\t[%" PRIu32 "]: cbAtr: %" PRIu32 " rgbAtr: %s", index, readerState->cbAtr, + smartcard_array_dump(readerState->rgbAtr, readerState->cbAtr, buffer, sizeof(buffer))); + + free(szCurrentState); + free(szEventState); + } + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_transmit_return(const Transmit_Return* ret) +{ + UINT32 cbExtraBytes = 0; + BYTE* pbExtraBytes = NULL; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "Transmit_Return {"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + + if (ret->pioRecvPci) + { + cbExtraBytes = (UINT32)(ret->pioRecvPci->cbPciLength - sizeof(SCARD_IO_REQUEST)); + pbExtraBytes = &((BYTE*)ret->pioRecvPci)[sizeof(SCARD_IO_REQUEST)]; + WLog_LVL(TAG, g_LogLevel, " pioRecvPci: dwProtocol: %" PRIu32 " cbExtraBytes: %" PRIu32 "", + ret->pioRecvPci->dwProtocol, cbExtraBytes); + + if (cbExtraBytes) + { + char buffer[1024]; + WLog_LVL(TAG, g_LogLevel, " pbExtraBytes: %s", + smartcard_array_dump(pbExtraBytes, cbExtraBytes, buffer, sizeof(buffer))); + } + } + else + { + WLog_LVL(TAG, g_LogLevel, " pioRecvPci: null"); + } + + WLog_LVL(TAG, g_LogLevel, " cbRecvLength: %" PRIu32 "", ret->cbRecvLength); + + if (ret->pbRecvBuffer) + { + char buffer[1024]; + WLog_DBG( + TAG, " pbRecvBuffer: %s", + smartcard_array_dump(ret->pbRecvBuffer, ret->cbRecvLength, buffer, sizeof(buffer))); + } + else + { + WLog_LVL(TAG, g_LogLevel, " pbRecvBuffer: null"); + } + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_control_return(const Control_Return* ret) +{ + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "Control_Return {"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + WLog_LVL(TAG, g_LogLevel, " cbOutBufferSize: %" PRIu32 "", ret->cbOutBufferSize); + + if (ret->pvOutBuffer) + { + char buffer[1024]; + WLog_DBG( + TAG, "pvOutBuffer: %s", + smartcard_array_dump(ret->pvOutBuffer, ret->cbOutBufferSize, buffer, sizeof(buffer))); + } + else + { + WLog_LVL(TAG, g_LogLevel, "pvOutBuffer: null"); + } + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_control_call(const Control_Call* call) +{ + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "Control_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + smartcard_log_redir_handle(TAG, &call->handles.hCard); + + WLog_LVL(TAG, g_LogLevel, + "dwControlCode: 0x%08" PRIX32 " cbInBufferSize: %" PRIu32 + " fpvOutBufferIsNULL: %" PRId32 " cbOutBufferSize: %" PRIu32 "", + call->dwControlCode, call->cbInBufferSize, call->fpvOutBufferIsNULL, + call->cbOutBufferSize); + + if (call->pvInBuffer) + { + char buffer[1024]; + WLog_DBG( + TAG, "pbInBuffer: %s", + smartcard_array_dump(call->pvInBuffer, call->cbInBufferSize, buffer, sizeof(buffer))); + } + else + { + WLog_LVL(TAG, g_LogLevel, "pvInBuffer: null"); + } + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_set_attrib_call(const SetAttrib_Call* call) +{ + char buffer[8192]; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetAttrib_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + smartcard_log_redir_handle(TAG, &call->handles.hCard); + WLog_LVL(TAG, g_LogLevel, "dwAttrId: 0x%08" PRIX32, call->dwAttrId); + WLog_LVL(TAG, g_LogLevel, "cbAttrLen: 0x%08" PRId32, call->cbAttrLen); + WLog_LVL(TAG, g_LogLevel, "pbAttr: %s", + smartcard_array_dump(call->pbAttr, call->cbAttrLen, buffer, sizeof(buffer))); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_get_attrib_return(const GetAttrib_Return* ret, DWORD dwAttrId) +{ + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetAttrib_Return {"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + WLog_LVL(TAG, g_LogLevel, " dwAttrId: %s (0x%08" PRIX32 ") cbAttrLen: 0x%08" PRIX32 "", + SCardGetAttributeString(dwAttrId), dwAttrId, ret->cbAttrLen); + + if (dwAttrId == SCARD_ATTR_VENDOR_NAME) + { + WLog_LVL(TAG, g_LogLevel, " pbAttr: %.*s", ret->cbAttrLen, (char*)ret->pbAttr); + } + else if (dwAttrId == SCARD_ATTR_CURRENT_PROTOCOL_TYPE) + { + union + { + BYTE* pb; + DWORD* pd; + } attr; + attr.pb = ret->pbAttr; + WLog_LVL(TAG, g_LogLevel, " dwProtocolType: %s (0x%08" PRIX32 ")", + SCardGetProtocolString(*attr.pd), *attr.pd); + } + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_get_attrib_call(const GetAttrib_Call* call) +{ + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetAttrib_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + smartcard_log_redir_handle(TAG, &call->handles.hCard); + + WLog_LVL(TAG, g_LogLevel, + "dwAttrId: %s (0x%08" PRIX32 ") fpbAttrIsNULL: %" PRId32 " cbAttrLen: 0x%08" PRIX32 "", + SCardGetAttributeString(call->dwAttrId), call->dwAttrId, call->fpbAttrIsNULL, + call->cbAttrLen); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_status_call(const Status_Call* call, BOOL unicode) +{ + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "Status%s_Call {", unicode ? "W" : "A"); + smartcard_log_context(TAG, &call->handles.hContext); + smartcard_log_redir_handle(TAG, &call->handles.hCard); + + WLog_LVL(TAG, g_LogLevel, + "fmszReaderNamesIsNULL: %" PRId32 " cchReaderLen: %" PRIu32 " cbAtrLen: %" PRIu32 "", + call->fmszReaderNamesIsNULL, call->cchReaderLen, call->cbAtrLen); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_status_return(const Status_Return* ret, BOOL unicode) +{ + char* mszReaderNamesA = NULL; + char buffer[1024]; + DWORD cBytes = 0; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + cBytes = ret->cBytes; + if (ret->ReturnCode != SCARD_S_SUCCESS) + cBytes = 0; + if (cBytes == SCARD_AUTOALLOCATE) + cBytes = 0; + mszReaderNamesA = smartcard_convert_string_list(ret->mszReaderNames, cBytes, unicode); + + WLog_LVL(TAG, g_LogLevel, "Status%s_Return {", unicode ? "W" : "A"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + WLog_LVL(TAG, g_LogLevel, " dwState: %s (0x%08" PRIX32 ") dwProtocol: %s (0x%08" PRIX32 ")", + SCardGetCardStateString(ret->dwState), ret->dwState, + SCardGetProtocolString(ret->dwProtocol), ret->dwProtocol); + + WLog_LVL(TAG, g_LogLevel, " cBytes: %" PRIu32 " mszReaderNames: %s", ret->cBytes, + mszReaderNamesA); + + WLog_LVL(TAG, g_LogLevel, " cbAtrLen: %" PRIu32 " pbAtr: %s", ret->cbAtrLen, + smartcard_array_dump(ret->pbAtr, ret->cbAtrLen, buffer, sizeof(buffer))); + WLog_LVL(TAG, g_LogLevel, "}"); + free(mszReaderNamesA); +} + +static void smartcard_trace_state_return(const State_Return* ret) +{ + char buffer[1024]; + char* state = NULL; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + state = SCardGetReaderStateString(ret->dwState); + WLog_LVL(TAG, g_LogLevel, "Reconnect_Return {"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + WLog_LVL(TAG, g_LogLevel, " dwState: %s (0x%08" PRIX32 ")", state, ret->dwState); + WLog_LVL(TAG, g_LogLevel, " dwProtocol: %s (0x%08" PRIX32 ")", + SCardGetProtocolString(ret->dwProtocol), ret->dwProtocol); + WLog_LVL(TAG, g_LogLevel, " cbAtrLen: (0x%08" PRIX32 ")", ret->cbAtrLen); + WLog_LVL(TAG, g_LogLevel, " rgAtr: %s", + smartcard_array_dump(ret->rgAtr, sizeof(ret->rgAtr), buffer, sizeof(buffer))); + WLog_LVL(TAG, g_LogLevel, "}"); + free(state); +} + +static void smartcard_trace_reconnect_return(const Reconnect_Return* ret) +{ + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "Reconnect_Return {"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + WLog_LVL(TAG, g_LogLevel, " dwActiveProtocol: %s (0x%08" PRIX32 ")", + SCardGetProtocolString(ret->dwActiveProtocol), ret->dwActiveProtocol); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_connect_a_call(const ConnectA_Call* call) +{ + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "ConnectA_Call {"); + smartcard_log_context(TAG, &call->Common.handles.hContext); + + WLog_LVL(TAG, g_LogLevel, + "szReader: %s dwShareMode: %s (0x%08" PRIX32 ") dwPreferredProtocols: %s (0x%08" PRIX32 + ")", + call->szReader, SCardGetShareModeString(call->Common.dwShareMode), + call->Common.dwShareMode, SCardGetProtocolString(call->Common.dwPreferredProtocols), + call->Common.dwPreferredProtocols); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_connect_w_call(const ConnectW_Call* call) +{ + char szReaderA[1024] = { 0 }; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + if (call->szReader) + ConvertWCharToUtf8(call->szReader, szReaderA, ARRAYSIZE(szReaderA)); + WLog_LVL(TAG, g_LogLevel, "ConnectW_Call {"); + smartcard_log_context(TAG, &call->Common.handles.hContext); + + WLog_LVL(TAG, g_LogLevel, + "szReader: %s dwShareMode: %s (0x%08" PRIX32 ") dwPreferredProtocols: %s (0x%08" PRIX32 + ")", + szReaderA, SCardGetShareModeString(call->Common.dwShareMode), call->Common.dwShareMode, + SCardGetProtocolString(call->Common.dwPreferredProtocols), + call->Common.dwPreferredProtocols); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_hcard_and_disposition_call(const HCardAndDisposition_Call* call, + const char* name) +{ + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "%s_Call {", name); + smartcard_log_context(TAG, &call->handles.hContext); + smartcard_log_redir_handle(TAG, &call->handles.hCard); + + WLog_LVL(TAG, g_LogLevel, "dwDisposition: %s (0x%08" PRIX32 ")", + SCardGetDispositionString(call->dwDisposition), call->dwDisposition); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_establish_context_call(const EstablishContext_Call* call) +{ + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "EstablishContext_Call {"); + WLog_LVL(TAG, g_LogLevel, "dwScope: %s (0x%08" PRIX32 ")", SCardGetScopeString(call->dwScope), + call->dwScope); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_establish_context_return(const EstablishContext_Return* ret) +{ + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "EstablishContext_Return {"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + smartcard_log_context(TAG, &ret->hContext); + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +void smartcard_trace_long_return(const Long_Return* ret, const char* name) +{ + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "%s_Return {", name); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_connect_return(const Connect_Return* ret) +{ + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "Connect_Return {"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + smartcard_log_context(TAG, &ret->hContext); + smartcard_log_redir_handle(TAG, &ret->hCard); + + WLog_LVL(TAG, g_LogLevel, " dwActiveProtocol: %s (0x%08" PRIX32 ")", + SCardGetProtocolString(ret->dwActiveProtocol), ret->dwActiveProtocol); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_reconnect_call(const Reconnect_Call* call) +{ + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "Reconnect_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + smartcard_log_redir_handle(TAG, &call->handles.hCard); + + WLog_LVL(TAG, g_LogLevel, + "dwShareMode: %s (0x%08" PRIX32 ") dwPreferredProtocols: %s (0x%08" PRIX32 + ") dwInitialization: %s (0x%08" PRIX32 ")", + SCardGetShareModeString(call->dwShareMode), call->dwShareMode, + SCardGetProtocolString(call->dwPreferredProtocols), call->dwPreferredProtocols, + SCardGetDispositionString(call->dwInitialization), call->dwInitialization); + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static void smartcard_trace_device_type_id_return(const GetDeviceTypeId_Return* ret) +{ + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetDeviceTypeId_Return {"); + WLog_LVL(TAG, g_LogLevel, " ReturnCode: %s (0x%08" PRIX32 ")", + SCardGetErrorString(ret->ReturnCode), ret->ReturnCode); + WLog_LVL(TAG, g_LogLevel, " dwDeviceId=%08" PRIx32, ret->dwDeviceId); + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static LONG smartcard_unpack_common_context_and_string_a(wStream* s, REDIR_SCARDCONTEXT* phContext, + CHAR** pszReaderName) +{ + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + LONG status = smartcard_unpack_redir_scard_context(s, phContext, &index, &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!smartcard_ndr_pointer_read(s, &index, NULL)) + return ERROR_INVALID_DATA; + + status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, phContext); + if (status != SCARD_S_SUCCESS) + return status; + + status = smartcard_ndr_read_a(s, pszReaderName, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + return status; + + smartcard_trace_context_and_string_call_a(__func__, phContext, *pszReaderName); + return SCARD_S_SUCCESS; +} + +static LONG smartcard_unpack_common_context_and_string_w(wStream* s, REDIR_SCARDCONTEXT* phContext, + WCHAR** pszReaderName) +{ + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + LONG status = smartcard_unpack_redir_scard_context(s, phContext, &index, &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!smartcard_ndr_pointer_read(s, &index, NULL)) + return ERROR_INVALID_DATA; + + status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, phContext); + if (status != SCARD_S_SUCCESS) + return status; + + status = smartcard_ndr_read_w(s, pszReaderName, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + return status; + + smartcard_trace_context_and_string_call_w(__func__, phContext, *pszReaderName); + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_common_type_header(wStream* s) +{ + UINT8 version = 0; + UINT32 filler = 0; + UINT8 endianness = 0; + UINT16 commonHeaderLength = 0; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return STATUS_BUFFER_TOO_SMALL; + + /* Process CommonTypeHeader */ + Stream_Read_UINT8(s, version); /* Version (1 byte) */ + Stream_Read_UINT8(s, endianness); /* Endianness (1 byte) */ + Stream_Read_UINT16(s, commonHeaderLength); /* CommonHeaderLength (2 bytes) */ + Stream_Read_UINT32(s, filler); /* Filler (4 bytes), should be 0xCCCCCCCC */ + + if (version != 1) + { + WLog_WARN(TAG, "Unsupported CommonTypeHeader Version %" PRIu8 "", version); + return STATUS_INVALID_PARAMETER; + } + + if (endianness != 0x10) + { + WLog_WARN(TAG, "Unsupported CommonTypeHeader Endianness %" PRIu8 "", endianness); + return STATUS_INVALID_PARAMETER; + } + + if (commonHeaderLength != 8) + { + WLog_WARN(TAG, "Unsupported CommonTypeHeader CommonHeaderLength %" PRIu16 "", + commonHeaderLength); + return STATUS_INVALID_PARAMETER; + } + + if (filler != 0xCCCCCCCC) + { + WLog_WARN(TAG, "Unexpected CommonTypeHeader Filler 0x%08" PRIX32 "", filler); + return STATUS_INVALID_PARAMETER; + } + + return SCARD_S_SUCCESS; +} + +void smartcard_pack_common_type_header(wStream* s) +{ + Stream_Write_UINT8(s, 1); /* Version (1 byte) */ + Stream_Write_UINT8(s, 0x10); /* Endianness (1 byte) */ + Stream_Write_UINT16(s, 8); /* CommonHeaderLength (2 bytes) */ + Stream_Write_UINT32(s, 0xCCCCCCCC); /* Filler (4 bytes), should be 0xCCCCCCCC */ +} + +LONG smartcard_unpack_private_type_header(wStream* s) +{ + UINT32 filler = 0; + UINT32 objectBufferLength = 0; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, objectBufferLength); /* ObjectBufferLength (4 bytes) */ + Stream_Read_UINT32(s, filler); /* Filler (4 bytes), should be 0x00000000 */ + + if (filler != 0x00000000) + { + WLog_WARN(TAG, "Unexpected PrivateTypeHeader Filler 0x%08" PRIX32 "", filler); + return STATUS_INVALID_PARAMETER; + } + + if (!Stream_CheckAndLogRequiredLength(TAG, s, objectBufferLength)) + return STATUS_INVALID_PARAMETER; + + return SCARD_S_SUCCESS; +} + +void smartcard_pack_private_type_header(wStream* s, UINT32 objectBufferLength) +{ + Stream_Write_UINT32(s, objectBufferLength); /* ObjectBufferLength (4 bytes) */ + Stream_Write_UINT32(s, 0x00000000); /* Filler (4 bytes), should be 0x00000000 */ +} + +LONG smartcard_unpack_read_size_align(wStream* s, size_t size, UINT32 alignment) +{ + size_t pad = 0; + + pad = size; + size = (size + alignment - 1) & ~(alignment - 1); + pad = size - pad; + + if (pad) + Stream_Seek(s, pad); + + return (LONG)pad; +} + +LONG smartcard_pack_write_size_align(wStream* s, size_t size, UINT32 alignment) +{ + size_t pad = 0; + + pad = size; + size = (size + alignment - 1) & ~(alignment - 1); + pad = size - pad; + + if (pad) + { + if (!Stream_EnsureRemainingCapacity(s, pad)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + return SCARD_F_INTERNAL_ERROR; + } + + Stream_Zero(s, pad); + } + + return SCARD_S_SUCCESS; +} + +SCARDCONTEXT smartcard_scard_context_native_from_redir(REDIR_SCARDCONTEXT* context) +{ + SCARDCONTEXT hContext = { 0 }; + + if ((context->cbContext != sizeof(ULONG_PTR)) && (context->cbContext != 0)) + { + WLog_WARN(TAG, + "REDIR_SCARDCONTEXT does not match native size: Actual: %" PRIu32 + ", Expected: %" PRIuz "", + context->cbContext, sizeof(ULONG_PTR)); + return 0; + } + + if (context->cbContext) + CopyMemory(&hContext, &(context->pbContext), context->cbContext); + + return hContext; +} + +void smartcard_scard_context_native_to_redir(REDIR_SCARDCONTEXT* context, SCARDCONTEXT hContext) +{ + WINPR_ASSERT(context); + ZeroMemory(context, sizeof(REDIR_SCARDCONTEXT)); + context->cbContext = sizeof(ULONG_PTR); + CopyMemory(&(context->pbContext), &hContext, context->cbContext); +} + +SCARDHANDLE smartcard_scard_handle_native_from_redir(REDIR_SCARDHANDLE* handle) +{ + SCARDHANDLE hCard = 0; + + if (handle->cbHandle == 0) + return hCard; + + if (handle->cbHandle != sizeof(ULONG_PTR)) + { + WLog_WARN(TAG, + "REDIR_SCARDHANDLE does not match native size: Actual: %" PRIu32 + ", Expected: %" PRIuz "", + handle->cbHandle, sizeof(ULONG_PTR)); + return 0; + } + + if (handle->cbHandle) + CopyMemory(&hCard, &(handle->pbHandle), handle->cbHandle); + + return hCard; +} + +void smartcard_scard_handle_native_to_redir(REDIR_SCARDHANDLE* handle, SCARDHANDLE hCard) +{ + WINPR_ASSERT(handle); + ZeroMemory(handle, sizeof(REDIR_SCARDHANDLE)); + handle->cbHandle = sizeof(ULONG_PTR); + CopyMemory(&(handle->pbHandle), &hCard, handle->cbHandle); +} + +LONG smartcard_unpack_redir_scard_context_(wStream* s, REDIR_SCARDCONTEXT* context, UINT32* index, + UINT32* ppbContextNdrPtr, const char* file, + const char* function, int line) +{ + UINT32 pbContextNdrPtr = 0; + + WINPR_UNUSED(file); + WINPR_ASSERT(context); + + ZeroMemory(context, sizeof(REDIR_SCARDCONTEXT)); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, context->cbContext); /* cbContext (4 bytes) */ + + if (!Stream_CheckAndLogRequiredLength(TAG, s, context->cbContext)) + return STATUS_BUFFER_TOO_SMALL; + + if ((context->cbContext != 0) && (context->cbContext != 4) && (context->cbContext != 8)) + { + WLog_WARN(TAG, "REDIR_SCARDCONTEXT length is not 0, 4 or 8: %" PRIu32 "", + context->cbContext); + return STATUS_INVALID_PARAMETER; + } + + if (!smartcard_ndr_pointer_read_(s, index, &pbContextNdrPtr, file, function, line)) + return ERROR_INVALID_DATA; + + if (((context->cbContext == 0) && pbContextNdrPtr) || + ((context->cbContext != 0) && !pbContextNdrPtr)) + { + WLog_WARN(TAG, + "REDIR_SCARDCONTEXT cbContext (%" PRIu32 ") pbContextNdrPtr (%" PRIu32 + ") inconsistency", + context->cbContext, pbContextNdrPtr); + return STATUS_INVALID_PARAMETER; + } + + if (!Stream_CheckAndLogRequiredLength(TAG, s, context->cbContext)) + return STATUS_INVALID_PARAMETER; + + *ppbContextNdrPtr = pbContextNdrPtr; + return SCARD_S_SUCCESS; +} + +LONG smartcard_pack_redir_scard_context(wStream* s, const REDIR_SCARDCONTEXT* context, DWORD* index) +{ + const UINT32 pbContextNdrPtr = 0x00020000 + *index * 4; + + if (context->cbContext != 0) + { + Stream_Write_UINT32(s, context->cbContext); /* cbContext (4 bytes) */ + Stream_Write_UINT32(s, pbContextNdrPtr); /* pbContextNdrPtr (4 bytes) */ + *index = *index + 1; + } + else + Stream_Zero(s, 8); + + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_redir_scard_context_ref(wStream* s, UINT32 pbContextNdrPtr, + REDIR_SCARDCONTEXT* context) +{ + UINT32 length = 0; + + WINPR_ASSERT(context); + if (context->cbContext == 0) + return SCARD_S_SUCCESS; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + + if (length != context->cbContext) + { + WLog_WARN(TAG, "REDIR_SCARDCONTEXT length (%" PRIu32 ") cbContext (%" PRIu32 ") mismatch", + length, context->cbContext); + return STATUS_INVALID_PARAMETER; + } + + if ((context->cbContext != 0) && (context->cbContext != 4) && (context->cbContext != 8)) + { + WLog_WARN(TAG, "REDIR_SCARDCONTEXT length is not 4 or 8: %" PRIu32 "", context->cbContext); + return STATUS_INVALID_PARAMETER; + } + + if (!Stream_CheckAndLogRequiredLength(TAG, s, context->cbContext)) + return STATUS_BUFFER_TOO_SMALL; + + if (context->cbContext) + Stream_Read(s, &(context->pbContext), context->cbContext); + else + ZeroMemory(&(context->pbContext), sizeof(context->pbContext)); + + return SCARD_S_SUCCESS; +} + +LONG smartcard_pack_redir_scard_context_ref(wStream* s, const REDIR_SCARDCONTEXT* context) +{ + + Stream_Write_UINT32(s, context->cbContext); /* Length (4 bytes) */ + + if (context->cbContext) + { + Stream_Write(s, &(context->pbContext), context->cbContext); + } + + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_redir_scard_handle_(wStream* s, REDIR_SCARDHANDLE* handle, UINT32* index, + const char* file, const char* function, int line) +{ + WINPR_ASSERT(handle); + ZeroMemory(handle, sizeof(REDIR_SCARDHANDLE)); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, handle->cbHandle); /* Length (4 bytes) */ + + if (!Stream_CheckAndLogRequiredLength(TAG, s, handle->cbHandle)) + return STATUS_BUFFER_TOO_SMALL; + + if (!smartcard_ndr_pointer_read_(s, index, NULL, file, function, line)) + return ERROR_INVALID_DATA; + + return SCARD_S_SUCCESS; +} + +LONG smartcard_pack_redir_scard_handle(wStream* s, const REDIR_SCARDHANDLE* handle, DWORD* index) +{ + const UINT32 pbContextNdrPtr = 0x00020000 + *index * 4; + + if (handle->cbHandle != 0) + { + Stream_Write_UINT32(s, handle->cbHandle); /* cbContext (4 bytes) */ + Stream_Write_UINT32(s, pbContextNdrPtr); /* pbContextNdrPtr (4 bytes) */ + *index = *index + 1; + } + else + Stream_Zero(s, 8); + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_redir_scard_handle_ref(wStream* s, REDIR_SCARDHANDLE* handle) +{ + UINT32 length = 0; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + + if (length != handle->cbHandle) + { + WLog_WARN(TAG, "REDIR_SCARDHANDLE length (%" PRIu32 ") cbHandle (%" PRIu32 ") mismatch", + length, handle->cbHandle); + return STATUS_INVALID_PARAMETER; + } + + if ((handle->cbHandle != 4) && (handle->cbHandle != 8)) + { + WLog_WARN(TAG, "REDIR_SCARDHANDLE length is not 4 or 8: %" PRIu32 "", handle->cbHandle); + return STATUS_INVALID_PARAMETER; + } + + if (!Stream_CheckAndLogRequiredLength(TAG, s, handle->cbHandle)) + return STATUS_BUFFER_TOO_SMALL; + + if (handle->cbHandle) + Stream_Read(s, &(handle->pbHandle), handle->cbHandle); + + return SCARD_S_SUCCESS; +} + +LONG smartcard_pack_redir_scard_handle_ref(wStream* s, const REDIR_SCARDHANDLE* handle) +{ + + Stream_Write_UINT32(s, handle->cbHandle); /* Length (4 bytes) */ + + if (handle->cbHandle) + Stream_Write(s, &(handle->pbHandle), handle->cbHandle); + + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_establish_context_call(wStream* s, EstablishContext_Call* call) +{ + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, call->dwScope); /* dwScope (4 bytes) */ + smartcard_trace_establish_context_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_pack_establish_context_return(wStream* s, const EstablishContext_Return* ret) +{ + LONG status = 0; + DWORD index = 0; + + smartcard_trace_establish_context_return(ret); + if (ret->ReturnCode != SCARD_S_SUCCESS) + return ret->ReturnCode; + + if ((status = smartcard_pack_redir_scard_context(s, &(ret->hContext), &index))) + return status; + + return smartcard_pack_redir_scard_context_ref(s, &(ret->hContext)); +} + +LONG smartcard_unpack_context_call(wStream* s, Context_Call* call, const char* name) +{ + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + WLog_ERR(TAG, "smartcard_unpack_redir_scard_context_ref failed with error %" PRId32 "", + status); + + smartcard_trace_context_call(call, name); + return status; +} + +LONG smartcard_unpack_list_reader_groups_call(wStream* s, ListReaderGroups_Call* call, BOOL unicode) +{ + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_INT32(s, call->fmszGroupsIsNULL); /* fmszGroupsIsNULL (4 bytes) */ + Stream_Read_UINT32(s, call->cchGroups); /* cchGroups (4 bytes) */ + status = + smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, &(call->handles.hContext)); + + if (status != SCARD_S_SUCCESS) + return status; + + smartcard_trace_list_reader_groups_call(call, unicode); + return SCARD_S_SUCCESS; +} + +LONG smartcard_pack_list_reader_groups_return(wStream* s, const ListReaderGroups_Return* ret, + BOOL unicode) +{ + LONG status = 0; + DWORD cBytes = ret->cBytes; + UINT32 index = 0; + + smartcard_trace_list_reader_groups_return(ret, unicode); + if (ret->ReturnCode != SCARD_S_SUCCESS) + cBytes = 0; + if (cBytes == SCARD_AUTOALLOCATE) + cBytes = 0; + + if (!Stream_EnsureRemainingCapacity(s, 4)) + return SCARD_E_NO_MEMORY; + + Stream_Write_UINT32(s, cBytes); /* cBytes (4 bytes) */ + if (!smartcard_ndr_pointer_write(s, &index, cBytes)) + return SCARD_E_NO_MEMORY; + + status = smartcard_ndr_write(s, ret->msz, cBytes, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + return ret->ReturnCode; +} + +LONG smartcard_unpack_list_readers_call(wStream* s, ListReaders_Call* call, BOOL unicode) +{ + UINT32 index = 0; + UINT32 mszGroupsNdrPtr = 0; + UINT32 pbContextNdrPtr = 0; + + call->mszGroups = NULL; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 16)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, call->cBytes); /* cBytes (4 bytes) */ + if (!smartcard_ndr_pointer_read(s, &index, &mszGroupsNdrPtr)) + return ERROR_INVALID_DATA; + Stream_Read_INT32(s, call->fmszReadersIsNULL); /* fmszReadersIsNULL (4 bytes) */ + Stream_Read_UINT32(s, call->cchReaders); /* cchReaders (4 bytes) */ + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if (mszGroupsNdrPtr) + { + status = smartcard_ndr_read(s, &call->mszGroups, call->cBytes, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + } + + smartcard_trace_list_readers_call(call, unicode); + return SCARD_S_SUCCESS; +} + +LONG smartcard_pack_list_readers_return(wStream* s, const ListReaders_Return* ret, BOOL unicode) +{ + LONG status = 0; + UINT32 index = 0; + UINT32 size = ret->cBytes; + + smartcard_trace_list_readers_return(ret, unicode); + if (ret->ReturnCode != SCARD_S_SUCCESS) + size = 0; + + if (!Stream_EnsureRemainingCapacity(s, 4)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + return SCARD_F_INTERNAL_ERROR; + } + + Stream_Write_UINT32(s, size); /* cBytes (4 bytes) */ + if (!smartcard_ndr_pointer_write(s, &index, size)) + return SCARD_E_NO_MEMORY; + + status = smartcard_ndr_write(s, ret->msz, size, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + return ret->ReturnCode; +} + +static LONG smartcard_unpack_connect_common(wStream* s, Connect_Common_Call* common, UINT32* index, + UINT32* ppbContextNdrPtr) +{ + LONG status = smartcard_unpack_redir_scard_context(s, &(common->handles.hContext), index, + ppbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, common->dwShareMode); /* dwShareMode (4 bytes) */ + Stream_Read_UINT32(s, common->dwPreferredProtocols); /* dwPreferredProtocols (4 bytes) */ + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_connect_a_call(wStream* s, ConnectA_Call* call) +{ + LONG status = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + call->szReader = NULL; + + if (!smartcard_ndr_pointer_read(s, &index, NULL)) + return ERROR_INVALID_DATA; + + if ((status = smartcard_unpack_connect_common(s, &(call->Common), &index, &pbContextNdrPtr))) + { + WLog_ERR(TAG, "smartcard_unpack_connect_common failed with error %" PRId32 "", status); + return status; + } + + status = smartcard_ndr_read_a(s, &call->szReader, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + return status; + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->Common.handles.hContext)))) + WLog_ERR(TAG, "smartcard_unpack_redir_scard_context_ref failed with error %" PRId32 "", + status); + + smartcard_trace_connect_a_call(call); + return status; +} + +LONG smartcard_unpack_connect_w_call(wStream* s, ConnectW_Call* call) +{ + LONG status = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + call->szReader = NULL; + + if (!smartcard_ndr_pointer_read(s, &index, NULL)) + return ERROR_INVALID_DATA; + + if ((status = smartcard_unpack_connect_common(s, &(call->Common), &index, &pbContextNdrPtr))) + { + WLog_ERR(TAG, "smartcard_unpack_connect_common failed with error %" PRId32 "", status); + return status; + } + + status = smartcard_ndr_read_w(s, &call->szReader, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + return status; + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->Common.handles.hContext)))) + WLog_ERR(TAG, "smartcard_unpack_redir_scard_context_ref failed with error %" PRId32 "", + status); + + smartcard_trace_connect_w_call(call); + return status; +} + +LONG smartcard_pack_connect_return(wStream* s, const Connect_Return* ret) +{ + LONG status = 0; + DWORD index = 0; + + smartcard_trace_connect_return(ret); + + status = smartcard_pack_redir_scard_context(s, &ret->hContext, &index); + if (status != SCARD_S_SUCCESS) + return status; + + status = smartcard_pack_redir_scard_handle(s, &ret->hCard, &index); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_EnsureRemainingCapacity(s, 4)) + return SCARD_E_NO_MEMORY; + + Stream_Write_UINT32(s, ret->dwActiveProtocol); /* dwActiveProtocol (4 bytes) */ + status = smartcard_pack_redir_scard_context_ref(s, &ret->hContext); + if (status != SCARD_S_SUCCESS) + return status; + return smartcard_pack_redir_scard_handle_ref(s, &(ret->hCard)); +} + +LONG smartcard_unpack_reconnect_call(wStream* s, Reconnect_Call* call) +{ + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, call->dwShareMode); /* dwShareMode (4 bytes) */ + Stream_Read_UINT32(s, call->dwPreferredProtocols); /* dwPreferredProtocols (4 bytes) */ + Stream_Read_UINT32(s, call->dwInitialization); /* dwInitialization (4 bytes) */ + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + { + WLog_ERR(TAG, "smartcard_unpack_redir_scard_context_ref failed with error %" PRId32 "", + status); + return status; + } + + if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard)))) + WLog_ERR(TAG, "smartcard_unpack_redir_scard_handle_ref failed with error %" PRId32 "", + status); + + smartcard_trace_reconnect_call(call); + return status; +} + +LONG smartcard_pack_reconnect_return(wStream* s, const Reconnect_Return* ret) +{ + smartcard_trace_reconnect_return(ret); + + if (!Stream_EnsureRemainingCapacity(s, 4)) + return SCARD_E_NO_MEMORY; + Stream_Write_UINT32(s, ret->dwActiveProtocol); /* dwActiveProtocol (4 bytes) */ + return ret->ReturnCode; +} + +LONG smartcard_unpack_hcard_and_disposition_call(wStream* s, HCardAndDisposition_Call* call, + const char* name) +{ + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, call->dwDisposition); /* dwDisposition (4 bytes) */ + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard)))) + return status; + + smartcard_trace_hcard_and_disposition_call(call, name); + return status; +} + +static void smartcard_trace_get_status_change_a_call(const GetStatusChangeA_Call* call) +{ + char* szEventState = NULL; + char* szCurrentState = NULL; + LPSCARD_READERSTATEA readerState = NULL; + + if (!WLog_IsLevelActive(WLog_Get(TAG), g_LogLevel)) + return; + + WLog_LVL(TAG, g_LogLevel, "GetStatusChangeA_Call {"); + smartcard_log_context(TAG, &call->handles.hContext); + + WLog_LVL(TAG, g_LogLevel, "dwTimeOut: 0x%08" PRIX32 " cReaders: %" PRIu32 "", call->dwTimeOut, + call->cReaders); + + for (UINT32 index = 0; index < call->cReaders; index++) + { + readerState = &call->rgReaderStates[index]; + WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: szReader: %s cbAtr: %" PRIu32 "", index, + readerState->szReader, readerState->cbAtr); + szCurrentState = SCardGetReaderStateString(readerState->dwCurrentState); + szEventState = SCardGetReaderStateString(readerState->dwEventState); + WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwCurrentState: %s (0x%08" PRIX32 ")", index, + szCurrentState, readerState->dwCurrentState); + WLog_LVL(TAG, g_LogLevel, "\t[%" PRIu32 "]: dwEventState: %s (0x%08" PRIX32 ")", index, + szEventState, readerState->dwEventState); + free(szCurrentState); + free(szEventState); + } + + WLog_LVL(TAG, g_LogLevel, "}"); +} + +static LONG smartcard_unpack_reader_state_a(wStream* s, LPSCARD_READERSTATEA* ppcReaders, + UINT32 cReaders, UINT32* ptrIndex) +{ + UINT32 len = 0; + LONG status = SCARD_E_NO_MEMORY; + LPSCARD_READERSTATEA rgReaderStates = NULL; + BOOL* states = NULL; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return status; + + Stream_Read_UINT32(s, len); + if (len != cReaders) + { + WLog_ERR(TAG, "Count mismatch when reading LPSCARD_READERSTATEA"); + return status; + } + rgReaderStates = (LPSCARD_READERSTATEA)calloc(cReaders, sizeof(SCARD_READERSTATEA)); + states = calloc(cReaders, sizeof(BOOL)); + if (!rgReaderStates || !states) + goto fail; + status = ERROR_INVALID_DATA; + + for (UINT32 index = 0; index < cReaders; index++) + { + UINT32 ptr = UINT32_MAX; + LPSCARD_READERSTATEA readerState = &rgReaderStates[index]; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 52)) + goto fail; + + if (!smartcard_ndr_pointer_read(s, ptrIndex, &ptr)) + { + if (ptr != 0) + goto fail; + } + /* Ignore NULL length strings */ + states[index] = ptr != 0; + Stream_Read_UINT32(s, readerState->dwCurrentState); /* dwCurrentState (4 bytes) */ + Stream_Read_UINT32(s, readerState->dwEventState); /* dwEventState (4 bytes) */ + Stream_Read_UINT32(s, readerState->cbAtr); /* cbAtr (4 bytes) */ + Stream_Read(s, readerState->rgbAtr, 36); /* rgbAtr [0..36] (36 bytes) */ + } + + for (UINT32 index = 0; index < cReaders; index++) + { + LPSCARD_READERSTATEA readerState = &rgReaderStates[index]; + + /* Ignore empty strings */ + if (!states[index]) + continue; + status = smartcard_ndr_read_a(s, &readerState->szReader, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + goto fail; + } + + *ppcReaders = rgReaderStates; + free(states); + return SCARD_S_SUCCESS; +fail: + if (rgReaderStates) + { + for (UINT32 index = 0; index < cReaders; index++) + { + LPSCARD_READERSTATEA readerState = &rgReaderStates[index]; + free(readerState->szReader); + } + } + free(rgReaderStates); + free(states); + return status; +} + +static LONG smartcard_unpack_reader_state_w(wStream* s, LPSCARD_READERSTATEW* ppcReaders, + UINT32 cReaders, UINT32* ptrIndex) +{ + UINT32 len = 0; + LONG status = SCARD_E_NO_MEMORY; + LPSCARD_READERSTATEW rgReaderStates = NULL; + BOOL* states = NULL; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return status; + + Stream_Read_UINT32(s, len); + if (len != cReaders) + { + WLog_ERR(TAG, "Count mismatch when reading LPSCARD_READERSTATEW"); + return status; + } + + rgReaderStates = (LPSCARD_READERSTATEW)calloc(cReaders, sizeof(SCARD_READERSTATEW)); + states = calloc(cReaders, sizeof(BOOL)); + + if (!rgReaderStates || !states) + goto fail; + + status = ERROR_INVALID_DATA; + for (UINT32 index = 0; index < cReaders; index++) + { + UINT32 ptr = UINT32_MAX; + LPSCARD_READERSTATEW readerState = &rgReaderStates[index]; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 52)) + goto fail; + + if (!smartcard_ndr_pointer_read(s, ptrIndex, &ptr)) + { + if (ptr != 0) + goto fail; + } + /* Ignore NULL length strings */ + states[index] = ptr != 0; + Stream_Read_UINT32(s, readerState->dwCurrentState); /* dwCurrentState (4 bytes) */ + Stream_Read_UINT32(s, readerState->dwEventState); /* dwEventState (4 bytes) */ + Stream_Read_UINT32(s, readerState->cbAtr); /* cbAtr (4 bytes) */ + Stream_Read(s, readerState->rgbAtr, 36); /* rgbAtr [0..36] (36 bytes) */ + } + + for (UINT32 index = 0; index < cReaders; index++) + { + LPSCARD_READERSTATEW readerState = &rgReaderStates[index]; + + /* Skip NULL pointers */ + if (!states[index]) + continue; + + status = smartcard_ndr_read_w(s, &readerState->szReader, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + goto fail; + } + + *ppcReaders = rgReaderStates; + free(states); + return SCARD_S_SUCCESS; +fail: + if (rgReaderStates) + { + for (UINT32 index = 0; index < cReaders; index++) + { + LPSCARD_READERSTATEW readerState = &rgReaderStates[index]; + free(readerState->szReader); + } + } + free(rgReaderStates); + free(states); + return status; +} + +/******************************************************************************/ +/************************************* End Trace Functions ********************/ +/******************************************************************************/ + +LONG smartcard_unpack_get_status_change_a_call(wStream* s, GetStatusChangeA_Call* call) +{ + UINT32 ndrPtr = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + call->rgReaderStates = NULL; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, call->dwTimeOut); /* dwTimeOut (4 bytes) */ + Stream_Read_UINT32(s, call->cReaders); /* cReaders (4 bytes) */ + if (!smartcard_ndr_pointer_read(s, &index, &ndrPtr)) + return ERROR_INVALID_DATA; + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if (ndrPtr) + { + status = smartcard_unpack_reader_state_a(s, &call->rgReaderStates, call->cReaders, &index); + if (status != SCARD_S_SUCCESS) + return status; + } + + smartcard_trace_get_status_change_a_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_get_status_change_w_call(wStream* s, GetStatusChangeW_Call* call) +{ + UINT32 ndrPtr = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + call->rgReaderStates = NULL; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, call->dwTimeOut); /* dwTimeOut (4 bytes) */ + Stream_Read_UINT32(s, call->cReaders); /* cReaders (4 bytes) */ + if (!smartcard_ndr_pointer_read(s, &index, &ndrPtr)) + return ERROR_INVALID_DATA; + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if (ndrPtr) + { + status = smartcard_unpack_reader_state_w(s, &call->rgReaderStates, call->cReaders, &index); + if (status != SCARD_S_SUCCESS) + return status; + } + + smartcard_trace_get_status_change_w_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_pack_get_status_change_return(wStream* s, const GetStatusChange_Return* ret, + BOOL unicode) +{ + LONG status = 0; + DWORD cReaders = ret->cReaders; + UINT32 index = 0; + + smartcard_trace_get_status_change_return(ret, unicode); + if (ret->ReturnCode != SCARD_S_SUCCESS) + cReaders = 0; + if (cReaders == SCARD_AUTOALLOCATE) + cReaders = 0; + + if (!Stream_EnsureRemainingCapacity(s, 4)) + return SCARD_E_NO_MEMORY; + + Stream_Write_UINT32(s, cReaders); /* cReaders (4 bytes) */ + if (!smartcard_ndr_pointer_write(s, &index, cReaders)) + return SCARD_E_NO_MEMORY; + status = smartcard_ndr_write_state(s, ret->rgReaderStates, cReaders, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + return ret->ReturnCode; +} + +LONG smartcard_unpack_state_call(wStream* s, State_Call* call) +{ + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_INT32(s, call->fpbAtrIsNULL); /* fpbAtrIsNULL (4 bytes) */ + Stream_Read_UINT32(s, call->cbAtrLen); /* cbAtrLen (4 bytes) */ + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard)))) + return status; + + return status; +} + +LONG smartcard_pack_state_return(wStream* s, const State_Return* ret) +{ + LONG status = 0; + DWORD cbAtrLen = ret->cbAtrLen; + UINT32 index = 0; + + smartcard_trace_state_return(ret); + if (ret->ReturnCode != SCARD_S_SUCCESS) + cbAtrLen = 0; + if (cbAtrLen == SCARD_AUTOALLOCATE) + cbAtrLen = 0; + + Stream_Write_UINT32(s, ret->dwState); /* dwState (4 bytes) */ + Stream_Write_UINT32(s, ret->dwProtocol); /* dwProtocol (4 bytes) */ + Stream_Write_UINT32(s, cbAtrLen); /* cbAtrLen (4 bytes) */ + if (!smartcard_ndr_pointer_write(s, &index, cbAtrLen)) + return SCARD_E_NO_MEMORY; + status = smartcard_ndr_write(s, ret->rgAtr, cbAtrLen, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + return ret->ReturnCode; +} + +LONG smartcard_unpack_status_call(wStream* s, Status_Call* call, BOOL unicode) +{ + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_INT32(s, call->fmszReaderNamesIsNULL); /* fmszReaderNamesIsNULL (4 bytes) */ + Stream_Read_UINT32(s, call->cchReaderLen); /* cchReaderLen (4 bytes) */ + Stream_Read_UINT32(s, call->cbAtrLen); /* cbAtrLen (4 bytes) */ + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard)))) + return status; + + smartcard_trace_status_call(call, unicode); + return status; +} + +LONG smartcard_pack_status_return(wStream* s, const Status_Return* ret, BOOL unicode) +{ + LONG status = 0; + UINT32 index = 0; + DWORD cBytes = ret->cBytes; + + smartcard_trace_status_return(ret, unicode); + if (ret->ReturnCode != SCARD_S_SUCCESS) + cBytes = 0; + if (cBytes == SCARD_AUTOALLOCATE) + cBytes = 0; + + if (!Stream_EnsureRemainingCapacity(s, 4)) + return SCARD_F_INTERNAL_ERROR; + + Stream_Write_UINT32(s, cBytes); /* cBytes (4 bytes) */ + if (!smartcard_ndr_pointer_write(s, &index, cBytes)) + return SCARD_E_NO_MEMORY; + + if (!Stream_EnsureRemainingCapacity(s, 44)) + return SCARD_F_INTERNAL_ERROR; + + Stream_Write_UINT32(s, ret->dwState); /* dwState (4 bytes) */ + Stream_Write_UINT32(s, ret->dwProtocol); /* dwProtocol (4 bytes) */ + Stream_Write(s, ret->pbAtr, sizeof(ret->pbAtr)); /* pbAtr (32 bytes) */ + Stream_Write_UINT32(s, ret->cbAtrLen); /* cbAtrLen (4 bytes) */ + status = smartcard_ndr_write(s, ret->mszReaderNames, cBytes, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + return ret->ReturnCode; +} + +LONG smartcard_unpack_get_attrib_call(wStream* s, GetAttrib_Call* call) +{ + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, call->dwAttrId); /* dwAttrId (4 bytes) */ + Stream_Read_INT32(s, call->fpbAttrIsNULL); /* fpbAttrIsNULL (4 bytes) */ + Stream_Read_UINT32(s, call->cbAttrLen); /* cbAttrLen (4 bytes) */ + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard)))) + return status; + + smartcard_trace_get_attrib_call(call); + return status; +} + +LONG smartcard_pack_get_attrib_return(wStream* s, const GetAttrib_Return* ret, DWORD dwAttrId, + DWORD cbAttrCallLen) +{ + LONG status = 0; + DWORD cbAttrLen = 0; + UINT32 index = 0; + smartcard_trace_get_attrib_return(ret, dwAttrId); + + if (!Stream_EnsureRemainingCapacity(s, 4)) + return SCARD_F_INTERNAL_ERROR; + + cbAttrLen = ret->cbAttrLen; + if (ret->ReturnCode != SCARD_S_SUCCESS) + cbAttrLen = 0; + if (cbAttrLen == SCARD_AUTOALLOCATE) + cbAttrLen = 0; + + if (ret->pbAttr) + { + if (cbAttrCallLen < cbAttrLen) + cbAttrLen = cbAttrCallLen; + } + Stream_Write_UINT32(s, cbAttrLen); /* cbAttrLen (4 bytes) */ + if (!smartcard_ndr_pointer_write(s, &index, cbAttrLen)) + return SCARD_E_NO_MEMORY; + + status = smartcard_ndr_write(s, ret->pbAttr, cbAttrLen, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + return ret->ReturnCode; +} + +LONG smartcard_unpack_control_call(wStream* s, Control_Call* call) +{ + UINT32 index = 0; + UINT32 pvInBufferNdrPtr = 0; + UINT32 pbContextNdrPtr = 0; + + call->pvInBuffer = NULL; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 20)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, call->dwControlCode); /* dwControlCode (4 bytes) */ + Stream_Read_UINT32(s, call->cbInBufferSize); /* cbInBufferSize (4 bytes) */ + if (!smartcard_ndr_pointer_read(s, &index, &pvInBufferNdrPtr)) /* pvInBufferNdrPtr (4 bytes) */ + return ERROR_INVALID_DATA; + Stream_Read_INT32(s, call->fpvOutBufferIsNULL); /* fpvOutBufferIsNULL (4 bytes) */ + Stream_Read_UINT32(s, call->cbOutBufferSize); /* cbOutBufferSize (4 bytes) */ + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard)))) + return status; + + if (pvInBufferNdrPtr) + { + status = smartcard_ndr_read(s, &call->pvInBuffer, call->cbInBufferSize, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + } + + smartcard_trace_control_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_pack_control_return(wStream* s, const Control_Return* ret) +{ + LONG status = 0; + DWORD cbDataLen = ret->cbOutBufferSize; + UINT32 index = 0; + + smartcard_trace_control_return(ret); + if (ret->ReturnCode != SCARD_S_SUCCESS) + cbDataLen = 0; + if (cbDataLen == SCARD_AUTOALLOCATE) + cbDataLen = 0; + + if (!Stream_EnsureRemainingCapacity(s, 4)) + return SCARD_F_INTERNAL_ERROR; + + Stream_Write_UINT32(s, cbDataLen); /* cbOutBufferSize (4 bytes) */ + if (!smartcard_ndr_pointer_write(s, &index, cbDataLen)) + return SCARD_E_NO_MEMORY; + + status = smartcard_ndr_write(s, ret->pvOutBuffer, cbDataLen, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + return ret->ReturnCode; +} + +LONG smartcard_unpack_transmit_call(wStream* s, Transmit_Call* call) +{ + UINT32 length = 0; + BYTE* pbExtraBytes = NULL; + UINT32 pbExtraBytesNdrPtr = 0; + UINT32 pbSendBufferNdrPtr = 0; + UINT32 pioRecvPciNdrPtr = 0; + SCardIO_Request ioSendPci; + SCardIO_Request ioRecvPci; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + call->pioSendPci = NULL; + call->pioRecvPci = NULL; + call->pbSendBuffer = NULL; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 32)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, ioSendPci.dwProtocol); /* dwProtocol (4 bytes) */ + Stream_Read_UINT32(s, ioSendPci.cbExtraBytes); /* cbExtraBytes (4 bytes) */ + if (!smartcard_ndr_pointer_read(s, &index, + &pbExtraBytesNdrPtr)) /* pbExtraBytesNdrPtr (4 bytes) */ + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, call->cbSendLength); /* cbSendLength (4 bytes) */ + if (!smartcard_ndr_pointer_read(s, &index, + &pbSendBufferNdrPtr)) /* pbSendBufferNdrPtr (4 bytes) */ + return ERROR_INVALID_DATA; + + if (!smartcard_ndr_pointer_read(s, &index, &pioRecvPciNdrPtr)) /* pioRecvPciNdrPtr (4 bytes) */ + return ERROR_INVALID_DATA; + + Stream_Read_INT32(s, call->fpbRecvBufferIsNULL); /* fpbRecvBufferIsNULL (4 bytes) */ + Stream_Read_UINT32(s, call->cbRecvLength); /* cbRecvLength (4 bytes) */ + + if (ioSendPci.cbExtraBytes > 1024) + { + WLog_WARN(TAG, + "Transmit_Call ioSendPci.cbExtraBytes is out of bounds: %" PRIu32 " (max: 1024)", + ioSendPci.cbExtraBytes); + return STATUS_INVALID_PARAMETER; + } + + if (call->cbSendLength > 66560) + { + WLog_WARN(TAG, "Transmit_Call cbSendLength is out of bounds: %" PRIu32 " (max: 66560)", + ioSendPci.cbExtraBytes); + return STATUS_INVALID_PARAMETER; + } + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard)))) + return status; + + if (ioSendPci.cbExtraBytes && !pbExtraBytesNdrPtr) + { + WLog_WARN( + TAG, "Transmit_Call ioSendPci.cbExtraBytes is non-zero but pbExtraBytesNdrPtr is null"); + return STATUS_INVALID_PARAMETER; + } + + if (pbExtraBytesNdrPtr) + { + // TODO: Use unified pointer reading + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + + if (!Stream_CheckAndLogRequiredLength(TAG, s, ioSendPci.cbExtraBytes)) + return STATUS_BUFFER_TOO_SMALL; + + ioSendPci.pbExtraBytes = Stream_Pointer(s); + call->pioSendPci = + (LPSCARD_IO_REQUEST)malloc(sizeof(SCARD_IO_REQUEST) + ioSendPci.cbExtraBytes); + + if (!call->pioSendPci) + { + WLog_WARN(TAG, "Transmit_Call out of memory error (pioSendPci)"); + return STATUS_NO_MEMORY; + } + + call->pioSendPci->dwProtocol = ioSendPci.dwProtocol; + call->pioSendPci->cbPciLength = (DWORD)(ioSendPci.cbExtraBytes + sizeof(SCARD_IO_REQUEST)); + pbExtraBytes = &((BYTE*)call->pioSendPci)[sizeof(SCARD_IO_REQUEST)]; + Stream_Read(s, pbExtraBytes, ioSendPci.cbExtraBytes); + smartcard_unpack_read_size_align(s, ioSendPci.cbExtraBytes, 4); + } + else + { + call->pioSendPci = (LPSCARD_IO_REQUEST)calloc(1, sizeof(SCARD_IO_REQUEST)); + + if (!call->pioSendPci) + { + WLog_WARN(TAG, "Transmit_Call out of memory error (pioSendPci)"); + return STATUS_NO_MEMORY; + } + + call->pioSendPci->dwProtocol = ioSendPci.dwProtocol; + call->pioSendPci->cbPciLength = sizeof(SCARD_IO_REQUEST); + } + + if (pbSendBufferNdrPtr) + { + status = smartcard_ndr_read(s, &call->pbSendBuffer, call->cbSendLength, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + } + + if (pioRecvPciNdrPtr) + { + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, ioRecvPci.dwProtocol); /* dwProtocol (4 bytes) */ + Stream_Read_UINT32(s, ioRecvPci.cbExtraBytes); /* cbExtraBytes (4 bytes) */ + if (!smartcard_ndr_pointer_read(s, &index, + &pbExtraBytesNdrPtr)) /* pbExtraBytesNdrPtr (4 bytes) */ + return ERROR_INVALID_DATA; + + if (ioRecvPci.cbExtraBytes && !pbExtraBytesNdrPtr) + { + WLog_WARN( + TAG, + "Transmit_Call ioRecvPci.cbExtraBytes is non-zero but pbExtraBytesNdrPtr is null"); + return STATUS_INVALID_PARAMETER; + } + + if (pbExtraBytesNdrPtr) + { + // TODO: Unify ndr pointer reading + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + + if (ioRecvPci.cbExtraBytes > 1024) + { + WLog_WARN(TAG, + "Transmit_Call ioRecvPci.cbExtraBytes is out of bounds: %" PRIu32 + " (max: 1024)", + ioRecvPci.cbExtraBytes); + return STATUS_INVALID_PARAMETER; + } + + if (length != ioRecvPci.cbExtraBytes) + { + WLog_WARN(TAG, + "Transmit_Call unexpected length: Actual: %" PRIu32 ", Expected: %" PRIu32 + " (ioRecvPci.cbExtraBytes)", + length, ioRecvPci.cbExtraBytes); + return STATUS_INVALID_PARAMETER; + } + + if (!Stream_CheckAndLogRequiredLength(TAG, s, ioRecvPci.cbExtraBytes)) + return STATUS_BUFFER_TOO_SMALL; + + ioRecvPci.pbExtraBytes = Stream_Pointer(s); + call->pioRecvPci = + (LPSCARD_IO_REQUEST)malloc(sizeof(SCARD_IO_REQUEST) + ioRecvPci.cbExtraBytes); + + if (!call->pioRecvPci) + { + WLog_WARN(TAG, "Transmit_Call out of memory error (pioRecvPci)"); + return STATUS_NO_MEMORY; + } + + call->pioRecvPci->dwProtocol = ioRecvPci.dwProtocol; + call->pioRecvPci->cbPciLength = + (DWORD)(ioRecvPci.cbExtraBytes + sizeof(SCARD_IO_REQUEST)); + pbExtraBytes = &((BYTE*)call->pioRecvPci)[sizeof(SCARD_IO_REQUEST)]; + Stream_Read(s, pbExtraBytes, ioRecvPci.cbExtraBytes); + smartcard_unpack_read_size_align(s, ioRecvPci.cbExtraBytes, 4); + } + else + { + call->pioRecvPci = (LPSCARD_IO_REQUEST)calloc(1, sizeof(SCARD_IO_REQUEST)); + + if (!call->pioRecvPci) + { + WLog_WARN(TAG, "Transmit_Call out of memory error (pioRecvPci)"); + return STATUS_NO_MEMORY; + } + + call->pioRecvPci->dwProtocol = ioRecvPci.dwProtocol; + call->pioRecvPci->cbPciLength = sizeof(SCARD_IO_REQUEST); + } + } + + smartcard_trace_transmit_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_pack_transmit_return(wStream* s, const Transmit_Return* ret) +{ + LONG status = 0; + UINT32 index = 0; + LONG error = 0; + UINT32 cbRecvLength = ret->cbRecvLength; + UINT32 cbRecvPci = ret->pioRecvPci ? ret->pioRecvPci->cbPciLength : 0; + + smartcard_trace_transmit_return(ret); + + if (!ret->pbRecvBuffer) + cbRecvLength = 0; + + if (!smartcard_ndr_pointer_write(s, &index, cbRecvPci)) + return SCARD_E_NO_MEMORY; + if (!Stream_EnsureRemainingCapacity(s, 4)) + return SCARD_E_NO_MEMORY; + Stream_Write_UINT32(s, cbRecvLength); /* cbRecvLength (4 bytes) */ + if (!smartcard_ndr_pointer_write(s, &index, cbRecvLength)) + return SCARD_E_NO_MEMORY; + + if (ret->pioRecvPci) + { + UINT32 cbExtraBytes = (UINT32)(ret->pioRecvPci->cbPciLength - sizeof(SCARD_IO_REQUEST)); + BYTE* pbExtraBytes = &((BYTE*)ret->pioRecvPci)[sizeof(SCARD_IO_REQUEST)]; + + if (!Stream_EnsureRemainingCapacity(s, cbExtraBytes + 16)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + return SCARD_F_INTERNAL_ERROR; + } + + Stream_Write_UINT32(s, ret->pioRecvPci->dwProtocol); /* dwProtocol (4 bytes) */ + Stream_Write_UINT32(s, cbExtraBytes); /* cbExtraBytes (4 bytes) */ + if (!smartcard_ndr_pointer_write(s, &index, cbExtraBytes)) + return SCARD_E_NO_MEMORY; + error = smartcard_ndr_write(s, pbExtraBytes, cbExtraBytes, 1, NDR_PTR_SIMPLE); + if (error) + return error; + } + + status = smartcard_ndr_write(s, ret->pbRecvBuffer, ret->cbRecvLength, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + return ret->ReturnCode; +} + +LONG smartcard_unpack_locate_cards_by_atr_a_call(wStream* s, LocateCardsByATRA_Call* call) +{ + UINT32 rgReaderStatesNdrPtr = 0; + UINT32 rgAtrMasksNdrPtr = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + call->rgReaderStates = NULL; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 16)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, call->cAtrs); + if (!smartcard_ndr_pointer_read(s, &index, &rgAtrMasksNdrPtr)) + return ERROR_INVALID_DATA; + Stream_Read_UINT32(s, call->cReaders); /* cReaders (4 bytes) */ + if (!smartcard_ndr_pointer_read(s, &index, &rgReaderStatesNdrPtr)) + return ERROR_INVALID_DATA; + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if ((rgAtrMasksNdrPtr && !call->cAtrs) || (!rgAtrMasksNdrPtr && call->cAtrs)) + { + WLog_WARN(TAG, + "LocateCardsByATRA_Call rgAtrMasksNdrPtr (0x%08" PRIX32 + ") and cAtrs (0x%08" PRIX32 ") inconsistency", + rgAtrMasksNdrPtr, call->cAtrs); + return STATUS_INVALID_PARAMETER; + } + + if (rgAtrMasksNdrPtr) + { + status = smartcard_ndr_read_atrmask(s, &call->rgAtrMasks, call->cAtrs, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + } + + if (rgReaderStatesNdrPtr) + { + status = smartcard_unpack_reader_state_a(s, &call->rgReaderStates, call->cReaders, &index); + if (status != SCARD_S_SUCCESS) + return status; + } + + smartcard_trace_locate_cards_by_atr_a_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_context_and_two_strings_a_call(wStream* s, ContextAndTwoStringA_Call* call) +{ + UINT32 sz1NdrPtr = 0; + UINT32 sz2NdrPtr = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!smartcard_ndr_pointer_read(s, &index, &sz1NdrPtr)) + return ERROR_INVALID_DATA; + if (!smartcard_ndr_pointer_read(s, &index, &sz2NdrPtr)) + return ERROR_INVALID_DATA; + + status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, &call->handles.hContext); + if (status != SCARD_S_SUCCESS) + return status; + + if (sz1NdrPtr) + { + status = smartcard_ndr_read_a(s, &call->sz1, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + return status; + } + if (sz2NdrPtr) + { + status = smartcard_ndr_read_a(s, &call->sz2, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + return status; + } + smartcard_trace_context_and_two_strings_a_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_context_and_two_strings_w_call(wStream* s, ContextAndTwoStringW_Call* call) +{ + UINT32 sz1NdrPtr = 0; + UINT32 sz2NdrPtr = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!smartcard_ndr_pointer_read(s, &index, &sz1NdrPtr)) + return ERROR_INVALID_DATA; + if (!smartcard_ndr_pointer_read(s, &index, &sz2NdrPtr)) + return ERROR_INVALID_DATA; + + status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, &call->handles.hContext); + if (status != SCARD_S_SUCCESS) + return status; + + if (sz1NdrPtr) + { + status = smartcard_ndr_read_w(s, &call->sz1, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + return status; + } + if (sz2NdrPtr) + { + status = smartcard_ndr_read_w(s, &call->sz2, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + return status; + } + smartcard_trace_context_and_two_strings_w_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_locate_cards_a_call(wStream* s, LocateCardsA_Call* call) +{ + UINT32 sz1NdrPtr = 0; + UINT32 sz2NdrPtr = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 16)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, call->cBytes); + if (!smartcard_ndr_pointer_read(s, &index, &sz1NdrPtr)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, call->cReaders); + if (!smartcard_ndr_pointer_read(s, &index, &sz2NdrPtr)) + return ERROR_INVALID_DATA; + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if (sz1NdrPtr) + { + status = + smartcard_ndr_read_fixed_string_a(s, &call->mszCards, call->cBytes, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + } + if (sz2NdrPtr) + { + status = smartcard_unpack_reader_state_a(s, &call->rgReaderStates, call->cReaders, &index); + if (status != SCARD_S_SUCCESS) + return status; + } + smartcard_trace_locate_cards_a_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_locate_cards_w_call(wStream* s, LocateCardsW_Call* call) +{ + UINT32 sz1NdrPtr = 0; + UINT32 sz2NdrPtr = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 16)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, call->cBytes); + if (!smartcard_ndr_pointer_read(s, &index, &sz1NdrPtr)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, call->cReaders); + if (!smartcard_ndr_pointer_read(s, &index, &sz2NdrPtr)) + return ERROR_INVALID_DATA; + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if (sz1NdrPtr) + { + status = + smartcard_ndr_read_fixed_string_w(s, &call->mszCards, call->cBytes, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + } + if (sz2NdrPtr) + { + status = smartcard_unpack_reader_state_w(s, &call->rgReaderStates, call->cReaders, &index); + if (status != SCARD_S_SUCCESS) + return status; + } + smartcard_trace_locate_cards_w_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_set_attrib_call(wStream* s, SetAttrib_Call* call) +{ + UINT32 index = 0; + UINT32 ndrPtr = 0; + UINT32 pbContextNdrPtr = 0; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return STATUS_BUFFER_TOO_SMALL; + Stream_Read_UINT32(s, call->dwAttrId); + Stream_Read_UINT32(s, call->cbAttrLen); + + if (!smartcard_ndr_pointer_read(s, &index, &ndrPtr)) + return ERROR_INVALID_DATA; + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard)))) + return status; + + if (ndrPtr) + { + // TODO: call->cbAttrLen was larger than the pointer value. + // TODO: Maybe need to refine the checks? + status = smartcard_ndr_read(s, &call->pbAttr, 0, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + } + smartcard_trace_set_attrib_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_locate_cards_by_atr_w_call(wStream* s, LocateCardsByATRW_Call* call) +{ + UINT32 rgReaderStatesNdrPtr = 0; + UINT32 rgAtrMasksNdrPtr = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + call->rgReaderStates = NULL; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 16)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, call->cAtrs); + if (!smartcard_ndr_pointer_read(s, &index, &rgAtrMasksNdrPtr)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, call->cReaders); /* cReaders (4 bytes) */ + if (!smartcard_ndr_pointer_read(s, &index, &rgReaderStatesNdrPtr)) + return ERROR_INVALID_DATA; + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + return status; + + if ((rgAtrMasksNdrPtr && !call->cAtrs) || (!rgAtrMasksNdrPtr && call->cAtrs)) + { + WLog_WARN(TAG, + "LocateCardsByATRW_Call rgAtrMasksNdrPtr (0x%08" PRIX32 + ") and cAtrs (0x%08" PRIX32 ") inconsistency", + rgAtrMasksNdrPtr, call->cAtrs); + return STATUS_INVALID_PARAMETER; + } + + if (rgAtrMasksNdrPtr) + { + status = smartcard_ndr_read_atrmask(s, &call->rgAtrMasks, call->cAtrs, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + } + + if (rgReaderStatesNdrPtr) + { + status = smartcard_unpack_reader_state_w(s, &call->rgReaderStates, call->cReaders, &index); + if (status != SCARD_S_SUCCESS) + return status; + } + + smartcard_trace_locate_cards_by_atr_w_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_read_cache_a_call(wStream* s, ReadCacheA_Call* call) +{ + UINT32 mszNdrPtr = 0; + UINT32 contextNdrPtr = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + if (!smartcard_ndr_pointer_read(s, &index, &mszNdrPtr)) + return ERROR_INVALID_DATA; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->Common.handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!smartcard_ndr_pointer_read(s, &index, &contextNdrPtr)) + return ERROR_INVALID_DATA; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return STATUS_BUFFER_TOO_SMALL; + Stream_Read_UINT32(s, call->Common.FreshnessCounter); + Stream_Read_INT32(s, call->Common.fPbDataIsNULL); + Stream_Read_UINT32(s, call->Common.cbDataLen); + + call->szLookupName = NULL; + if (mszNdrPtr) + { + status = smartcard_ndr_read_a(s, &call->szLookupName, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + return status; + } + + status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &call->Common.handles.hContext); + if (status != SCARD_S_SUCCESS) + return status; + + if (contextNdrPtr) + { + status = smartcard_ndr_read_u(s, &call->Common.CardIdentifier); + if (status != SCARD_S_SUCCESS) + return status; + } + smartcard_trace_read_cache_a_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_read_cache_w_call(wStream* s, ReadCacheW_Call* call) +{ + UINT32 mszNdrPtr = 0; + UINT32 contextNdrPtr = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + if (!smartcard_ndr_pointer_read(s, &index, &mszNdrPtr)) + return ERROR_INVALID_DATA; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->Common.handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!smartcard_ndr_pointer_read(s, &index, &contextNdrPtr)) + return ERROR_INVALID_DATA; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return STATUS_BUFFER_TOO_SMALL; + Stream_Read_UINT32(s, call->Common.FreshnessCounter); + Stream_Read_INT32(s, call->Common.fPbDataIsNULL); + Stream_Read_UINT32(s, call->Common.cbDataLen); + + call->szLookupName = NULL; + if (mszNdrPtr) + { + status = smartcard_ndr_read_w(s, &call->szLookupName, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + return status; + } + + status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &call->Common.handles.hContext); + if (status != SCARD_S_SUCCESS) + return status; + + if (contextNdrPtr) + { + status = smartcard_ndr_read_u(s, &call->Common.CardIdentifier); + if (status != SCARD_S_SUCCESS) + return status; + } + smartcard_trace_read_cache_w_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_write_cache_a_call(wStream* s, WriteCacheA_Call* call) +{ + UINT32 mszNdrPtr = 0; + UINT32 contextNdrPtr = 0; + UINT32 pbDataNdrPtr = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + if (!smartcard_ndr_pointer_read(s, &index, &mszNdrPtr)) + return ERROR_INVALID_DATA; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->Common.handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!smartcard_ndr_pointer_read(s, &index, &contextNdrPtr)) + return ERROR_INVALID_DATA; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return STATUS_BUFFER_TOO_SMALL; + + Stream_Read_UINT32(s, call->Common.FreshnessCounter); + Stream_Read_UINT32(s, call->Common.cbDataLen); + + if (!smartcard_ndr_pointer_read(s, &index, &pbDataNdrPtr)) + return ERROR_INVALID_DATA; + + call->szLookupName = NULL; + if (mszNdrPtr) + { + status = smartcard_ndr_read_a(s, &call->szLookupName, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + return status; + } + + status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &call->Common.handles.hContext); + if (status != SCARD_S_SUCCESS) + return status; + + call->Common.CardIdentifier = NULL; + if (contextNdrPtr) + { + status = smartcard_ndr_read_u(s, &call->Common.CardIdentifier); + if (status != SCARD_S_SUCCESS) + return status; + } + + call->Common.pbData = NULL; + if (pbDataNdrPtr) + { + status = + smartcard_ndr_read(s, &call->Common.pbData, call->Common.cbDataLen, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + } + smartcard_trace_write_cache_a_call(call); + return SCARD_S_SUCCESS; +} + +LONG smartcard_unpack_write_cache_w_call(wStream* s, WriteCacheW_Call* call) +{ + UINT32 mszNdrPtr = 0; + UINT32 contextNdrPtr = 0; + UINT32 pbDataNdrPtr = 0; + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + if (!smartcard_ndr_pointer_read(s, &index, &mszNdrPtr)) + return ERROR_INVALID_DATA; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->Common.handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + if (!smartcard_ndr_pointer_read(s, &index, &contextNdrPtr)) + return ERROR_INVALID_DATA; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return STATUS_BUFFER_TOO_SMALL; + Stream_Read_UINT32(s, call->Common.FreshnessCounter); + Stream_Read_UINT32(s, call->Common.cbDataLen); + + if (!smartcard_ndr_pointer_read(s, &index, &pbDataNdrPtr)) + return ERROR_INVALID_DATA; + + call->szLookupName = NULL; + if (mszNdrPtr) + { + status = smartcard_ndr_read_w(s, &call->szLookupName, NDR_PTR_FULL); + if (status != SCARD_S_SUCCESS) + return status; + } + + status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &call->Common.handles.hContext); + if (status != SCARD_S_SUCCESS) + return status; + + call->Common.CardIdentifier = NULL; + if (contextNdrPtr) + { + status = smartcard_ndr_read_u(s, &call->Common.CardIdentifier); + if (status != SCARD_S_SUCCESS) + return status; + } + + call->Common.pbData = NULL; + if (pbDataNdrPtr) + { + status = + smartcard_ndr_read(s, &call->Common.pbData, call->Common.cbDataLen, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + } + smartcard_trace_write_cache_w_call(call); + return status; +} + +LONG smartcard_unpack_get_transmit_count_call(wStream* s, GetTransmitCount_Call* call) +{ + UINT32 index = 0; + UINT32 pbContextNdrPtr = 0; + + LONG status = smartcard_unpack_redir_scard_context(s, &(call->handles.hContext), &index, + &pbContextNdrPtr); + if (status != SCARD_S_SUCCESS) + return status; + + status = smartcard_unpack_redir_scard_handle(s, &(call->handles.hCard), &index); + if (status != SCARD_S_SUCCESS) + return status; + + if ((status = smartcard_unpack_redir_scard_context_ref(s, pbContextNdrPtr, + &(call->handles.hContext)))) + { + WLog_ERR(TAG, "smartcard_unpack_redir_scard_context_ref failed with error %" PRId32 "", + status); + return status; + } + + if ((status = smartcard_unpack_redir_scard_handle_ref(s, &(call->handles.hCard)))) + WLog_ERR(TAG, "smartcard_unpack_redir_scard_handle_ref failed with error %" PRId32 "", + status); + + smartcard_trace_get_transmit_count_call(call); + return status; +} + +LONG smartcard_unpack_get_reader_icon_call(wStream* s, GetReaderIcon_Call* call) +{ + return smartcard_unpack_common_context_and_string_w(s, &call->handles.hContext, + &call->szReaderName); +} + +LONG smartcard_unpack_context_and_string_a_call(wStream* s, ContextAndStringA_Call* call) +{ + return smartcard_unpack_common_context_and_string_a(s, &call->handles.hContext, &call->sz); +} + +LONG smartcard_unpack_context_and_string_w_call(wStream* s, ContextAndStringW_Call* call) +{ + return smartcard_unpack_common_context_and_string_w(s, &call->handles.hContext, &call->sz); +} + +LONG smartcard_unpack_get_device_type_id_call(wStream* s, GetDeviceTypeId_Call* call) +{ + return smartcard_unpack_common_context_and_string_w(s, &call->handles.hContext, + &call->szReaderName); +} + +LONG smartcard_pack_device_type_id_return(wStream* s, const GetDeviceTypeId_Return* ret) +{ + smartcard_trace_device_type_id_return(ret); + + if (!Stream_EnsureRemainingCapacity(s, 4)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + return SCARD_F_INTERNAL_ERROR; + } + + Stream_Write_UINT32(s, ret->dwDeviceId); /* cBytes (4 bytes) */ + + return ret->ReturnCode; +} + +LONG smartcard_pack_locate_cards_return(wStream* s, const LocateCards_Return* ret) +{ + LONG status = 0; + DWORD cbDataLen = ret->cReaders; + UINT32 index = 0; + + smartcard_trace_locate_cards_return(ret); + if (ret->ReturnCode != SCARD_S_SUCCESS) + cbDataLen = 0; + if (cbDataLen == SCARD_AUTOALLOCATE) + cbDataLen = 0; + + if (!Stream_EnsureRemainingCapacity(s, 4)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + return SCARD_F_INTERNAL_ERROR; + } + + Stream_Write_UINT32(s, cbDataLen); /* cBytes (4 cbDataLen) */ + if (!smartcard_ndr_pointer_write(s, &index, cbDataLen)) + return SCARD_E_NO_MEMORY; + + status = smartcard_ndr_write_state(s, ret->rgReaderStates, cbDataLen, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + return ret->ReturnCode; +} + +LONG smartcard_pack_get_reader_icon_return(wStream* s, const GetReaderIcon_Return* ret) +{ + LONG status = 0; + UINT32 index = 0; + DWORD cbDataLen = ret->cbDataLen; + smartcard_trace_get_reader_icon_return(ret); + if (ret->ReturnCode != SCARD_S_SUCCESS) + cbDataLen = 0; + if (cbDataLen == SCARD_AUTOALLOCATE) + cbDataLen = 0; + + if (!Stream_EnsureRemainingCapacity(s, 4)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + return SCARD_F_INTERNAL_ERROR; + } + + Stream_Write_UINT32(s, cbDataLen); /* cBytes (4 cbDataLen) */ + if (!smartcard_ndr_pointer_write(s, &index, cbDataLen)) + return SCARD_E_NO_MEMORY; + + status = smartcard_ndr_write(s, ret->pbData, cbDataLen, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + return ret->ReturnCode; +} + +LONG smartcard_pack_get_transmit_count_return(wStream* s, const GetTransmitCount_Return* ret) +{ + smartcard_trace_get_transmit_count_return(ret); + + if (!Stream_EnsureRemainingCapacity(s, 4)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + return SCARD_F_INTERNAL_ERROR; + } + + Stream_Write_UINT32(s, ret->cTransmitCount); /* cBytes (4 cbDataLen) */ + + return ret->ReturnCode; +} + +LONG smartcard_pack_read_cache_return(wStream* s, const ReadCache_Return* ret) +{ + LONG status = 0; + UINT32 index = 0; + DWORD cbDataLen = ret->cbDataLen; + smartcard_trace_read_cache_return(ret); + if (ret->ReturnCode != SCARD_S_SUCCESS) + cbDataLen = 0; + + if (cbDataLen == SCARD_AUTOALLOCATE) + cbDataLen = 0; + + if (!Stream_EnsureRemainingCapacity(s, 4)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + return SCARD_F_INTERNAL_ERROR; + } + + Stream_Write_UINT32(s, cbDataLen); /* cBytes (4 cbDataLen) */ + if (!smartcard_ndr_pointer_write(s, &index, cbDataLen)) + return SCARD_E_NO_MEMORY; + + status = smartcard_ndr_write(s, ret->pbData, cbDataLen, 1, NDR_PTR_SIMPLE); + if (status != SCARD_S_SUCCESS) + return status; + return ret->ReturnCode; +} diff --git a/libfreerdp/utils/stopwatch.c b/libfreerdp/utils/stopwatch.c new file mode 100644 index 0000000..3989638 --- /dev/null +++ b/libfreerdp/utils/stopwatch.c @@ -0,0 +1,98 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Stopwatch Utils + * + * Copyright 2011 Stephen Erisman + * + * 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. + */ + +#include <freerdp/config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <freerdp/utils/stopwatch.h> + +#ifdef _WIN32 +LARGE_INTEGER stopwatch_freq = { 0 }; +#else +#include <sys/time.h> +#endif + +static void stopwatch_set_time(UINT64* usecs) +{ +#ifdef _WIN32 + LARGE_INTEGER perfcount; + QueryPerformanceCounter(&perfcount); + *usecs = (perfcount.QuadPart * 1000000) / stopwatch_freq.QuadPart; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + *usecs = tv.tv_sec * 1000000 + tv.tv_usec; +#endif +} + +STOPWATCH* stopwatch_create(void) +{ + STOPWATCH* sw = NULL; +#ifdef _WIN32 + if (stopwatch_freq.QuadPart == 0) + { + QueryPerformanceFrequency(&stopwatch_freq); + } +#endif + + sw = (STOPWATCH*)malloc(sizeof(STOPWATCH)); + if (!sw) + return NULL; + stopwatch_reset(sw); + + return sw; +} + +void stopwatch_free(STOPWATCH* stopwatch) +{ + free(stopwatch); +} + +void stopwatch_start(STOPWATCH* stopwatch) +{ + stopwatch_set_time(&stopwatch->start); + stopwatch->count++; +} + +void stopwatch_stop(STOPWATCH* stopwatch) +{ + stopwatch_set_time(&stopwatch->end); + stopwatch->elapsed += (stopwatch->end - stopwatch->start); +} + +void stopwatch_reset(STOPWATCH* stopwatch) +{ + stopwatch->start = 0; + stopwatch->end = 0; + stopwatch->elapsed = 0; + stopwatch->count = 0; +} + +double stopwatch_get_elapsed_time_in_seconds(STOPWATCH* stopwatch) +{ + return (stopwatch->elapsed / 1000000.0); +} + +void stopwatch_get_elapsed_time_in_useconds(STOPWATCH* stopwatch, UINT32* sec, UINT32* usec) +{ + *sec = (UINT32)stopwatch->elapsed / 1000000; + *usec = (UINT32)stopwatch->elapsed % 1000000; +} diff --git a/libfreerdp/utils/string.c b/libfreerdp/utils/string.c new file mode 100644 index 0000000..3ee73f1 --- /dev/null +++ b/libfreerdp/utils/string.c @@ -0,0 +1,105 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * String Utils - Helper functions converting something to string + * + * Copyright 2022 Armin Novak <armin.novak@thincast.com> + * Copyright 2022 Thincast Technologies GmbH + * + * 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. + */ + +#include <freerdp/utils/string.h> +#include <freerdp/settings.h> + +char* rdp_redirection_flags_to_string(UINT32 flags, char* buffer, size_t size) +{ + struct map_t + { + UINT32 flag; + const char* name; + }; + const struct map_t map[] = { + { LB_TARGET_NET_ADDRESS, "LB_TARGET_NET_ADDRESS" }, + { LB_LOAD_BALANCE_INFO, "LB_LOAD_BALANCE_INFO" }, + { LB_USERNAME, "LB_USERNAME" }, + { LB_DOMAIN, "LB_DOMAIN" }, + { LB_PASSWORD, "LB_PASSWORD" }, + { LB_DONTSTOREUSERNAME, "LB_DONTSTOREUSERNAME" }, + { LB_SMARTCARD_LOGON, "LB_SMARTCARD_LOGON" }, + { LB_NOREDIRECT, "LB_NOREDIRECT" }, + { LB_TARGET_FQDN, "LB_TARGET_FQDN" }, + { LB_TARGET_NETBIOS_NAME, "LB_TARGET_NETBIOS_NAME" }, + { LB_TARGET_NET_ADDRESSES, "LB_TARGET_NET_ADDRESSES" }, + { LB_CLIENT_TSV_URL, "LB_CLIENT_TSV_URL" }, + { LB_SERVER_TSV_CAPABLE, "LB_SERVER_TSV_CAPABLE" }, + { LB_PASSWORD_IS_PK_ENCRYPTED, "LB_PASSWORD_IS_PK_ENCRYPTED" }, + { LB_REDIRECTION_GUID, "LB_REDIRECTION_GUID" }, + { LB_TARGET_CERTIFICATE, "LB_TARGET_CERTIFICATE" }, + }; + + for (size_t x = 0; x < ARRAYSIZE(map); x++) + { + const struct map_t* cur = &map[x]; + if (flags & cur->flag) + { + if (!winpr_str_append(cur->name, buffer, size, "|")) + return NULL; + } + } + return buffer; +} + +char* rdp_cluster_info_flags_to_string(UINT32 flags, char* buffer, size_t size) +{ + const UINT32 version = (flags & ServerSessionRedirectionVersionMask) >> 2; + if (flags & REDIRECTION_SUPPORTED) + winpr_str_append("REDIRECTION_SUPPORTED", buffer, size, "|"); + if (flags & REDIRECTED_SESSIONID_FIELD_VALID) + winpr_str_append("REDIRECTED_SESSIONID_FIELD_VALID", buffer, size, "|"); + if (flags & REDIRECTED_SMARTCARD) + winpr_str_append("REDIRECTED_SMARTCARD", buffer, size, "|"); + + const char* str = NULL; + switch (version) + { + case REDIRECTION_VERSION1: + str = "REDIRECTION_VERSION1"; + break; + case REDIRECTION_VERSION2: + str = "REDIRECTION_VERSION2"; + break; + case REDIRECTION_VERSION3: + str = "REDIRECTION_VERSION3"; + break; + case REDIRECTION_VERSION4: + str = "REDIRECTION_VERSION4"; + break; + case REDIRECTION_VERSION5: + str = "REDIRECTION_VERSION5"; + break; + case REDIRECTION_VERSION6: + str = "REDIRECTION_VERSION6"; + break; + default: + str = "REDIRECTION_VERSION_UNKNOWN"; + break; + } + winpr_str_append(str, buffer, size, "|"); + { + char msg[32] = { 0 }; + _snprintf(msg, sizeof(msg), "[0x%08" PRIx32 "]", flags); + winpr_str_append(msg, buffer, size, ""); + } + return buffer; +} diff --git a/libfreerdp/utils/test/CMakeLists.txt b/libfreerdp/utils/test/CMakeLists.txt new file mode 100644 index 0000000..f2b6977 --- /dev/null +++ b/libfreerdp/utils/test/CMakeLists.txt @@ -0,0 +1,28 @@ + +set(MODULE_NAME "TestFreeRDPUtils") +set(MODULE_PREFIX "TEST_FREERDP_UTILS") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestRingBuffer.c + TestPodArrays.c +) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} freerdp winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Test") + diff --git a/libfreerdp/utils/test/TestPodArrays.c b/libfreerdp/utils/test/TestPodArrays.c new file mode 100644 index 0000000..6c88b53 --- /dev/null +++ b/libfreerdp/utils/test/TestPodArrays.c @@ -0,0 +1,131 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * TestPodArrays + * + * Copyright 2022 David Fort <contact@hardening-consulting.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. + */ +#include <freerdp/utils/pod_arrays.h> + +static BOOL cb_compute_sum(UINT32* v, void* target) +{ + UINT32* ret = (UINT32*)target; + *ret += *v; + return TRUE; +} + +static BOOL cb_stop_at_5(UINT32* v, void* target) +{ + UINT32* ret = (UINT32*)target; + *ret += 1; + return (*ret != 5); +} + +static BOOL cb_set_to_1(UINT32* v, void* target) +{ + *v = 1; + return TRUE; +} + +static BOOL cb_reset_after_1(UINT32* v, void* target) +{ + ArrayUINT32* a = (ArrayUINT32*)target; + array_uint32_reset(a); + return TRUE; +} + +typedef struct +{ + UINT32 v1; + UINT16 v2; +} BasicStruct; + +static BOOL cb_basic_struct(BasicStruct* v, void* target) +{ + return (v->v1 == 1) && (v->v2 == 2); +} + +POD_ARRAYS_IMPL(BasicStruct, basicstruct) + +int TestPodArrays(int argc, char* argv[]) +{ + int rc = -1; + UINT32 sum = 0; + UINT32 foreach_index = 0; + ArrayUINT32 uint32s = { 0 }; + UINT32* ptr = NULL; + const UINT32* cptr = NULL; + ArrayBasicStruct basicStructs = { 0 }; + BasicStruct basicStruct = { 1, 2 }; + + array_uint32_init(&uint32s); + array_basicstruct_init(&basicStructs); + + for (UINT32 i = 0; i < 10; i++) + if (!array_uint32_append(&uint32s, i)) + goto fail; + + sum = 0; + if (!array_uint32_foreach(&uint32s, cb_compute_sum, &sum)) + goto fail; + + if (sum != 45) + goto fail; + + foreach_index = 0; + if (array_uint32_foreach(&uint32s, cb_stop_at_5, &foreach_index)) + goto fail; + + if (foreach_index != 5) + goto fail; + + if (array_uint32_get(&uint32s, 4) != 4) + goto fail; + + array_uint32_set(&uint32s, 4, 5); + if (array_uint32_get(&uint32s, 4) != 5) + goto fail; + + ptr = array_uint32_data(&uint32s); + if (*ptr != 0) + goto fail; + + cptr = array_uint32_cdata(&uint32s); + if (*cptr != 0) + goto fail; + + /* test modifying values of the array during the foreach */ + if (!array_uint32_foreach(&uint32s, cb_set_to_1, NULL) || array_uint32_get(&uint32s, 5) != 1) + goto fail; + + /* this one is to test that we can modify the array itself during the foreach and that things + * go nicely */ + if (!array_uint32_foreach(&uint32s, cb_reset_after_1, &uint32s) || array_uint32_size(&uint32s)) + goto fail; + + /* give a try with an array of BasicStructs */ + if (!array_basicstruct_append(&basicStructs, basicStruct)) + goto fail; + + if (!array_basicstruct_foreach(&basicStructs, cb_basic_struct, NULL)) + goto fail; + + rc = 0; + +fail: + array_uint32_uninit(&uint32s); + array_basicstruct_uninit(&basicStructs); + + return rc; +} diff --git a/libfreerdp/utils/test/TestRingBuffer.c b/libfreerdp/utils/test/TestRingBuffer.c new file mode 100644 index 0000000..8e88a65 --- /dev/null +++ b/libfreerdp/utils/test/TestRingBuffer.c @@ -0,0 +1,226 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2014 Thincast Technologies GmbH + * Copyright 2014 Hardening <contact@hardening-consulting.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. + */ + +#include <stdio.h> +#include <string.h> + +#include <freerdp/utils/ringbuffer.h> + +static BOOL test_overlaps(void) +{ + RingBuffer rb; + DataChunk chunks[2]; + BYTE bytes[200]; + int nchunks = 0; + int counter = 0; + + for (size_t i = 0; i < sizeof(bytes); i++) + bytes[i] = (BYTE)i; + + ringbuffer_init(&rb, 5); + if (!ringbuffer_write(&rb, bytes, 4)) /* [0123.] */ + goto error; + counter += 4; + ringbuffer_commit_read_bytes(&rb, 2); /* [..23.] */ + + if (!ringbuffer_write(&rb, &bytes[counter], 2)) /* [5.234] */ + goto error; + counter += 2; + + nchunks = ringbuffer_peek(&rb, chunks, 4); + if (nchunks != 2 || chunks[0].size != 3 || chunks[1].size != 1) + goto error; + + for (int x = 0, j = 2; x < nchunks; x++) + { + for (size_t k = 0; k < chunks[x].size; k++, j++) + { + if (chunks[x].data[k] != (BYTE)j) + goto error; + } + } + + ringbuffer_commit_read_bytes(&rb, 3); /* [5....] */ + if (ringbuffer_used(&rb) != 1) + goto error; + + if (!ringbuffer_write(&rb, &bytes[counter], 6)) /* [56789ab....] */ + goto error; + + ringbuffer_commit_read_bytes(&rb, 6); /* [......b....] */ + nchunks = ringbuffer_peek(&rb, chunks, 10); + if (nchunks != 1 || chunks[0].size != 1 || (*chunks[0].data != 0xb)) + goto error; + + if (ringbuffer_capacity(&rb) != 5) + goto error; + + ringbuffer_destroy(&rb); + return TRUE; +error: + ringbuffer_destroy(&rb); + return FALSE; +} + +int TestRingBuffer(int argc, char* argv[]) +{ + RingBuffer ringBuffer; + int testNo = 0; + BYTE* tmpBuf = NULL; + BYTE* rb_ptr = NULL; + DataChunk chunks[2]; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!ringbuffer_init(&ringBuffer, 10)) + { + fprintf(stderr, "unable to initialize ringbuffer\n"); + return -1; + } + + tmpBuf = (BYTE*)malloc(50); + if (!tmpBuf) + return -1; + + for (int i = 0; i < 50; i++) + tmpBuf[i] = (char)i; + + fprintf(stderr, "%d: basic tests...", ++testNo); + if (!ringbuffer_write(&ringBuffer, tmpBuf, 5) || !ringbuffer_write(&ringBuffer, tmpBuf, 5) || + !ringbuffer_write(&ringBuffer, tmpBuf, 5)) + { + fprintf(stderr, "error when writing bytes\n"); + return -1; + } + + if (ringbuffer_used(&ringBuffer) != 15) + { + fprintf(stderr, "invalid used size got %" PRIuz " when I would expect 15\n", + ringbuffer_used(&ringBuffer)); + return -1; + } + + if (ringbuffer_peek(&ringBuffer, chunks, 10) != 1 || chunks[0].size != 10) + { + fprintf(stderr, "error when reading bytes\n"); + return -1; + } + ringbuffer_commit_read_bytes(&ringBuffer, chunks[0].size); + + /* check retrieved bytes */ + for (size_t i = 0; i < chunks[0].size; i++) + { + if (chunks[0].data[i] != i % 5) + { + fprintf(stderr, "invalid byte at %d, got %" PRIu8 " instead of %d\n", i, + chunks[0].data[i], i % 5); + return -1; + } + } + + if (ringbuffer_used(&ringBuffer) != 5) + { + fprintf(stderr, "invalid used size after read got %" PRIuz " when I would expect 5\n", + ringbuffer_used(&ringBuffer)); + return -1; + } + + /* write some more bytes to have writePtr < readPtr and data splitted in 2 chunks */ + if (!ringbuffer_write(&ringBuffer, tmpBuf, 6) || + ringbuffer_peek(&ringBuffer, chunks, 11) != 2 || chunks[0].size != 10 || + chunks[1].size != 1) + { + fprintf(stderr, "invalid read of splitted data\n"); + return -1; + } + + ringbuffer_commit_read_bytes(&ringBuffer, 11); + fprintf(stderr, "ok\n"); + + fprintf(stderr, "%d: peek with nothing to read...", ++testNo); + if (ringbuffer_peek(&ringBuffer, chunks, 10)) + { + fprintf(stderr, "peek returns some chunks\n"); + return -1; + } + fprintf(stderr, "ok\n"); + + fprintf(stderr, "%d: ensure_linear_write / read() shouldn't grow...", ++testNo); + for (int i = 0; i < 1000; i++) + { + rb_ptr = ringbuffer_ensure_linear_write(&ringBuffer, 50); + if (!rb_ptr) + { + fprintf(stderr, "ringbuffer_ensure_linear_write() error\n"); + return -1; + } + + memcpy(rb_ptr, tmpBuf, 50); + + if (!ringbuffer_commit_written_bytes(&ringBuffer, 50)) + { + fprintf(stderr, "ringbuffer_commit_written_bytes() error, i=%d\n", i); + return -1; + } + + // ringbuffer_commit_read_bytes(&ringBuffer, 25); + } + + for (int i = 0; i < 1000; i++) + ringbuffer_commit_read_bytes(&ringBuffer, 25); + + for (int i = 0; i < 1000; i++) + ringbuffer_commit_read_bytes(&ringBuffer, 25); + + if (ringbuffer_capacity(&ringBuffer) != 10) + { + fprintf(stderr, "not the expected capacity, have %" PRIuz " and expects 10\n", + ringbuffer_capacity(&ringBuffer)); + return -1; + } + fprintf(stderr, "ok\n"); + + fprintf(stderr, "%d: free size is correctly computed...", ++testNo); + for (int i = 0; i < 1000; i++) + { + ringbuffer_ensure_linear_write(&ringBuffer, 50); + if (!ringbuffer_commit_written_bytes(&ringBuffer, 50)) + { + fprintf(stderr, "ringbuffer_commit_written_bytes() error, i=%d\n", i); + return -1; + } + } + ringbuffer_commit_read_bytes(&ringBuffer, 50 * 1000); + fprintf(stderr, "ok\n"); + + ringbuffer_destroy(&ringBuffer); + + fprintf(stderr, "%d: specific overlaps test...", ++testNo); + if (!test_overlaps()) + { + fprintf(stderr, "ko\n"); + return -1; + } + fprintf(stderr, "ok\n"); + + ringbuffer_destroy(&ringBuffer); + free(tmpBuf); + return 0; +} |