| /* |
| * Copyright 2004-2007 Juan Lang |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| #include <stdarg.h> |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wincrypt.h" |
| #include "winnls.h" |
| #include "wine/debug.h" |
| #include "wine/unicode.h" |
| #include "crypt32_private.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(crypt); |
| |
| typedef struct _WINE_FILESTOREINFO |
| { |
| DWORD dwOpenFlags; |
| HCERTSTORE memStore; |
| HANDLE file; |
| DWORD type; |
| BOOL dirty; |
| } WINE_FILESTOREINFO; |
| |
| static void WINAPI CRYPT_FileCloseStore(HCERTSTORE hCertStore, DWORD dwFlags) |
| { |
| WINE_FILESTOREINFO *store = hCertStore; |
| |
| TRACE("(%p, %08x)\n", store, dwFlags); |
| if (store->dirty) |
| CertSaveStore(store->memStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, |
| store->type, CERT_STORE_SAVE_TO_FILE, store->file, 0); |
| CloseHandle(store->file); |
| CryptMemFree(store); |
| } |
| |
| static BOOL WINAPI CRYPT_FileWriteCert(HCERTSTORE hCertStore, |
| PCCERT_CONTEXT cert, DWORD dwFlags) |
| { |
| WINE_FILESTOREINFO *store = hCertStore; |
| |
| TRACE("(%p, %p, %d)\n", hCertStore, cert, dwFlags); |
| store->dirty = TRUE; |
| return TRUE; |
| } |
| |
| static BOOL WINAPI CRYPT_FileDeleteCert(HCERTSTORE hCertStore, |
| PCCERT_CONTEXT pCertContext, DWORD dwFlags) |
| { |
| WINE_FILESTOREINFO *store = hCertStore; |
| |
| TRACE("(%p, %p, %08x)\n", hCertStore, pCertContext, dwFlags); |
| store->dirty = TRUE; |
| return TRUE; |
| } |
| |
| static BOOL WINAPI CRYPT_FileWriteCRL(HCERTSTORE hCertStore, |
| PCCRL_CONTEXT crl, DWORD dwFlags) |
| { |
| WINE_FILESTOREINFO *store = hCertStore; |
| |
| TRACE("(%p, %p, %d)\n", hCertStore, crl, dwFlags); |
| store->dirty = TRUE; |
| return TRUE; |
| } |
| |
| static BOOL WINAPI CRYPT_FileDeleteCRL(HCERTSTORE hCertStore, |
| PCCRL_CONTEXT pCrlContext, DWORD dwFlags) |
| { |
| WINE_FILESTOREINFO *store = hCertStore; |
| |
| TRACE("(%p, %p, %08x)\n", hCertStore, pCrlContext, dwFlags); |
| store->dirty = TRUE; |
| return TRUE; |
| } |
| |
| static BOOL WINAPI CRYPT_FileWriteCTL(HCERTSTORE hCertStore, |
| PCCTL_CONTEXT ctl, DWORD dwFlags) |
| { |
| WINE_FILESTOREINFO *store = hCertStore; |
| |
| TRACE("(%p, %p, %d)\n", hCertStore, ctl, dwFlags); |
| store->dirty = TRUE; |
| return TRUE; |
| } |
| |
| static BOOL WINAPI CRYPT_FileDeleteCTL(HCERTSTORE hCertStore, |
| PCCTL_CONTEXT pCtlContext, DWORD dwFlags) |
| { |
| WINE_FILESTOREINFO *store = hCertStore; |
| |
| TRACE("(%p, %p, %08x)\n", hCertStore, pCtlContext, dwFlags); |
| store->dirty = TRUE; |
| return TRUE; |
| } |
| |
| static BOOL CRYPT_ReadBlobFromFile(HANDLE file, PCERT_BLOB blob) |
| { |
| BOOL ret = TRUE; |
| |
| blob->cbData = GetFileSize(file, NULL); |
| if (blob->cbData) |
| { |
| blob->pbData = CryptMemAlloc(blob->cbData); |
| if (blob->pbData) |
| { |
| DWORD read; |
| |
| ret = ReadFile(file, blob->pbData, blob->cbData, &read, NULL) && read == blob->cbData; |
| if (!ret) CryptMemFree(blob->pbData); |
| } |
| else |
| ret = FALSE; |
| } |
| return ret; |
| } |
| |
| static BOOL WINAPI CRYPT_FileControl(HCERTSTORE hCertStore, DWORD dwFlags, |
| DWORD dwCtrlType, void const *pvCtrlPara) |
| { |
| WINE_FILESTOREINFO *store = hCertStore; |
| BOOL ret; |
| |
| TRACE("(%p, %08x, %d, %p)\n", hCertStore, dwFlags, dwCtrlType, |
| pvCtrlPara); |
| |
| switch (dwCtrlType) |
| { |
| case CERT_STORE_CTRL_RESYNC: |
| store->dirty = FALSE; |
| if (store->type == CERT_STORE_SAVE_AS_STORE) |
| { |
| HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, |
| CERT_STORE_CREATE_NEW_FLAG, NULL); |
| |
| /* FIXME: if I could translate a handle to a path, I could use |
| * CryptQueryObject instead, but there's no API to do so yet. |
| */ |
| ret = CRYPT_ReadSerializedStoreFromFile(store->file, memStore); |
| if (ret) |
| I_CertUpdateStore(store->memStore, memStore, 0, 0); |
| CertCloseStore(memStore, 0); |
| } |
| else if (store->type == CERT_STORE_SAVE_AS_PKCS7) |
| { |
| CERT_BLOB blob = { 0, NULL }; |
| |
| ret = CRYPT_ReadBlobFromFile(store->file, &blob); |
| if (ret) |
| { |
| HCERTSTORE messageStore; |
| |
| ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, |
| CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED, |
| CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, |
| &messageStore, NULL, NULL); |
| I_CertUpdateStore(store->memStore, messageStore, 0, 0); |
| CertCloseStore(messageStore, 0); |
| CryptMemFree(blob.pbData); |
| } |
| } |
| else |
| { |
| WARN("unknown type %d\n", store->type); |
| ret = FALSE; |
| } |
| break; |
| case CERT_STORE_CTRL_COMMIT: |
| if (!(store->dwOpenFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG)) |
| { |
| SetLastError(ERROR_CALL_NOT_IMPLEMENTED); |
| ret = FALSE; |
| } |
| else if (store->dirty) |
| ret = CertSaveStore(store->memStore, |
| X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, |
| store->type, CERT_STORE_SAVE_TO_FILE, store->file, 0); |
| else |
| ret = TRUE; |
| break; |
| default: |
| FIXME("%d: stub\n", dwCtrlType); |
| ret = FALSE; |
| } |
| return ret; |
| } |
| |
| static void *fileProvFuncs[] = { |
| CRYPT_FileCloseStore, |
| NULL, /* CERT_STORE_PROV_READ_CERT_FUNC */ |
| CRYPT_FileWriteCert, |
| CRYPT_FileDeleteCert, |
| NULL, /* CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC */ |
| NULL, /* CERT_STORE_PROV_READ_CRL_FUNC */ |
| CRYPT_FileWriteCRL, |
| CRYPT_FileDeleteCRL, |
| NULL, /* CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC */ |
| NULL, /* CERT_STORE_PROV_READ_CTL_FUNC */ |
| CRYPT_FileWriteCTL, |
| CRYPT_FileDeleteCTL, |
| NULL, /* CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC */ |
| CRYPT_FileControl, |
| }; |
| |
| static WINECRYPT_CERTSTORE *CRYPT_CreateFileStore(DWORD dwFlags, |
| HCERTSTORE memStore, HANDLE file, DWORD type) |
| { |
| WINECRYPT_CERTSTORE *store = NULL; |
| WINE_FILESTOREINFO *info = CryptMemAlloc(sizeof(WINE_FILESTOREINFO)); |
| |
| if (info) |
| { |
| CERT_STORE_PROV_INFO provInfo = { 0 }; |
| |
| info->dwOpenFlags = dwFlags; |
| info->memStore = memStore; |
| info->file = file; |
| info->type = type; |
| info->dirty = FALSE; |
| provInfo.cbSize = sizeof(provInfo); |
| provInfo.cStoreProvFunc = sizeof(fileProvFuncs) / |
| sizeof(fileProvFuncs[0]); |
| provInfo.rgpvStoreProvFunc = fileProvFuncs; |
| provInfo.hStoreProv = info; |
| store = CRYPT_ProvCreateStore(dwFlags, memStore, &provInfo); |
| } |
| return store; |
| } |
| |
| WINECRYPT_CERTSTORE *CRYPT_FileOpenStore(HCRYPTPROV hCryptProv, DWORD dwFlags, |
| const void *pvPara) |
| { |
| WINECRYPT_CERTSTORE *store = NULL; |
| HANDLE file = (HANDLE)pvPara; |
| |
| TRACE("(%ld, %08x, %p)\n", hCryptProv, dwFlags, pvPara); |
| |
| if (!pvPara) |
| { |
| SetLastError(ERROR_INVALID_HANDLE); |
| return NULL; |
| } |
| if (dwFlags & CERT_STORE_DELETE_FLAG) |
| { |
| SetLastError(E_INVALIDARG); |
| return NULL; |
| } |
| if ((dwFlags & CERT_STORE_READONLY_FLAG) && |
| (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG)) |
| { |
| SetLastError(E_INVALIDARG); |
| return NULL; |
| } |
| |
| if (DuplicateHandle(GetCurrentProcess(), (HANDLE)pvPara, |
| GetCurrentProcess(), &file, dwFlags & CERT_STORE_READONLY_FLAG ? |
| GENERIC_READ : GENERIC_READ | GENERIC_WRITE, TRUE, 0)) |
| { |
| HCERTSTORE memStore; |
| |
| memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, |
| CERT_STORE_CREATE_NEW_FLAG, NULL); |
| if (memStore) |
| { |
| if (CRYPT_ReadSerializedStoreFromFile(file, memStore)) |
| { |
| store = CRYPT_CreateFileStore(dwFlags, memStore, file, |
| CERT_STORE_SAVE_AS_STORE); |
| /* File store doesn't need crypto provider, so close it */ |
| if (hCryptProv && |
| !(dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG)) |
| CryptReleaseContext(hCryptProv, 0); |
| } |
| } |
| } |
| TRACE("returning %p\n", store); |
| return store; |
| } |
| |
| WINECRYPT_CERTSTORE *CRYPT_FileNameOpenStoreW(HCRYPTPROV hCryptProv, |
| DWORD dwFlags, const void *pvPara) |
| { |
| HCERTSTORE store = 0; |
| LPCWSTR fileName = pvPara; |
| DWORD access, create; |
| HANDLE file; |
| |
| TRACE("(%ld, %08x, %s)\n", hCryptProv, dwFlags, debugstr_w(fileName)); |
| |
| if (!fileName) |
| { |
| SetLastError(ERROR_PATH_NOT_FOUND); |
| return NULL; |
| } |
| if ((dwFlags & CERT_STORE_READONLY_FLAG) && |
| (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG)) |
| { |
| SetLastError(E_INVALIDARG); |
| return NULL; |
| } |
| |
| access = GENERIC_READ; |
| if (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG) |
| access |= GENERIC_WRITE; |
| if (dwFlags & CERT_STORE_CREATE_NEW_FLAG) |
| create = CREATE_NEW; |
| else if (dwFlags & CERT_STORE_OPEN_EXISTING_FLAG) |
| create = OPEN_EXISTING; |
| else |
| create = OPEN_ALWAYS; |
| file = CreateFileW(fileName, access, FILE_SHARE_READ, NULL, create, |
| FILE_ATTRIBUTE_NORMAL, NULL); |
| if (file != INVALID_HANDLE_VALUE) |
| { |
| HCERTSTORE memStore = NULL; |
| DWORD size = GetFileSize(file, NULL), type = 0; |
| |
| /* If the file isn't empty, try to get the type from the file itself */ |
| if (size) |
| { |
| DWORD contentType; |
| BOOL ret; |
| |
| /* Close the file so CryptQueryObject can succeed.. */ |
| CloseHandle(file); |
| ret = CryptQueryObject(CERT_QUERY_OBJECT_FILE, fileName, |
| CERT_QUERY_CONTENT_FLAG_CERT | |
| CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE | |
| CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED, |
| CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &contentType, NULL, |
| &memStore, NULL, NULL); |
| if (ret) |
| { |
| if (contentType == CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED) |
| type = CERT_STORE_SAVE_AS_PKCS7; |
| else |
| type = CERT_STORE_SAVE_AS_STORE; |
| /* and reopen the file. */ |
| file = CreateFileW(fileName, access, FILE_SHARE_READ, NULL, |
| create, FILE_ATTRIBUTE_NORMAL, NULL); |
| } |
| } |
| else |
| { |
| static const WCHAR spc[] = { 's','p','c',0 }; |
| static const WCHAR p7c[] = { 'p','7','c',0 }; |
| LPCWSTR ext = strrchrW(fileName, '.'); |
| |
| if (ext) |
| { |
| ext++; |
| if (!lstrcmpiW(ext, spc) || !lstrcmpiW(ext, p7c)) |
| type = CERT_STORE_SAVE_AS_PKCS7; |
| } |
| if (!type) |
| type = CERT_STORE_SAVE_AS_STORE; |
| memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, |
| CERT_STORE_CREATE_NEW_FLAG, NULL); |
| } |
| if (memStore) |
| { |
| store = CRYPT_CreateFileStore(dwFlags, memStore, file, type); |
| /* File store doesn't need crypto provider, so close it */ |
| if (hCryptProv && !(dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG)) |
| CryptReleaseContext(hCryptProv, 0); |
| } |
| } |
| return store; |
| } |
| |
| WINECRYPT_CERTSTORE *CRYPT_FileNameOpenStoreA(HCRYPTPROV hCryptProv, |
| DWORD dwFlags, const void *pvPara) |
| { |
| int len; |
| WINECRYPT_CERTSTORE *ret = NULL; |
| |
| TRACE("(%ld, %08x, %s)\n", hCryptProv, dwFlags, |
| debugstr_a(pvPara)); |
| |
| if (!pvPara) |
| { |
| SetLastError(ERROR_FILE_NOT_FOUND); |
| return NULL; |
| } |
| len = MultiByteToWideChar(CP_ACP, 0, pvPara, -1, NULL, 0); |
| if (len) |
| { |
| LPWSTR storeName = CryptMemAlloc(len * sizeof(WCHAR)); |
| |
| if (storeName) |
| { |
| MultiByteToWideChar(CP_ACP, 0, pvPara, -1, storeName, len); |
| ret = CRYPT_FileNameOpenStoreW(hCryptProv, dwFlags, storeName); |
| CryptMemFree(storeName); |
| } |
| } |
| return ret; |
| } |