diff --git a/dlls/crypt32/tests/store.c b/dlls/crypt32/tests/store.c index ee91e73b77daa8c08c2c2ac16d198a3ece167aa4..72a215423275d4f637bf0986b97eec857d40e51b 100644 --- a/dlls/crypt32/tests/store.c +++ b/dlls/crypt32/tests/store.c @@ -2063,6 +2063,362 @@ static void testAddSerialized(void) CertCloseStore(store, 0); } +static void compareStore(HCERTSTORE store, LPCSTR name, const BYTE *pb, + DWORD cb) +{ + BOOL ret; + CRYPT_DATA_BLOB blob = { 0, NULL }; + + ret = CertSaveStore(store, X509_ASN_ENCODING, CERT_STORE_SAVE_AS_STORE, + CERT_STORE_SAVE_TO_MEMORY, &blob, 0); + ok(blob.cbData == cb, "%s: expected size %d, got %d\n", name, cb, + blob.cbData); + blob.pbData = HeapAlloc(GetProcessHeap(), 0, blob.cbData); + if (blob.pbData) + { + ret = CertSaveStore(store, X509_ASN_ENCODING, CERT_STORE_SAVE_AS_STORE, + CERT_STORE_SAVE_TO_MEMORY, &blob, 0); + ok(ret, "CertSaveStore failed: %08x\n", GetLastError()); + ok(!memcmp(pb, blob.pbData, cb), "%s: unexpected value\n", name); + HeapFree(GetProcessHeap(), 0, blob.pbData); + } +} + +static const BYTE serializedCertWithFriendlyName[] = { +0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x57,0x00,0x69, +0x00,0x6e,0x00,0x65,0x00,0x54,0x00,0x65,0x00,0x73,0x00,0x74,0x00,0x00,0x00, +0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x30,0x7a,0x02, +0x01,0x01,0x30,0x02,0x06,0x00,0x30,0x15,0x31,0x13,0x30,0x11,0x06,0x03,0x55, +0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61,0x6e,0x67,0x00,0x30, +0x22,0x18,0x0f,0x31,0x36,0x30,0x31,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30, +0x30,0x30,0x5a,0x18,0x0f,0x31,0x36,0x30,0x31,0x30,0x31,0x30,0x31,0x30,0x30, +0x30,0x30,0x30,0x30,0x5a,0x30,0x15,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04, +0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61,0x6e,0x67,0x00,0x30,0x07, +0x30,0x02,0x06,0x00,0x03,0x01,0x00,0xa3,0x16,0x30,0x14,0x30,0x12,0x06,0x03, +0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x08,0x30,0x06,0x01,0x01,0xff,0x02,0x01, +0x01 }; +static const BYTE serializedStoreWithCertWithFriendlyName[] = { +0x00,0x00,0x00,0x00,0x43,0x45,0x52,0x54,0x0b,0x00,0x00,0x00,0x01,0x00,0x00, +0x00,0x12,0x00,0x00,0x00,0x57,0x00,0x69,0x00,0x6e,0x00,0x65,0x00,0x54,0x00, +0x65,0x00,0x73,0x00,0x74,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00, +0x00,0x7c,0x00,0x00,0x00,0x30,0x7a,0x02,0x01,0x01,0x30,0x02,0x06,0x00,0x30, +0x15,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61, +0x6e,0x20,0x4c,0x61,0x6e,0x67,0x00,0x30,0x22,0x18,0x0f,0x31,0x36,0x30,0x31, +0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x18,0x0f,0x31,0x36, +0x30,0x31,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x30,0x15, +0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e, +0x20,0x4c,0x61,0x6e,0x67,0x00,0x30,0x07,0x30,0x02,0x06,0x00,0x03,0x01,0x00, +0xa3,0x16,0x30,0x14,0x30,0x12,0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04, +0x08,0x30,0x06,0x01,0x01,0xff,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00 }; +static const BYTE serializedStoreWithCertAndHash[] = { +0x00,0x00,0x00,0x00,0x43,0x45,0x52,0x54,0x03,0x00,0x00,0x00,0x01,0x00,0x00, +0x00,0x14,0x00,0x00,0x00,0x6e,0x30,0x90,0x71,0x5f,0xd9,0x23,0x56,0xeb,0xae, +0x25,0x40,0xe6,0x22,0xda,0x19,0x26,0x02,0xa6,0x08,0x20,0x00,0x00,0x00,0x01, +0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x30,0x7a,0x02,0x01,0x01,0x30,0x02,0x06, +0x00,0x30,0x15,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a, +0x75,0x61,0x6e,0x20,0x4c,0x61,0x6e,0x67,0x00,0x30,0x22,0x18,0x0f,0x31,0x36, +0x30,0x31,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x18,0x0f, +0x31,0x36,0x30,0x31,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a, +0x30,0x15,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75, +0x61,0x6e,0x20,0x4c,0x61,0x6e,0x67,0x00,0x30,0x07,0x30,0x02,0x06,0x00,0x03, +0x01,0x00,0xa3,0x16,0x30,0x14,0x30,0x12,0x06,0x03,0x55,0x1d,0x13,0x01,0x01, +0xff,0x04,0x08,0x30,0x06,0x01,0x01,0xff,0x02,0x01,0x01,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + +static void testAddCertificateLink(void) +{ + BOOL ret; + HCERTSTORE store1, store2; + PCCERT_CONTEXT source, linked; + DWORD size; + LPBYTE buf; + CERT_NAME_BLOB blob; + static const WCHAR szPrefix[] = { 'c','e','r',0 }; + static const WCHAR szDot[] = { '.',0 }; + static const WCHAR WineTestW[] = { 'W','i','n','e','T','e','s','t',0 }; + WCHAR filename1[MAX_PATH], filename2[MAX_PATH]; + HANDLE file; + + if (0) + { + /* Crashes, i.e. the store is dereferenced without checking. */ + ret = CertAddCertificateLinkToStore(NULL, NULL, 0, NULL); + } + + /* Adding a certificate link to a store requires a valid add disposition */ + store1 = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, + CERT_STORE_CREATE_NEW_FLAG, NULL); + SetLastError(0xdeadbeef); + ret = CertAddCertificateLinkToStore(store1, NULL, 0, NULL); + todo_wine + ok(!ret && GetLastError() == E_INVALIDARG, + "expected E_INVALIDARG, got %08x\n", GetLastError()); + source = CertCreateCertificateContext(X509_ASN_ENCODING, bigCert, + sizeof(bigCert)); + SetLastError(0xdeadbeef); + ret = CertAddCertificateLinkToStore(store1, source, 0, NULL); + todo_wine + ok(!ret && GetLastError() == E_INVALIDARG, + "expected E_INVALIDARG, got %08x\n", GetLastError()); + ret = CertAddCertificateLinkToStore(store1, source, CERT_STORE_ADD_ALWAYS, + NULL); + todo_wine + ok(ret, "CertAddCertificateLinkToStore failed: %08x\n", GetLastError()); + if (0) + { + /* Crashes, i.e. the source certificate is dereferenced without + * checking when a valid add disposition is given. + */ + ret = CertAddCertificateLinkToStore(store1, NULL, CERT_STORE_ADD_ALWAYS, + NULL); + } + CertCloseStore(store1, 0); + + store1 = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, + CERT_STORE_CREATE_NEW_FLAG, NULL); + ret = CertAddCertificateLinkToStore(store1, source, CERT_STORE_ADD_ALWAYS, + &linked); + todo_wine + ok(ret, "CertAddCertificateLinkToStore failed: %08x\n", GetLastError()); + if (ret) + { + ok(linked->hCertStore == store1, "unexpected store"); + ret = CertSerializeCertificateStoreElement(linked, 0, NULL, &size); + ok(ret, "CertSerializeCertificateStoreElement failed: %08x\n", + GetLastError()); + buf = HeapAlloc(GetProcessHeap(), 0, size); + if (buf) + { + ret = CertSerializeCertificateStoreElement(linked, 0, buf, &size); + /* The serialized linked certificate is identical to the serialized + * original certificate. + */ + ok(size == sizeof(serializedCert), "Wrong size %d\n", size); + ok(!memcmp(serializedCert, buf, size), + "Unexpected serialized cert\n"); + HeapFree(GetProcessHeap(), 0, buf); + } + /* Set a friendly name on the source certificate... */ + blob.pbData = (LPBYTE)WineTestW; + blob.cbData = sizeof(WineTestW); + ret = CertSetCertificateContextProperty(source, + CERT_FRIENDLY_NAME_PROP_ID, 0, &blob); + ok(ret, "CertSetCertificateContextProperty failed: %08x\n", + GetLastError()); + /* and the linked certificate has the same friendly name. */ + ret = CertGetCertificateContextProperty(linked, + CERT_FRIENDLY_NAME_PROP_ID, NULL, &size); + ok(ret, "CertGetCertificateContextProperty failed: %08x\n", + GetLastError()); + buf = HeapAlloc(GetProcessHeap(), 0, size); + if (buf) + { + ret = CertGetCertificateContextProperty(linked, + CERT_FRIENDLY_NAME_PROP_ID, buf, &size); + ok(!lstrcmpW((LPCWSTR)buf, WineTestW), + "unexpected friendly name\n"); + HeapFree(GetProcessHeap(), 0, buf); + } + CertFreeCertificateContext(linked); + } + CertFreeCertificateContext(source); + CertCloseStore(store1, 0); + + /* Test adding a cert to a file store, committing the change to the store, + * and creating a link to the resulting cert. + */ + if (!GetTempFileNameW(szDot, szPrefix, 0, filename1)) + return; + + DeleteFileW(filename1); + file = CreateFileW(filename1, GENERIC_READ | GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file == INVALID_HANDLE_VALUE) + return; + + store1 = CertOpenStore(CERT_STORE_PROV_FILE, 0, 0, + CERT_FILE_STORE_COMMIT_ENABLE_FLAG, file); + ok(store1 != NULL, "CertOpenStore failed: %08x\n", GetLastError()); + CloseHandle(file); + + ret = CertAddEncodedCertificateToStore(store1, X509_ASN_ENCODING, + bigCert, sizeof(bigCert), CERT_STORE_ADD_ALWAYS, &source); + ok(ret, "CertAddEncodedCertificateToStore failed: %08x\n", + GetLastError()); + + /* Test adding a link to a memory store. */ + store2 = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, + CERT_STORE_CREATE_NEW_FLAG, NULL); + ret = CertAddCertificateLinkToStore(store2, source, CERT_STORE_ADD_ALWAYS, + &linked); + todo_wine + ok(ret, "CertAddCertificateLinkToStore failed: %08x\n", GetLastError()); + if (ret) + { + ok(linked->hCertStore == store2, "unexpected store"); + ret = CertSerializeCertificateStoreElement(linked, 0, NULL, &size); + ok(ret, "CertSerializeCertificateStoreElement failed: %08x\n", + GetLastError()); + buf = HeapAlloc(GetProcessHeap(), 0, size); + if (buf) + { + ret = CertSerializeCertificateStoreElement(linked, 0, buf, &size); + /* The serialized linked certificate is identical to the serialized + * original certificate. + */ + ok(size == sizeof(serializedCert), "Wrong size %d\n", size); + ok(!memcmp(serializedCert, buf, size), + "Unexpected serialized cert\n"); + HeapFree(GetProcessHeap(), 0, buf); + } + /* Set a friendly name on the source certificate... */ + blob.pbData = (LPBYTE)WineTestW; + blob.cbData = sizeof(WineTestW); + ret = CertSetCertificateContextProperty(source, + CERT_FRIENDLY_NAME_PROP_ID, 0, &blob); + ok(ret, "CertSetCertificateContextProperty failed: %08x\n", + GetLastError()); + /* and the linked certificate has the same friendly name. */ + ret = CertGetCertificateContextProperty(linked, + CERT_FRIENDLY_NAME_PROP_ID, NULL, &size); + ok(ret, "CertGetCertificateContextProperty failed: %08x\n", + GetLastError()); + buf = HeapAlloc(GetProcessHeap(), 0, size); + if (buf) + { + ret = CertGetCertificateContextProperty(linked, + CERT_FRIENDLY_NAME_PROP_ID, buf, &size); + ok(!lstrcmpW((LPCWSTR)buf, WineTestW), + "unexpected friendly name\n"); + HeapFree(GetProcessHeap(), 0, buf); + } + CertFreeCertificateContext(linked); + } + CertCloseStore(store2, 0); + + if (!GetTempFileNameW(szDot, szPrefix, 0, filename2)) + return; + + DeleteFileW(filename2); + file = CreateFileW(filename2, GENERIC_READ | GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file == INVALID_HANDLE_VALUE) + return; + + store2 = CertOpenStore(CERT_STORE_PROV_FILE, 0, 0, + CERT_FILE_STORE_COMMIT_ENABLE_FLAG, file); + ok(store2 != NULL, "CertOpenStore failed: %08x\n", GetLastError()); + CloseHandle(file); + /* Test adding a link to a file store. */ + ret = CertAddCertificateLinkToStore(store2, source, CERT_STORE_ADD_ALWAYS, + &linked); + todo_wine + ok(ret, "CertAddCertificateLinkToStore failed: %08x\n", GetLastError()); + if (ret) + { + ok(linked->hCertStore == store2, "unexpected store"); + ret = CertSerializeCertificateStoreElement(linked, 0, NULL, &size); + ok(ret, "CertSerializeCertificateStoreElement failed: %08x\n", + GetLastError()); + buf = HeapAlloc(GetProcessHeap(), 0, size); + if (buf) + { + ret = CertSerializeCertificateStoreElement(linked, 0, buf, &size); + /* The serialized linked certificate now contains the friendly + * name property. + */ + ok(size == sizeof(serializedCertWithFriendlyName), + "Wrong size %d\n", size); + ok(!memcmp(serializedCertWithFriendlyName, buf, size), + "Unexpected serialized cert\n"); + HeapFree(GetProcessHeap(), 0, buf); + } + CertFreeCertificateContext(linked); + compareStore(store2, "file store -> file store", + serializedStoreWithCertWithFriendlyName, + sizeof(serializedStoreWithCertWithFriendlyName)); + } + CertCloseStore(store2, 0); + DeleteFileW(filename2); + + CertFreeCertificateContext(source); + + CertCloseStore(store1, 0); + DeleteFileW(filename1); + + /* Test adding a link to a system store (which is a collection store.) */ + store1 = CertOpenSystemStoreA(0, "My"); + source = CertCreateCertificateContext(X509_ASN_ENCODING, bigCert, + sizeof(bigCert)); + SetLastError(0xdeadbeef); + ret = CertAddCertificateLinkToStore(store1, source, CERT_STORE_ADD_ALWAYS, + &linked); + todo_wine + ok(!ret && GetLastError() == E_INVALIDARG, + "expected E_INVALIDARG, got %08x\n", GetLastError()); + CertFreeCertificateContext(source); + + /* Test adding a link to a file store, where the linked certificate is + * in a system store. + */ + ret = CertAddEncodedCertificateToStore(store1, X509_ASN_ENCODING, + bigCert, sizeof(bigCert), CERT_STORE_ADD_ALWAYS, &source); + ok(ret, "CertAddEncodedCertificateToStore failed: %08x\n", + GetLastError()); + if (!GetTempFileNameW(szDot, szPrefix, 0, filename1)) + return; + + DeleteFileW(filename1); + file = CreateFileW(filename1, GENERIC_READ | GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file == INVALID_HANDLE_VALUE) + return; + + store2 = CertOpenStore(CERT_STORE_PROV_FILE, 0, 0, + CERT_FILE_STORE_COMMIT_ENABLE_FLAG, file); + ok(store2 != NULL, "CertOpenStore failed: %08x\n", GetLastError()); + CloseHandle(file); + + ret = CertAddCertificateLinkToStore(store2, source, CERT_STORE_ADD_ALWAYS, + &linked); + todo_wine + ok(ret, "CertAddCertificateLinkToStore failed: %08x\n", GetLastError()); + if (ret) + { + ok(linked->hCertStore == store2, "unexpected store"); + ret = pCertControlStore(store2, 0, CERT_STORE_CTRL_COMMIT, NULL); + ok(ret, "CertControlStore failed: %d\n", ret); + compareStore(store2, "file store -> system store", + serializedStoreWithCertAndHash, + sizeof(serializedStoreWithCertAndHash)); + CertFreeCertificateContext(linked); + } + + CertCloseStore(store2, 0); + DeleteFileW(filename1); + + /* Test adding a link to a registry store, where the linked certificate is + * in a system store. + */ + store2 = CertOpenStore(CERT_STORE_PROV_SYSTEM_REGISTRY, 0, 0, + CERT_SYSTEM_STORE_CURRENT_USER, WineTestW); + ok(store2 != NULL, "CertOpenStore failed: %08x\n", GetLastError()); + ret = CertAddCertificateLinkToStore(store2, source, CERT_STORE_ADD_ALWAYS, + &linked); + todo_wine + ok(ret, "CertAddCertificateLinkToStore failed: %08x\n", GetLastError()); + if (ret) + { + ok(linked->hCertStore == store2, "unexpected store"); + CertDeleteCertificateFromStore(linked); + } + CertCloseStore(store2, 0); + + CertFreeCertificateContext(source); + CertCloseStore(store1, 0); +} + static DWORD countCertsInStore(HCERTSTORE store) { PCCERT_CONTEXT cert = NULL; @@ -2191,6 +2547,7 @@ START_TEST(store) testStoreProperty(); testAddSerialized(); + testAddCertificateLink(); test_I_UpdateStore(); }