/* * 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, *PWINE_FILESTOREINFO; static void WINAPI CRYPT_FileCloseStore(HCERTSTORE hCertStore, DWORD dwFlags) { PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)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); CertCloseStore(store->memStore, dwFlags); CloseHandle(store->file); CryptMemFree(store); } static BOOL WINAPI CRYPT_FileWriteCert(HCERTSTORE hCertStore, PCCERT_CONTEXT cert, DWORD dwFlags) { PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)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) { PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)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) { PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)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) { PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore; TRACE("(%p, %p, %08x)\n", hCertStore, pCrlContext, 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); } } return ret; } static BOOL WINAPI CRYPT_FileControl(HCERTSTORE hCertStore, DWORD dwFlags, DWORD dwCtrlType, void const *pvCtrlPara) { PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)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 */ NULL, /* CERT_STORE_PROV_WRITE_CTL_FUNC */ NULL, /* CERT_STORE_PROV_DELETE_CTL_FUNC */ NULL, /* CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC */ CRYPT_FileControl, }; static PWINECRYPT_CERTSTORE CRYPT_CreateFileStore(DWORD dwFlags, HCERTSTORE memStore, HANDLE file, DWORD type) { PWINECRYPT_CERTSTORE store = NULL; PWINE_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; } PWINECRYPT_CERTSTORE CRYPT_FileOpenStore(HCRYPTPROV hCryptProv, DWORD dwFlags, const void *pvPara) { PWINECRYPT_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; } PWINECRYPT_CERTSTORE CRYPT_FileNameOpenStoreW(HCRYPTPROV hCryptProv, DWORD dwFlags, const void *pvPara) { HCERTSTORE store = 0; LPCWSTR fileName = (LPCWSTR)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_BINARY, 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 (PWINECRYPT_CERTSTORE)store; } PWINECRYPT_CERTSTORE CRYPT_FileNameOpenStoreA(HCRYPTPROV hCryptProv, DWORD dwFlags, const void *pvPara) { int len; PWINECRYPT_CERTSTORE ret = NULL; TRACE("(%ld, %08x, %s)\n", hCryptProv, dwFlags, debugstr_a((LPCSTR)pvPara)); if (!pvPara) { SetLastError(ERROR_FILE_NOT_FOUND); return NULL; } len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pvPara, -1, NULL, 0); if (len) { LPWSTR storeName = CryptMemAlloc(len * sizeof(WCHAR)); if (storeName) { MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pvPara, -1, storeName, len); ret = CRYPT_FileNameOpenStoreW(hCryptProv, dwFlags, storeName); CryptMemFree(storeName); } } return ret; }