1 /* 2 * Copyright 2004-2007 Juan Lang 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 17 */ 18 19 #include "crypt32_private.h" 20 21 WINE_DEFAULT_DEBUG_CHANNEL(crypt); 22 23 typedef struct _WINE_FILESTOREINFO 24 { 25 DWORD dwOpenFlags; 26 HCERTSTORE memStore; 27 HANDLE file; 28 DWORD type; 29 BOOL dirty; 30 } WINE_FILESTOREINFO; 31 32 static void WINAPI CRYPT_FileCloseStore(HCERTSTORE hCertStore, DWORD dwFlags) 33 { 34 WINE_FILESTOREINFO *store = hCertStore; 35 36 TRACE("(%p, %08x)\n", store, dwFlags); 37 if (store->dirty) 38 CertSaveStore(store->memStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 39 store->type, CERT_STORE_SAVE_TO_FILE, store->file, 0); 40 CloseHandle(store->file); 41 CryptMemFree(store); 42 } 43 44 static BOOL WINAPI CRYPT_FileWriteCert(HCERTSTORE hCertStore, 45 PCCERT_CONTEXT cert, DWORD dwFlags) 46 { 47 WINE_FILESTOREINFO *store = hCertStore; 48 49 TRACE("(%p, %p, %d)\n", hCertStore, cert, dwFlags); 50 store->dirty = TRUE; 51 return TRUE; 52 } 53 54 static BOOL WINAPI CRYPT_FileDeleteCert(HCERTSTORE hCertStore, 55 PCCERT_CONTEXT pCertContext, DWORD dwFlags) 56 { 57 WINE_FILESTOREINFO *store = hCertStore; 58 59 TRACE("(%p, %p, %08x)\n", hCertStore, pCertContext, dwFlags); 60 store->dirty = TRUE; 61 return TRUE; 62 } 63 64 static BOOL WINAPI CRYPT_FileWriteCRL(HCERTSTORE hCertStore, 65 PCCRL_CONTEXT crl, DWORD dwFlags) 66 { 67 WINE_FILESTOREINFO *store = hCertStore; 68 69 TRACE("(%p, %p, %d)\n", hCertStore, crl, dwFlags); 70 store->dirty = TRUE; 71 return TRUE; 72 } 73 74 static BOOL WINAPI CRYPT_FileDeleteCRL(HCERTSTORE hCertStore, 75 PCCRL_CONTEXT pCrlContext, DWORD dwFlags) 76 { 77 WINE_FILESTOREINFO *store = hCertStore; 78 79 TRACE("(%p, %p, %08x)\n", hCertStore, pCrlContext, dwFlags); 80 store->dirty = TRUE; 81 return TRUE; 82 } 83 84 static BOOL WINAPI CRYPT_FileWriteCTL(HCERTSTORE hCertStore, 85 PCCTL_CONTEXT ctl, DWORD dwFlags) 86 { 87 WINE_FILESTOREINFO *store = hCertStore; 88 89 TRACE("(%p, %p, %d)\n", hCertStore, ctl, dwFlags); 90 store->dirty = TRUE; 91 return TRUE; 92 } 93 94 static BOOL WINAPI CRYPT_FileDeleteCTL(HCERTSTORE hCertStore, 95 PCCTL_CONTEXT pCtlContext, DWORD dwFlags) 96 { 97 WINE_FILESTOREINFO *store = hCertStore; 98 99 TRACE("(%p, %p, %08x)\n", hCertStore, pCtlContext, dwFlags); 100 store->dirty = TRUE; 101 return TRUE; 102 } 103 104 static BOOL CRYPT_ReadBlobFromFile(HANDLE file, PCERT_BLOB blob) 105 { 106 BOOL ret = TRUE; 107 108 blob->cbData = GetFileSize(file, NULL); 109 if (blob->cbData) 110 { 111 blob->pbData = CryptMemAlloc(blob->cbData); 112 if (blob->pbData) 113 { 114 DWORD read; 115 116 ret = ReadFile(file, blob->pbData, blob->cbData, &read, NULL) && read == blob->cbData; 117 if (!ret) CryptMemFree(blob->pbData); 118 } 119 else 120 ret = FALSE; 121 } 122 return ret; 123 } 124 125 static BOOL WINAPI CRYPT_FileControl(HCERTSTORE hCertStore, DWORD dwFlags, 126 DWORD dwCtrlType, void const *pvCtrlPara) 127 { 128 WINE_FILESTOREINFO *store = hCertStore; 129 BOOL ret; 130 131 TRACE("(%p, %08x, %d, %p)\n", hCertStore, dwFlags, dwCtrlType, 132 pvCtrlPara); 133 134 switch (dwCtrlType) 135 { 136 case CERT_STORE_CTRL_RESYNC: 137 store->dirty = FALSE; 138 if (store->type == CERT_STORE_SAVE_AS_STORE) 139 { 140 HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 141 CERT_STORE_CREATE_NEW_FLAG, NULL); 142 143 /* FIXME: if I could translate a handle to a path, I could use 144 * CryptQueryObject instead, but there's no API to do so yet. 145 */ 146 ret = CRYPT_ReadSerializedStoreFromFile(store->file, memStore); 147 if (ret) 148 I_CertUpdateStore(store->memStore, memStore, 0, 0); 149 CertCloseStore(memStore, 0); 150 } 151 else if (store->type == CERT_STORE_SAVE_AS_PKCS7) 152 { 153 CERT_BLOB blob = { 0, NULL }; 154 155 ret = CRYPT_ReadBlobFromFile(store->file, &blob); 156 if (ret) 157 { 158 HCERTSTORE messageStore; 159 160 ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, 161 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED, 162 CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, 163 &messageStore, NULL, NULL); 164 I_CertUpdateStore(store->memStore, messageStore, 0, 0); 165 CertCloseStore(messageStore, 0); 166 CryptMemFree(blob.pbData); 167 } 168 } 169 else 170 { 171 WARN("unknown type %d\n", store->type); 172 ret = FALSE; 173 } 174 break; 175 case CERT_STORE_CTRL_COMMIT: 176 if (!(store->dwOpenFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG)) 177 { 178 SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 179 ret = FALSE; 180 } 181 else if (store->dirty) 182 ret = CertSaveStore(store->memStore, 183 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 184 store->type, CERT_STORE_SAVE_TO_FILE, store->file, 0); 185 else 186 ret = TRUE; 187 break; 188 default: 189 FIXME("%d: stub\n", dwCtrlType); 190 ret = FALSE; 191 } 192 return ret; 193 } 194 195 static void *fileProvFuncs[] = { 196 CRYPT_FileCloseStore, 197 NULL, /* CERT_STORE_PROV_READ_CERT_FUNC */ 198 CRYPT_FileWriteCert, 199 CRYPT_FileDeleteCert, 200 NULL, /* CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC */ 201 NULL, /* CERT_STORE_PROV_READ_CRL_FUNC */ 202 CRYPT_FileWriteCRL, 203 CRYPT_FileDeleteCRL, 204 NULL, /* CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC */ 205 NULL, /* CERT_STORE_PROV_READ_CTL_FUNC */ 206 CRYPT_FileWriteCTL, 207 CRYPT_FileDeleteCTL, 208 NULL, /* CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC */ 209 CRYPT_FileControl, 210 }; 211 212 static WINECRYPT_CERTSTORE *CRYPT_CreateFileStore(DWORD dwFlags, 213 HCERTSTORE memStore, HANDLE file, DWORD type) 214 { 215 WINECRYPT_CERTSTORE *store = NULL; 216 WINE_FILESTOREINFO *info = CryptMemAlloc(sizeof(WINE_FILESTOREINFO)); 217 218 if (info) 219 { 220 CERT_STORE_PROV_INFO provInfo = { 0 }; 221 222 info->dwOpenFlags = dwFlags; 223 info->memStore = memStore; 224 info->file = file; 225 info->type = type; 226 info->dirty = FALSE; 227 provInfo.cbSize = sizeof(provInfo); 228 provInfo.cStoreProvFunc = sizeof(fileProvFuncs) / 229 sizeof(fileProvFuncs[0]); 230 provInfo.rgpvStoreProvFunc = fileProvFuncs; 231 provInfo.hStoreProv = info; 232 store = CRYPT_ProvCreateStore(dwFlags, memStore, &provInfo); 233 } 234 return store; 235 } 236 237 WINECRYPT_CERTSTORE *CRYPT_FileOpenStore(HCRYPTPROV hCryptProv, DWORD dwFlags, 238 const void *pvPara) 239 { 240 WINECRYPT_CERTSTORE *store = NULL; 241 HANDLE file = (HANDLE)pvPara; 242 243 TRACE("(%ld, %08x, %p)\n", hCryptProv, dwFlags, pvPara); 244 245 if (!pvPara) 246 { 247 SetLastError(ERROR_INVALID_HANDLE); 248 return NULL; 249 } 250 if (dwFlags & CERT_STORE_DELETE_FLAG) 251 { 252 SetLastError(E_INVALIDARG); 253 return NULL; 254 } 255 if ((dwFlags & CERT_STORE_READONLY_FLAG) && 256 (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG)) 257 { 258 SetLastError(E_INVALIDARG); 259 return NULL; 260 } 261 262 if (DuplicateHandle(GetCurrentProcess(), (HANDLE)pvPara, 263 GetCurrentProcess(), &file, dwFlags & CERT_STORE_READONLY_FLAG ? 264 GENERIC_READ : GENERIC_READ | GENERIC_WRITE, TRUE, 0)) 265 { 266 HCERTSTORE memStore; 267 268 memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 269 CERT_STORE_CREATE_NEW_FLAG, NULL); 270 if (memStore) 271 { 272 if (CRYPT_ReadSerializedStoreFromFile(file, memStore)) 273 { 274 store = CRYPT_CreateFileStore(dwFlags, memStore, file, 275 CERT_STORE_SAVE_AS_STORE); 276 /* File store doesn't need crypto provider, so close it */ 277 if (hCryptProv && 278 !(dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG)) 279 CryptReleaseContext(hCryptProv, 0); 280 } 281 } 282 } 283 TRACE("returning %p\n", store); 284 return store; 285 } 286 287 WINECRYPT_CERTSTORE *CRYPT_FileNameOpenStoreW(HCRYPTPROV hCryptProv, 288 DWORD dwFlags, const void *pvPara) 289 { 290 HCERTSTORE store = 0; 291 LPCWSTR fileName = pvPara; 292 DWORD access, create; 293 HANDLE file; 294 295 TRACE("(%ld, %08x, %s)\n", hCryptProv, dwFlags, debugstr_w(fileName)); 296 297 if (!fileName) 298 { 299 SetLastError(ERROR_PATH_NOT_FOUND); 300 return NULL; 301 } 302 if ((dwFlags & CERT_STORE_READONLY_FLAG) && 303 (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG)) 304 { 305 SetLastError(E_INVALIDARG); 306 return NULL; 307 } 308 309 access = GENERIC_READ; 310 if (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG) 311 access |= GENERIC_WRITE; 312 if (dwFlags & CERT_STORE_CREATE_NEW_FLAG) 313 create = CREATE_NEW; 314 else if (dwFlags & CERT_STORE_OPEN_EXISTING_FLAG) 315 create = OPEN_EXISTING; 316 else 317 create = OPEN_ALWAYS; 318 file = CreateFileW(fileName, access, FILE_SHARE_READ, NULL, create, 319 FILE_ATTRIBUTE_NORMAL, NULL); 320 if (file != INVALID_HANDLE_VALUE) 321 { 322 HCERTSTORE memStore = NULL; 323 DWORD size = GetFileSize(file, NULL), type = 0; 324 325 /* If the file isn't empty, try to get the type from the file itself */ 326 if (size) 327 { 328 DWORD contentType; 329 BOOL ret; 330 331 /* Close the file so CryptQueryObject can succeed.. */ 332 CloseHandle(file); 333 ret = CryptQueryObject(CERT_QUERY_OBJECT_FILE, fileName, 334 CERT_QUERY_CONTENT_FLAG_CERT | 335 CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE | 336 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED, 337 CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &contentType, NULL, 338 &memStore, NULL, NULL); 339 if (ret) 340 { 341 if (contentType == CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED) 342 type = CERT_STORE_SAVE_AS_PKCS7; 343 else 344 type = CERT_STORE_SAVE_AS_STORE; 345 /* and reopen the file. */ 346 file = CreateFileW(fileName, access, FILE_SHARE_READ, NULL, 347 create, FILE_ATTRIBUTE_NORMAL, NULL); 348 } 349 } 350 else 351 { 352 static const WCHAR spc[] = { 's','p','c',0 }; 353 static const WCHAR p7c[] = { 'p','7','c',0 }; 354 LPCWSTR ext = strrchrW(fileName, '.'); 355 356 if (ext) 357 { 358 ext++; 359 if (!lstrcmpiW(ext, spc) || !lstrcmpiW(ext, p7c)) 360 type = CERT_STORE_SAVE_AS_PKCS7; 361 } 362 if (!type) 363 type = CERT_STORE_SAVE_AS_STORE; 364 memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 365 CERT_STORE_CREATE_NEW_FLAG, NULL); 366 } 367 if (memStore) 368 { 369 store = CRYPT_CreateFileStore(dwFlags, memStore, file, type); 370 /* File store doesn't need crypto provider, so close it */ 371 if (hCryptProv && !(dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG)) 372 CryptReleaseContext(hCryptProv, 0); 373 } 374 } 375 return store; 376 } 377 378 WINECRYPT_CERTSTORE *CRYPT_FileNameOpenStoreA(HCRYPTPROV hCryptProv, 379 DWORD dwFlags, const void *pvPara) 380 { 381 int len; 382 WINECRYPT_CERTSTORE *ret = NULL; 383 384 TRACE("(%ld, %08x, %s)\n", hCryptProv, dwFlags, 385 debugstr_a(pvPara)); 386 387 if (!pvPara) 388 { 389 SetLastError(ERROR_FILE_NOT_FOUND); 390 return NULL; 391 } 392 len = MultiByteToWideChar(CP_ACP, 0, pvPara, -1, NULL, 0); 393 if (len) 394 { 395 LPWSTR storeName = CryptMemAlloc(len * sizeof(WCHAR)); 396 397 if (storeName) 398 { 399 MultiByteToWideChar(CP_ACP, 0, pvPara, -1, storeName, len); 400 ret = CRYPT_FileNameOpenStoreW(hCryptProv, dwFlags, storeName); 401 CryptMemFree(storeName); 402 } 403 } 404 return ret; 405 } 406