From: Guillaume Raffin <theelectronwill@gmail.com> --- dlls/winscard/Makefile.in | 1 + dlls/winscard/tests/Makefile.in | 2 +- dlls/winscard/tests/winscard.c | 199 +++++++++++++++++++++++++++++++- dlls/winscard/winscard.c | 107 ++++++++++++++++- dlls/winscard/winscard.spec | 2 +- include/winscard.h | 11 +- 6 files changed, 312 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..96f1f504f1f 100644 --- a/dlls/winscard/tests/winscard.c +++ b/dlls/winscard/tests/winscard.c @@ -18,10 +18,13 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include <stdio.h> +#include <wchar.h> #include <windows.h> #include <winscard.h> +#include <winnt.h> +#include <winreg.h> + #include "wine/test.h" static void test_SCardEstablishContext(void) @@ -254,7 +257,201 @@ static void test_SCardEstablishContext(void) ok( ret == ERROR_INVALID_HANDLE, "got %lx\n", ret ); } +static LSTATUS create_empty_key(HKEY hkey, LPCSTR name, PHKEY open_key) +{ + HKEY key; + LSTATUS ret; + + ret = RegOpenKeyExA(hkey, name, 0, KEY_READ|KEY_WRITE, &key); + if (ret == ERROR_SUCCESS) { + ret = RegDeleteTreeA(key, NULL); + if (ret != ERROR_SUCCESS) { + return ret; + } + } + ret = RegCreateKeyExA(hkey, name, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, open_key, NULL); + return ret; +} + +const char* SUBKEY_SMARTCARDS_DATABASE = "SOFTWARE\\Microsoft\\Cryptography\\Calais\\SmartCards"; +const char *CARD_NAME_1 = "PKI-test-1"; +const char *CARD_NAME_2 = "PKI-test-2"; +const WCHAR *CARD_NAME_1L = L"PKI-test-1"; +const WCHAR *CARD_NAME_2L = L"PKI-test-2"; +const BYTE CARD_ATR_1[] = {0x3b, 0xda, 0x13, 0xff, 0x81, 0x31, 0xfb, 0x46, 0x80, 0x12, + 0x39, 0x2f, 0x31, 0xc1, 0x73, 0xc6, 0x01, 0xc0, 0x3b}; +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}; + + ret = create_empty_key(HKEY_LOCAL_MACHINE, SUBKEY_SMARTCARDS_DATABASE, &key_db); + ok(ret == ERROR_SUCCESS, "failed to create an empty %s: %ld\n", SUBKEY_SMARTCARDS_DATABASE, ret); + + 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 = 0; /* SCardGetCardTypeProviderName does not need a context */ + DWORD len = 0; + WCHAR *provider; + + /* test basic error conditions */ + provider = malloc(sizeof(WCHAR)); + ret = SCardGetCardTypeProviderNameW(ctx, NULL, SCARD_PROVIDER_CARD_MODULE, provider, &len); + ok(ret == SCARD_E_INVALID_PARAMETER, "should fail when card_type is null\n"); + + ret = SCardGetCardTypeProviderNameW(ctx, CARD_NAME_1L, 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(ctx, CARD_NAME_1L, 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(ctx, CARD_NAME_1L, 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(ctx, CARD_NAME_1L, 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(ctx, CARD_NAME_1L, 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(ctx, CARD_NAME_2L, 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(ctx, CARD_NAME_2L, 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(ctx, CARD_NAME_2L, 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 a context */ + ret = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &ctx); + 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_1L, 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); + + /* test with auto alloc */ + len = SCARD_AUTOALLOCATE; + provider = NULL; + ret = SCardGetCardTypeProviderNameW(ctx, CARD_NAME_1L, 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(ctx, provider); + + len = SCARD_AUTOALLOCATE; + provider = NULL; + ret = SCardGetCardTypeProviderNameW(ctx, CARD_NAME_1L, 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(ctx, provider); + + ret = SCardReleaseContext(ctx); + ok(ret == ERROR_SUCCESS, "failed to release context: error %ld\n", ret); +} + +static void test_smartcard_db(void) +{ + LONG ret; + + ret = populate_smartcard_db(); + ok(ret == ERROR_SUCCESS, "failed to populate database: error %ld\n", ret); + if (ret) return; + + test_SCardGetCardTypeProviderName(); +} + START_TEST(winscard) { test_SCardEstablishContext(); + test_smartcard_db(); } diff --git a/dlls/winscard/winscard.c b/dlls/winscard/winscard.c index 5a92b5110c6..ca826695f84 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,107 @@ 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"; + +/******************************************************************************* + * SCardGetCardTypeProviderNameW (winscard.@) + * + * Return the name of the "provider" that handles the given type of card. + * Depending on the value of `provider_id`, this return either the name of the DLL, crypto provider, or key storage provider. + * + * PARAMS + * context [I] handle of scard context (can be null) + * card_type [I] name of the card type to search for + * provider_id [I] which provider to get (SCARD_PROVIDER_PRIMARY, SCARD_PROVIDER_CSP, SCARD_PROVIDER_KSP or SCARD_PROVIDER_CARD_MODULE) + * out_provider [O] provider name (can be null, even if msdn doesn't mention it) + * inout_provider_len [I/O] length of out_provider in characters, including '\0'. Set this to SCARD_AUTOALLOCATE to allocate out_provider automatically + */ +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 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) */ + if (out_provider == NULL) + { + /* 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