From: Guillaume Raffin <theelectronwill@gmail.com> --- dlls/winscard/Makefile.in | 1 + dlls/winscard/tests/Makefile.in | 2 +- dlls/winscard/tests/winscard.c | 256 +++++++++++++++++++++++++++++++- dlls/winscard/winscard.c | 98 +++++++++++- dlls/winscard/winscard.spec | 2 +- include/winscard.h | 11 +- 6 files changed, 360 insertions(+), 10 deletions(-) diff --git a/dlls/winscard/Makefile.in b/dlls/winscard/Makefile.in index c49b52626d6..5474a15e6ef 100644 --- a/dlls/winscard/Makefile.in +++ b/dlls/winscard/Makefile.in @@ -1,5 +1,6 @@ MODULE = winscard.dll IMPORTLIB = winscard +IMPORTS = kernelbase UNIXLIB = winscard.so UNIX_LIBS = $(PCSCLITE_LIBS) diff --git a/dlls/winscard/tests/Makefile.in b/dlls/winscard/tests/Makefile.in index efc5d6d6e25..faa68059404 100644 --- a/dlls/winscard/tests/Makefile.in +++ b/dlls/winscard/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = winscard.dll -IMPORTS = winscard +IMPORTS = winscard kernelbase SOURCES = \ winscard.c diff --git a/dlls/winscard/tests/winscard.c b/dlls/winscard/tests/winscard.c index 63a1d8f4faa..81934e8b496 100644 --- a/dlls/winscard/tests/winscard.c +++ b/dlls/winscard/tests/winscard.c @@ -18,7 +18,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include <stdio.h> #include <windows.h> #include <winscard.h> @@ -254,7 +253,262 @@ static void test_SCardEstablishContext(void) ok( ret == ERROR_INVALID_HANDLE, "got %lx\n", ret ); } +static const char *subkey_smartcards_database = "SOFTWARE\\Microsoft\\Cryptography\\Calais\\SmartCards"; +static const char *card_name_1 = "PKI-test-1"; +static const char *card_name_2 = "PKI-test-2"; +static const WCHAR *card_name_1W = L"PKI-test-1"; +static const WCHAR *card_name_2W = L"PKI-test-2"; +static const BYTE card_atr_1[] = { + 0x3b, 0xda, 0x13, 0xff, 0x81, 0x31, 0xfb, 0x46, 0x80, 0x12, 0x39, 0x2f, 0x31, 0xc1, 0x73, 0xc6, 0x01, 0xc0, 0x3b +}; +static const BYTE card_atr_2[] = { 0x3b, 0xd2, 0x18, 0x00, 0x81, 0x31, 0xfe, 0x58, 0xC9, 0x03, 0x16 }; + +static LONG populate_smartcard_db(void) +{ + HKEY key_db, key_card1, key_card2; + LONG ret; + + const char *crypto_provider_1 = "Microsoft Base Smart Card Crypto Provider"; + const char *crypto_provider_2 = "Test Crypto Provider"; + const char *storage_provider_1 = "Microsoft Smart Card Key Storage Provider"; + const char *storage_provider_2 = "Test Key Storage Provider"; + const char *dll_1 = "opensc-driver.dll"; + const char *dll_2 = "cardoscm64.dll"; + const BYTE atr_mask_2[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16 }; + + /* open the smartcard db, create it if needed */ + ret = RegCreateKeyExA( + HKEY_LOCAL_MACHINE, subkey_smartcards_database, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &key_db, NULL); + ok(ret == ERROR_SUCCESS, "failed to open HKLM\\%s: %ld\n", subkey_smartcards_database, ret); + if (ret) return ret; + + /* delete the test keys, in case they have not been deleted properly by the previous test run */ + RegDeleteTreeA(key_db, card_name_1); + RegDeleteTreeA(key_db, card_name_2); + + /* create the test keys */ + ret = RegCreateKeyExA(key_db, card_name_1, 0, NULL, 0, KEY_WRITE, NULL, &key_card1, NULL); + if (ret) return ret; + ret = RegCreateKeyExA(key_db, card_name_2, 0, NULL, 0, KEY_WRITE, NULL, &key_card2, NULL); + if (ret) return ret; + + ret = RegSetValueExA( + key_card1, "Crypto Provider", 0, REG_SZ, (const BYTE *)crypto_provider_1, strlen(crypto_provider_1)); + if (ret) return ret; + ret = RegSetValueExA( + key_card2, "Crypto Provider", 0, REG_SZ, (const BYTE *)crypto_provider_2, strlen(crypto_provider_2)); + if (ret) return ret; + + ret = RegSetValueExA(key_card1, + "Smart Card Key Storage Provider", + 0, + REG_SZ, + (const BYTE *)storage_provider_1, + strlen(storage_provider_1)); + if (ret) return ret; + ret = RegSetValueExA(key_card2, + "Smart Card Key Storage Provider", + 0, + REG_SZ, + (const BYTE *)storage_provider_2, + strlen(storage_provider_2)); + if (ret) return ret; + + ret = RegSetValueExA(key_card1, "80000001", 0, REG_SZ, (const BYTE *)dll_1, strlen(dll_1)); + if (ret) return ret; + ret = RegSetValueExA(key_card2, "80000001", 0, REG_SZ, (const BYTE *)dll_2, strlen(dll_2)); + if (ret) return ret; + + ret = RegSetValueExA(key_card1, "ATR", 0, REG_BINARY, (const BYTE *)card_atr_1, sizeof(card_atr_1)); + if (ret) return ret; + ret = RegSetValueExA(key_card2, "ATR", 0, REG_BINARY, (const BYTE *)card_atr_2, sizeof(card_atr_2)); + if (ret) return ret; + + /* don't create ATRMask for the first card, it will default to a mask full of ones */ + ret = RegSetValueExA(key_card2, "ATRMask", 0, REG_BINARY, (const BYTE *)atr_mask_2, sizeof(atr_mask_2)); + if (ret) return ret; + + ret = RegCloseKey(key_card1); + if (ret) return ret; + ret = RegCloseKey(key_card2); + if (ret) return ret; + ret = RegCloseKey(key_db); + if (ret) return ret; + + return 0; +} + +static void test_SCardGetCardTypeProviderName(void) +{ + LONG ret; + SCARDCONTEXT ctx; + DWORD len = 0; + WCHAR *provider; + + /* test basic error conditions */ + provider = malloc(sizeof(WCHAR)); + ret = SCardGetCardTypeProviderNameW(0, NULL, SCARD_PROVIDER_CARD_MODULE, provider, &len); + ok(ret == SCARD_E_INVALID_PARAMETER, "should fail when card_type is null\n"); + + ret = SCardGetCardTypeProviderNameW(0, card_name_1W, SCARD_PROVIDER_CARD_MODULE, provider, NULL); + ok(ret == SCARD_E_INVALID_PARAMETER, "should fail when length is null\n"); + free(provider); + + /* test get length and allocate */ + ret = SCardGetCardTypeProviderNameW(0, card_name_1W, SCARD_PROVIDER_CARD_MODULE, NULL, &len); + ok(len == 18, "invalid length: got %lu, expected %lu\n", len, 18L); + provider = calloc(len, sizeof(WCHAR)); + ret = SCardGetCardTypeProviderNameW(0, card_name_1W, SCARD_PROVIDER_CARD_MODULE, provider, &len); + ok(ret == ERROR_SUCCESS, "SCardGetCardTypeProviderNameW returned an error: %#lx\n", ret); + ok(len == 18, "invalid length: got %lu, expected %lu\n", len, 18L); + ok(wcscmp(provider, L"opensc-driver.dll") == 0, + "bad output of SCardGetCardTypeProviderNameW: '%ls' (len %ld)\n", + provider, + len); + free(provider); + + /* test lookup with a pre-allocated space */ + len = 4; /* too small */ + provider = calloc(len, sizeof(WCHAR)); + ret = SCardGetCardTypeProviderNameW(0, card_name_1W, SCARD_PROVIDER_CARD_MODULE, provider, &len); + ok(ret == SCARD_E_INSUFFICIENT_BUFFER, + "should have failed with SCARD_E_INSUFFICIENT_BUFFER but returned %#lx\n", + ret); + ok(len == 4, "the length should not have been set, but was %ld\n", len); + ok(provider[0] == 0, "the provider should not have been set, but was %s\n", debugstr_w(provider)); + free(provider); + + len = 32; /* ok */ + provider = malloc(len * sizeof(WCHAR)); + for (int i = 0; i < 32; i++) { provider[i] = 0xcafe; } + ret = SCardGetCardTypeProviderNameW(0, card_name_1W, SCARD_PROVIDER_CARD_MODULE, provider, &len); + ok(ret == ERROR_SUCCESS, "SCardGetCardTypeProviderNameW returned an error: %#lx\n", ret); + ok(wcscmp(provider, L"opensc-driver.dll") == 0, + "bad output of SCardGetCardTypeProviderNameW: '%ls' (len %ld)\n", + provider, + len); + for (int i = len; i < 32; i++) + { + ok(provider[i] == 0xcafe, "memory corruption: provider[%d] = %u\n", i, provider[i]); + } + free(provider); + + len = 32; + provider = calloc(len, sizeof(WCHAR)); + ret = SCardGetCardTypeProviderNameW(0, card_name_2W, SCARD_PROVIDER_CARD_MODULE, provider, &len); + ok(ret == ERROR_SUCCESS, "SCardGetCardTypeProviderNameW returned an error: %#lx\n", ret); + ok(wcscmp(provider, L"cardoscm64.dll") == 0, + "bad output of SCardGetCardTypeProviderNameW: '%ls' (len %ld)\n", + provider, + len); + free(provider); + + len = 32; + provider = calloc(len, sizeof(WCHAR)); + ret = SCardGetCardTypeProviderNameW(0, card_name_2W, SCARD_PROVIDER_KSP, provider, &len); + ok(ret == ERROR_SUCCESS, "SCardGetCardTypeProviderNameW returned an error: %#lx\n", ret); + ok(wcscmp(provider, L"Test Key Storage Provider") == 0, + "bad output of SCardGetCardTypeProviderNameW: '%ls' (len %ld)\n", + provider, + len); + free(provider); + + len = 32; + provider = calloc(len, sizeof(WCHAR)); + ret = SCardGetCardTypeProviderNameW(0, card_name_2W, SCARD_PROVIDER_CSP, provider, &len); + ok(ret == ERROR_SUCCESS, "SCardGetCardTypeProviderNameW returned an error: %#lx\n", ret); + ok(wcscmp(provider, L"Test Crypto Provider") == 0, + "bad output of SCardGetCardTypeProviderNameW: '%ls' (len %ld)\n", + provider, + len); + free(provider); + + /* test with auto alloc and SCARD_PROVIDER_CARD_MODULE */ + len = SCARD_AUTOALLOCATE; + provider = NULL; + ret = SCardGetCardTypeProviderNameW(0, card_name_1W, SCARD_PROVIDER_CARD_MODULE, (LPWSTR)&provider, &len); + ok(ret == ERROR_SUCCESS, "SCardGetCardTypeProviderNameW returned an error: %#lx\n", ret); + ok(wcscmp(provider, L"opensc-driver.dll") == 0, + "bad output of SCardGetCardTypeProviderNameW: '%ls' (len %ld)\n", + provider, + len); + ok(len == wcslen(provider) + 1, + "bad length from SCardGetCardTypeProviderNameW: got %lu, expected %Iu\n", + len, + wcslen(provider) + 1); + SCardFreeMemory(0, provider); + + /* test with auto alloc and SCARD_PROVIDER_CSP */ + len = SCARD_AUTOALLOCATE; + provider = NULL; + ret = SCardGetCardTypeProviderNameW(0, card_name_1W, SCARD_PROVIDER_CSP, (LPWSTR)&provider, &len); + ok(ret == ERROR_SUCCESS, "SCardGetCardTypeProviderNameW returned an error: %#lx\n", ret); + ok(wcscmp(provider, L"Microsoft Base Smart Card Crypto Provider") == 0, + "bad output of SCardGetCardTypeProviderNameW: '%ls' (len %ld)\n", + provider, + len); + ok(len == wcslen(provider) + 1, + "bad length from SCardGetCardTypeProviderNameW: got %lu, expected %Iu\n", + len, + wcslen(provider) + 1); + SCardFreeMemory(0, provider); + + /* test with a context */ + ret = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &ctx); + if (ret == SCARD_E_NO_SERVICE) + { + skip( "can't establish context, make sure pcscd is running\n" ); + return; + } + ok(ret == ERROR_SUCCESS, "failed to establish context: error %ld\n", ret); + if (ret) return; + + len = 32; + provider = calloc(len, sizeof(WCHAR)); + ret = SCardGetCardTypeProviderNameW(ctx, card_name_1W, SCARD_PROVIDER_CARD_MODULE, provider, &len); + ok(ret == ERROR_SUCCESS, "SCardGetCardTypeProviderNameW returned an error: %#lx\n", ret); + ok(wcscmp(provider, L"opensc-driver.dll") == 0, + "bad output of SCardGetCardTypeProviderNameW: '%ls' (len %ld)\n", + provider, + len); + free(provider); + + ret = SCardReleaseContext(ctx); + ok(ret == ERROR_SUCCESS, "failed to release context: error %ld\n", ret); +} + +static void test_smartcard_db(void) +{ + LONG ret; + HKEY key; + + /* prepare the database */ + ret = populate_smartcard_db(); + ok(ret == ERROR_SUCCESS, "failed to populate database: error %ld\n", ret); + if (ret) return; + + /* run the tests */ + test_SCardGetCardTypeProviderName(); + + /* delete the test keys */ + ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey_smartcards_database, 0, KEY_READ | KEY_WRITE, &key); + ok(ret == ERROR_SUCCESS, "failed to open the smartcard database: error %ld\n", ret); + ret = RegDeleteTreeA(key, card_name_1); + ok(ret == ERROR_SUCCESS, + "failed to delete HKLM\\%s\\%s: error %ld\n", + subkey_smartcards_database, + card_name_1, + ret); + ret = RegDeleteTreeA(key, card_name_2); + ok(ret == ERROR_SUCCESS, + "failed to delete HKLM\\%s\\%s: error %ld\n", + subkey_smartcards_database, + card_name_1, + ret); +} + START_TEST(winscard) { test_SCardEstablishContext(); + test_smartcard_db(); } diff --git a/dlls/winscard/winscard.c b/dlls/winscard/winscard.c index 5a92b5110c6..16f7d04636b 100644 --- a/dlls/winscard/winscard.c +++ b/dlls/winscard/winscard.c @@ -17,12 +17,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include <stdarg.h> #define WINSCARDAPI +#include "assert.h" +#include <stdarg.h> +#include "winerror.h" #include "windef.h" #include "winbase.h" +#include "winreg.h" #include "winscard.h" -#include "winternl.h" #include "wine/debug.h" #include "wine/unixlib.h" @@ -966,6 +968,98 @@ LONG WINAPI SCardFreeMemory( SCARDCONTEXT context, const void *mem ) return SCARD_S_SUCCESS; } +/* subkey of the db in HKLM */ +const WCHAR* SUBKEY_SMARTCARDS_DATABASE = L"SOFTWARE\\Microsoft\\Cryptography\\Calais\\SmartCards"; + +/* The ATR is between 2 and 33 bytes, but winscard pads it to 36 bytes (see struct SCARD_READERSTATEW) */ +#define ATR_N_BYTES 36 + +LONG WINAPI SCardGetCardTypeProviderNameW( + SCARDCONTEXT context, const WCHAR *card_type, DWORD provider_id, WCHAR *out_provider, DWORD *inout_provider_len) +{ + struct handle *handle = (struct handle *)context; + HKEY key; + LONG ret; + DWORD value_len_wchars; + DWORD value_len_bytes = MAX_PATH * sizeof(WCHAR); + WCHAR value[MAX_PATH]; + BYTE **new_output; + + TRACE("%Ix, %s, %lu, %p, %p\n", context, debugstr_w(card_type), provider_id, out_provider, inout_provider_len); + + if (!card_type || !inout_provider_len) return SCARD_E_INVALID_PARAMETER; + + if (handle != NULL) + { + if (handle->magic != CONTEXT_MAGIC) + { + return ERROR_INVALID_HANDLE; + } + /* the handle should be used to restrict the visible cards, continue anyway */ + FIXME("card scopes not implemented\n"); + } + + ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, SUBKEY_SMARTCARDS_DATABASE, 0, KEY_READ, &key); + if (ret != ERROR_SUCCESS) + { + WARN("could not open the SmartCard database: error %ld\n", ret); + return SCARD_E_UNKNOWN_CARD; + } + + /* read the value that corresponds to the requested type of provider (given by provider_id) */ + switch (provider_id) + { + case SCARD_PROVIDER_PRIMARY: + FIXME("SCARD_PROVIDER_PRIMARY not implemented\n"); + SetLastError(ERROR_NOT_SUPPORTED); + return SCARD_F_INTERNAL_ERROR; + case SCARD_PROVIDER_CSP: + ret = RegGetValueW(key, card_type, L"Crypto Provider", RRF_RT_REG_SZ, NULL, &value, &value_len_bytes); + break; + case SCARD_PROVIDER_KSP: + ret = RegGetValueW( + key, card_type, L"Smart Card Key Storage Provider", RRF_RT_REG_SZ, NULL, &value, &value_len_bytes); + break; + case SCARD_PROVIDER_CARD_MODULE: + ret = RegGetValueW(key, card_type, L"80000001", RRF_RT_REG_SZ, NULL, &value, &value_len_bytes); + break; + default: return SCARD_E_INVALID_PARAMETER; + } + RegCloseKey(key); + if (ret != ERROR_SUCCESS) return ret; + + /* note: the value includes a trailing zero thanks to RegGetValueW */ + assert(value_len_bytes % sizeof(WCHAR) == 0); + value_len_wchars = value_len_bytes / sizeof(WCHAR); + + /* store the value in out_provider (its length *in wchars* will go in inout_provider_len, including the trailing NUL char) */ + if (out_provider == NULL) + { + /* this is fine (even if it's not clear from msdn), just return the length */ + } + else if (*inout_provider_len == SCARD_AUTOALLOCATE) + { + /* write the value to a new buffer */ + new_output = (BYTE**)out_provider; + *new_output = malloc(value_len_bytes); + if (*new_output == NULL) return ERROR_NOT_ENOUGH_MEMORY; + memcpy(*new_output, value, value_len_bytes); + TRACE("returning %s at %p, length %lu\n", debugstr_w((WCHAR*)*new_output), *new_output, value_len_wchars); + } + else if (*inout_provider_len < value_len_wchars) + { + return SCARD_E_INSUFFICIENT_BUFFER; + } + else + { + /* write the value to out_provider */ + memcpy(out_provider, value, value_len_bytes); + TRACE("returning %s, length %lu\n", debugstr_w(out_provider), value_len_wchars); + } + *inout_provider_len = value_len_wchars; + return SCARD_S_SUCCESS; +} + BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, void *reserved ) { switch (reason) diff --git a/dlls/winscard/winscard.spec b/dlls/winscard/winscard.spec index 34bf1c67950..f5f9deac283 100644 --- a/dlls/winscard/winscard.spec +++ b/dlls/winscard/winscard.spec @@ -22,7 +22,7 @@ @ stdcall SCardFreeMemory(long ptr) @ stdcall SCardGetAttrib(long long ptr ptr) @ stub SCardGetCardTypeProviderNameA -@ stub SCardGetCardTypeProviderNameW +@ stdcall SCardGetCardTypeProviderNameW(long wstr long wstr ptr) @ stub SCardGetProviderIdA @ stub SCardGetProviderIdW @ stdcall SCardGetStatusChangeA(long long ptr long) diff --git a/include/winscard.h b/include/winscard.h index 132972a4a9b..0c214450e1a 100644 --- a/include/winscard.h +++ b/include/winscard.h @@ -28,11 +28,6 @@ #define WINSCARDAPI DECLSPEC_IMPORT #endif -/* Valid scopes for contexts */ -#define SCARD_SCOPE_USER 0 -#define SCARD_SCOPE_TERMINAL 1 -#define SCARD_SCOPE_SYSTEM 2 - #ifndef _LPCBYTE_DEFINED #define _LPCBYTE_DEFINED typedef const BYTE *LPCBYTE; @@ -98,6 +93,12 @@ DECL_WINELIB_TYPE_AW(LPSCARD_READERSTATE) #define SCARD_UNPOWER_CARD 2 #define SCARD_EJECT_CARD 3 +/* constants for SCard{Get,Set}CardTypeProviderName */ +#define SCARD_PROVIDER_PRIMARY 1 +#define SCARD_PROVIDER_CSP 2 +#define SCARD_PROVIDER_KSP 3 +#define SCARD_PROVIDER_CARD_MODULE 0x80000001 + #ifdef __cplusplus extern "C" { #endif -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10751