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 #include <stdarg.h> 19 #include "windef.h" 20 #include "winbase.h" 21 #include "wincrypt.h" 22 #include "wine/debug.h" 23 #include "crypt32_private.h" 24 25 WINE_DEFAULT_DEBUG_CHANNEL(crypt); 26 27 typedef struct _WINE_STORE_LIST_ENTRY 28 { 29 WINECRYPT_CERTSTORE *store; 30 DWORD dwUpdateFlags; 31 DWORD dwPriority; 32 struct list entry; 33 } WINE_STORE_LIST_ENTRY; 34 35 typedef struct _WINE_COLLECTIONSTORE 36 { 37 WINECRYPT_CERTSTORE hdr; 38 CRITICAL_SECTION cs; 39 struct list stores; 40 } WINE_COLLECTIONSTORE; 41 42 static void Collection_addref(WINECRYPT_CERTSTORE *store) 43 { 44 LONG ref = InterlockedIncrement(&store->ref); 45 TRACE("ref = %d\n", ref); 46 } 47 48 static DWORD Collection_release(WINECRYPT_CERTSTORE *store, DWORD flags) 49 { 50 WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; 51 WINE_STORE_LIST_ENTRY *entry, *next; 52 LONG ref; 53 54 if(flags) 55 FIXME("Unimplemented flags %x\n", flags); 56 57 ref = InterlockedDecrement(&cs->hdr.ref); 58 TRACE("(%p) ref=%d\n", store, ref); 59 if(ref) 60 return ERROR_SUCCESS; 61 62 LIST_FOR_EACH_ENTRY_SAFE(entry, next, &cs->stores, WINE_STORE_LIST_ENTRY, entry) 63 { 64 TRACE("closing %p\n", entry); 65 entry->store->vtbl->release(entry->store, flags); 66 CryptMemFree(entry); 67 } 68 cs->cs.DebugInfo->Spare[0] = 0; 69 DeleteCriticalSection(&cs->cs); 70 CRYPT_FreeStore(store); 71 return ERROR_SUCCESS; 72 } 73 74 static void Collection_releaseContext(WINECRYPT_CERTSTORE *store, context_t *context) 75 { 76 /* We don't cache context links, so just free them. */ 77 Context_Free(context); 78 } 79 80 static context_t *CRYPT_CollectionCreateContextFromChild(WINE_COLLECTIONSTORE *store, 81 WINE_STORE_LIST_ENTRY *storeEntry, context_t *child) 82 { 83 context_t *ret; 84 85 ret = child->vtbl->clone(child, &store->hdr, TRUE); 86 if (!ret) 87 return NULL; 88 89 ret->u.ptr = storeEntry; 90 return ret; 91 } 92 93 static BOOL CRYPT_CollectionAddContext(WINE_COLLECTIONSTORE *store, 94 unsigned int contextFuncsOffset, context_t *context, context_t *toReplace, 95 context_t **pChildContext) 96 { 97 BOOL ret; 98 context_t *childContext = NULL; 99 WINE_STORE_LIST_ENTRY *storeEntry = NULL; 100 101 TRACE("(%p, %d, %p, %p)\n", store, contextFuncsOffset, context, toReplace); 102 103 ret = FALSE; 104 if (toReplace) 105 { 106 context_t *existingLinked = toReplace->linked; 107 CONTEXT_FUNCS *contextFuncs; 108 109 storeEntry = toReplace->u.ptr; 110 contextFuncs = (CONTEXT_FUNCS*)((LPBYTE)storeEntry->store->vtbl + 111 contextFuncsOffset); 112 ret = contextFuncs->addContext(storeEntry->store, context, 113 existingLinked, &childContext, TRUE); 114 } 115 else 116 { 117 WINE_STORE_LIST_ENTRY *entry; 118 119 EnterCriticalSection(&store->cs); 120 LIST_FOR_EACH_ENTRY(entry, &store->stores, WINE_STORE_LIST_ENTRY, entry) 121 { 122 if (entry->dwUpdateFlags & CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG) 123 { 124 CONTEXT_FUNCS *contextFuncs = (CONTEXT_FUNCS*)( 125 (LPBYTE)entry->store->vtbl + contextFuncsOffset); 126 127 storeEntry = entry; 128 ret = contextFuncs->addContext(entry->store, context, NULL, &childContext, TRUE); 129 break; 130 } 131 } 132 LeaveCriticalSection(&store->cs); 133 if (!storeEntry) 134 SetLastError(E_ACCESSDENIED); 135 } 136 *pChildContext = childContext; 137 return ret; 138 } 139 140 /* Advances a collection enumeration by one context, if possible, where 141 * advancing means: 142 * - calling the current store's enumeration function once, and returning 143 * the enumerated context if one is returned 144 * - moving to the next store if the current store has no more items, and 145 * recursively calling itself to get the next item. 146 * Returns NULL if the collection contains no more items or on error. 147 * Assumes the collection store's lock is held. 148 */ 149 static context_t *CRYPT_CollectionAdvanceEnum(WINE_COLLECTIONSTORE *store, 150 WINE_STORE_LIST_ENTRY *storeEntry, const CONTEXT_FUNCS *contextFuncs, 151 context_t *prev) 152 { 153 context_t *child, *ret; 154 struct list *storeNext = list_next(&store->stores, &storeEntry->entry); 155 156 TRACE("(%p, %p, %p)\n", store, storeEntry, prev); 157 158 if (prev) 159 { 160 /* Ref-counting funny business: "duplicate" (addref) the child, because 161 * the free(pPrev) below can cause the ref count to become negative. 162 */ 163 child = prev->linked; 164 Context_AddRef(child); 165 child = contextFuncs->enumContext(storeEntry->store, child); 166 Context_Release(prev); 167 } 168 else 169 { 170 child = contextFuncs->enumContext(storeEntry->store, NULL); 171 } 172 if (child) { 173 ret = CRYPT_CollectionCreateContextFromChild(store, storeEntry, child); 174 Context_Release(child); 175 } 176 else 177 { 178 if (storeNext) 179 { 180 /* We always want the same function pointers (from certs, crls) 181 * in the next store, so use the same offset into the next store. 182 */ 183 size_t offset = (const BYTE *)contextFuncs - (LPBYTE)storeEntry->store->vtbl; 184 WINE_STORE_LIST_ENTRY *storeNextEntry = 185 LIST_ENTRY(storeNext, WINE_STORE_LIST_ENTRY, entry); 186 CONTEXT_FUNCS *storeNextContexts = 187 (CONTEXT_FUNCS*)((LPBYTE)storeNextEntry->store->vtbl + offset); 188 189 ret = CRYPT_CollectionAdvanceEnum(store, storeNextEntry, 190 storeNextContexts, NULL); 191 } 192 else 193 { 194 SetLastError(CRYPT_E_NOT_FOUND); 195 ret = NULL; 196 } 197 } 198 TRACE("returning %p\n", ret); 199 return ret; 200 } 201 202 static BOOL Collection_addCert(WINECRYPT_CERTSTORE *store, context_t *cert, 203 context_t *toReplace, context_t **ppStoreContext, BOOL use_link) 204 { 205 BOOL ret; 206 context_t *childContext = NULL; 207 WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; 208 209 ret = CRYPT_CollectionAddContext(cs, offsetof(store_vtbl_t, certs), 210 cert, toReplace, &childContext); 211 if (ppStoreContext && childContext) 212 { 213 WINE_STORE_LIST_ENTRY *storeEntry = childContext->u.ptr; 214 cert_t *context = (cert_t*)CRYPT_CollectionCreateContextFromChild(cs, storeEntry, 215 childContext); 216 217 *ppStoreContext = &context->base; 218 } 219 if (childContext) 220 Context_Release(childContext); 221 return ret; 222 } 223 224 static context_t *Collection_enumCert(WINECRYPT_CERTSTORE *store, context_t *prev) 225 { 226 WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; 227 context_t *ret; 228 229 TRACE("(%p, %p)\n", store, prev); 230 231 EnterCriticalSection(&cs->cs); 232 if (prev) 233 { 234 WINE_STORE_LIST_ENTRY *storeEntry = prev->u.ptr; 235 236 ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, 237 &storeEntry->store->vtbl->certs, prev); 238 } 239 else 240 { 241 if (!list_empty(&cs->stores)) 242 { 243 WINE_STORE_LIST_ENTRY *storeEntry = LIST_ENTRY(cs->stores.next, 244 WINE_STORE_LIST_ENTRY, entry); 245 246 ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, 247 &storeEntry->store->vtbl->certs, NULL); 248 } 249 else 250 { 251 SetLastError(CRYPT_E_NOT_FOUND); 252 ret = NULL; 253 } 254 } 255 LeaveCriticalSection(&cs->cs); 256 TRACE("returning %p\n", ret); 257 return ret; 258 } 259 260 static BOOL Collection_deleteCert(WINECRYPT_CERTSTORE *store, context_t *context) 261 { 262 cert_t *cert = (cert_t*)context; 263 cert_t *linked; 264 265 TRACE("(%p, %p)\n", store, cert); 266 267 linked = (cert_t*)context->linked; 268 return CertDeleteCertificateFromStore(&linked->ctx); 269 } 270 271 static BOOL Collection_addCRL(WINECRYPT_CERTSTORE *store, context_t *crl, 272 context_t *toReplace, context_t **ppStoreContext, BOOL use_link) 273 { 274 BOOL ret; 275 context_t *childContext = NULL; 276 WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; 277 278 ret = CRYPT_CollectionAddContext(cs, offsetof(store_vtbl_t, crls), 279 crl, toReplace, &childContext); 280 if (ppStoreContext && childContext) 281 { 282 WINE_STORE_LIST_ENTRY *storeEntry = childContext->u.ptr; 283 crl_t *context = (crl_t*)CRYPT_CollectionCreateContextFromChild(cs, storeEntry, 284 childContext); 285 286 *ppStoreContext = &context->base; 287 } 288 if (childContext) 289 Context_Release(childContext); 290 return ret; 291 } 292 293 static context_t *Collection_enumCRL(WINECRYPT_CERTSTORE *store, context_t *prev) 294 { 295 WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; 296 context_t *ret; 297 298 TRACE("(%p, %p)\n", store, prev); 299 300 EnterCriticalSection(&cs->cs); 301 if (prev) 302 { 303 WINE_STORE_LIST_ENTRY *storeEntry = prev->u.ptr; 304 305 ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, 306 &storeEntry->store->vtbl->crls, prev); 307 } 308 else 309 { 310 if (!list_empty(&cs->stores)) 311 { 312 WINE_STORE_LIST_ENTRY *storeEntry = LIST_ENTRY(cs->stores.next, 313 WINE_STORE_LIST_ENTRY, entry); 314 315 ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, 316 &storeEntry->store->vtbl->crls, NULL); 317 } 318 else 319 { 320 SetLastError(CRYPT_E_NOT_FOUND); 321 ret = NULL; 322 } 323 } 324 LeaveCriticalSection(&cs->cs); 325 TRACE("returning %p\n", ret); 326 return ret; 327 } 328 329 static BOOL Collection_deleteCRL(WINECRYPT_CERTSTORE *store, context_t *context) 330 { 331 crl_t *crl = (crl_t*)context, *linked; 332 333 TRACE("(%p, %p)\n", store, crl); 334 335 linked = (crl_t*)context->linked; 336 return CertDeleteCRLFromStore(&linked->ctx); 337 } 338 339 static BOOL Collection_addCTL(WINECRYPT_CERTSTORE *store, context_t *ctl, 340 context_t *toReplace, context_t **ppStoreContext, BOOL use_link) 341 { 342 BOOL ret; 343 context_t *childContext = NULL; 344 WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; 345 346 ret = CRYPT_CollectionAddContext(cs, offsetof(store_vtbl_t, ctls), 347 ctl, toReplace, &childContext); 348 if (ppStoreContext && childContext) 349 { 350 WINE_STORE_LIST_ENTRY *storeEntry = childContext->u.ptr; 351 ctl_t *context = (ctl_t*)CRYPT_CollectionCreateContextFromChild(cs, storeEntry, 352 childContext); 353 354 *ppStoreContext = &context->base; 355 } 356 if (childContext) 357 Context_Release(childContext); 358 return ret; 359 } 360 361 static context_t *Collection_enumCTL(WINECRYPT_CERTSTORE *store, context_t *prev) 362 { 363 WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store; 364 void *ret; 365 366 TRACE("(%p, %p)\n", store, prev); 367 368 EnterCriticalSection(&cs->cs); 369 if (prev) 370 { 371 WINE_STORE_LIST_ENTRY *storeEntry = prev->u.ptr; 372 373 ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, 374 &storeEntry->store->vtbl->ctls, prev); 375 } 376 else 377 { 378 if (!list_empty(&cs->stores)) 379 { 380 WINE_STORE_LIST_ENTRY *storeEntry = LIST_ENTRY(cs->stores.next, 381 WINE_STORE_LIST_ENTRY, entry); 382 383 ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry, 384 &storeEntry->store->vtbl->ctls, NULL); 385 } 386 else 387 { 388 SetLastError(CRYPT_E_NOT_FOUND); 389 ret = NULL; 390 } 391 } 392 LeaveCriticalSection(&cs->cs); 393 TRACE("returning %p\n", ret); 394 return ret; 395 } 396 397 static BOOL Collection_deleteCTL(WINECRYPT_CERTSTORE *store, context_t *context) 398 { 399 ctl_t *ctl = (ctl_t*)context, *linked; 400 401 TRACE("(%p, %p)\n", store, ctl); 402 403 linked = (ctl_t*)context->linked; 404 return CertDeleteCTLFromStore(&linked->ctx); 405 } 406 407 static BOOL Collection_control(WINECRYPT_CERTSTORE *cert_store, DWORD dwFlags, 408 DWORD dwCtrlType, void const *pvCtrlPara) 409 { 410 BOOL ret; 411 WINE_COLLECTIONSTORE *store = (WINE_COLLECTIONSTORE*)cert_store; 412 WINE_STORE_LIST_ENTRY *entry; 413 414 TRACE("(%p, %08x, %d, %p)\n", cert_store, dwFlags, dwCtrlType, pvCtrlPara); 415 416 if (!store) 417 return TRUE; 418 if (store->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC) 419 { 420 SetLastError(E_INVALIDARG); 421 return FALSE; 422 } 423 if (store->hdr.type != StoreTypeCollection) 424 { 425 SetLastError(E_INVALIDARG); 426 return FALSE; 427 } 428 429 ret = TRUE; 430 EnterCriticalSection(&store->cs); 431 LIST_FOR_EACH_ENTRY(entry, &store->stores, WINE_STORE_LIST_ENTRY, entry) 432 { 433 if (entry->store->vtbl->control) 434 { 435 ret = entry->store->vtbl->control(entry->store, dwFlags, dwCtrlType, pvCtrlPara); 436 if (!ret) 437 break; 438 } 439 } 440 LeaveCriticalSection(&store->cs); 441 return ret; 442 } 443 444 static const store_vtbl_t CollectionStoreVtbl = { 445 Collection_addref, 446 Collection_release, 447 Collection_releaseContext, 448 Collection_control, 449 { 450 Collection_addCert, 451 Collection_enumCert, 452 Collection_deleteCert 453 }, { 454 Collection_addCRL, 455 Collection_enumCRL, 456 Collection_deleteCRL 457 }, { 458 Collection_addCTL, 459 Collection_enumCTL, 460 Collection_deleteCTL 461 } 462 }; 463 464 WINECRYPT_CERTSTORE *CRYPT_CollectionOpenStore(HCRYPTPROV hCryptProv, 465 DWORD dwFlags, const void *pvPara) 466 { 467 WINE_COLLECTIONSTORE *store; 468 469 if (dwFlags & CERT_STORE_DELETE_FLAG) 470 { 471 SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 472 store = NULL; 473 } 474 else 475 { 476 store = CryptMemAlloc(sizeof(WINE_COLLECTIONSTORE)); 477 if (store) 478 { 479 memset(store, 0, sizeof(WINE_COLLECTIONSTORE)); 480 CRYPT_InitStore(&store->hdr, dwFlags, StoreTypeCollection, &CollectionStoreVtbl); 481 InitializeCriticalSection(&store->cs); 482 store->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PWINE_COLLECTIONSTORE->cs"); 483 list_init(&store->stores); 484 } 485 } 486 return (WINECRYPT_CERTSTORE*)store; 487 } 488 489 BOOL WINAPI CertAddStoreToCollection(HCERTSTORE hCollectionStore, 490 HCERTSTORE hSiblingStore, DWORD dwUpdateFlags, DWORD dwPriority) 491 { 492 WINE_COLLECTIONSTORE *collection = hCollectionStore; 493 WINECRYPT_CERTSTORE *sibling = hSiblingStore; 494 WINE_STORE_LIST_ENTRY *entry; 495 BOOL ret; 496 497 TRACE("(%p, %p, %08x, %d)\n", hCollectionStore, hSiblingStore, 498 dwUpdateFlags, dwPriority); 499 500 if (!collection || !sibling) 501 return TRUE; 502 if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC) 503 { 504 SetLastError(E_INVALIDARG); 505 return FALSE; 506 } 507 if (collection->hdr.type != StoreTypeCollection) 508 { 509 SetLastError(E_INVALIDARG); 510 return FALSE; 511 } 512 if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) 513 { 514 SetLastError(E_INVALIDARG); 515 return FALSE; 516 } 517 518 entry = CryptMemAlloc(sizeof(WINE_STORE_LIST_ENTRY)); 519 if (entry) 520 { 521 InterlockedIncrement(&sibling->ref); 522 TRACE("sibling %p's ref count is %d\n", sibling, sibling->ref); 523 entry->store = sibling; 524 entry->dwUpdateFlags = dwUpdateFlags; 525 entry->dwPriority = dwPriority; 526 TRACE("%p: adding %p, priority %d\n", collection, entry, dwPriority); 527 EnterCriticalSection(&collection->cs); 528 if (dwPriority) 529 { 530 WINE_STORE_LIST_ENTRY *cursor; 531 BOOL added = FALSE; 532 533 LIST_FOR_EACH_ENTRY(cursor, &collection->stores, 534 WINE_STORE_LIST_ENTRY, entry) 535 { 536 if (cursor->dwPriority < dwPriority) 537 { 538 list_add_before(&cursor->entry, &entry->entry); 539 added = TRUE; 540 break; 541 } 542 } 543 if (!added) 544 list_add_tail(&collection->stores, &entry->entry); 545 } 546 else 547 list_add_tail(&collection->stores, &entry->entry); 548 LeaveCriticalSection(&collection->cs); 549 ret = TRUE; 550 } 551 else 552 ret = FALSE; 553 return ret; 554 } 555 556 void WINAPI CertRemoveStoreFromCollection(HCERTSTORE hCollectionStore, 557 HCERTSTORE hSiblingStore) 558 { 559 WINE_COLLECTIONSTORE *collection = hCollectionStore; 560 WINECRYPT_CERTSTORE *sibling = hSiblingStore; 561 WINE_STORE_LIST_ENTRY *store; 562 563 TRACE("(%p, %p)\n", hCollectionStore, hSiblingStore); 564 565 if (!collection || !sibling) 566 return; 567 if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC) 568 { 569 SetLastError(E_INVALIDARG); 570 return; 571 } 572 if (collection->hdr.type != StoreTypeCollection) 573 return; 574 if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) 575 { 576 SetLastError(E_INVALIDARG); 577 return; 578 } 579 EnterCriticalSection(&collection->cs); 580 LIST_FOR_EACH_ENTRY(store, &collection->stores, WINE_STORE_LIST_ENTRY, entry) 581 { 582 if (store->store == sibling) 583 { 584 list_remove(&store->entry); 585 CertCloseStore(store->store, 0); 586 CryptMemFree(store); 587 break; 588 } 589 } 590 LeaveCriticalSection(&collection->cs); 591 } 592