collectionstore.c 17.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * 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 "wine/debug.h"
#include "crypt32_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(crypt);

typedef struct _WINE_STORE_LIST_ENTRY
{
29
    WINECRYPT_CERTSTORE *store;
30 31 32
    DWORD                dwUpdateFlags;
    DWORD                dwPriority;
    struct list          entry;
33
} WINE_STORE_LIST_ENTRY;
34 35 36 37 38 39

typedef struct _WINE_COLLECTIONSTORE
{
    WINECRYPT_CERTSTORE hdr;
    CRITICAL_SECTION    cs;
    struct list         stores;
40
} WINE_COLLECTIONSTORE;
41

42 43 44 45 46 47
static void Collection_addref(WINECRYPT_CERTSTORE *store)
{
    LONG ref = InterlockedIncrement(&store->ref);
    TRACE("ref = %d\n", ref);
}

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 55
    if(flags)
        FIXME("Unimplemented flags %x\n", flags);
56

57 58 59 60 61 62
    ref = InterlockedDecrement(&cs->hdr.ref);
    TRACE("(%p) ref=%d\n", store, ref);
    if(ref)
        return ERROR_SUCCESS;

    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 67 68 69
        CryptMemFree(entry);
    }
    cs->cs.DebugInfo->Spare[0] = 0;
    DeleteCriticalSection(&cs->cs);
70
    CRYPT_FreeStore(store);
71
    return ERROR_SUCCESS;
72 73
}

74 75 76 77 78 79
static void Collection_releaseContext(WINECRYPT_CERTSTORE *store, context_t *context)
{
    /* We don't cache context links, so just free them. */
    Context_Free(context);
}

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 87
    if (!ret)
        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 104 105

    ret = FALSE;
    if (toReplace)
    {
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 112
         contextFuncsOffset);
        ret = contextFuncs->addContext(storeEntry->store, context,
113
         existingLinked, &childContext, TRUE);
114 115 116
    }
    else
    {
117
        WINE_STORE_LIST_ENTRY *entry, *next;
118 119

        EnterCriticalSection(&store->cs);
120
        LIST_FOR_EACH_ENTRY_SAFE(entry, next, &store->stores, WINE_STORE_LIST_ENTRY, entry)
121 122 123
        {
            if (entry->dwUpdateFlags & CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG)
            {
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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
                break;
            }
        }
        LeaveCriticalSection(&store->cs);
        if (!storeEntry)
            SetLastError(E_ACCESSDENIED);
    }
    *pChildContext = childContext;
    return ret;
}

/* Advances a collection enumeration by one context, if possible, where
 * advancing means:
 * - calling the current store's enumeration function once, and returning
 *   the enumerated context if one is returned
 * - moving to the next store if the current store has no more items, and
 *   recursively calling itself to get the next item.
 * Returns NULL if the collection contains no more items or on error.
 * Assumes the collection store's lock is held.
 */
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 155
    struct list *storeNext = list_next(&store->stores, &storeEntry->entry);

156
    TRACE("(%p, %p, %p)\n", store, storeEntry, prev);
157

158
    if (prev)
159 160 161 162
    {
        /* Ref-counting funny business: "duplicate" (addref) the child, because
         * the free(pPrev) below can cause the ref count to become negative.
         */
163
        child = prev->linked;
164
        Context_AddRef(child);
165 166 167
        child = contextFuncs->enumContext(storeEntry->store, child);
        Context_Release(prev);
        prev = NULL;
168 169
    }
    else
170
    {
171
        child = contextFuncs->enumContext(storeEntry->store, NULL);
172
    }
173
    if (child) {
174
        ret = CRYPT_CollectionCreateContextFromChild(store, storeEntry, child);
175
        Context_Release(child);
176
    }
177 178 179 180 181 182 183
    else
    {
        if (storeNext)
        {
            /* We always want the same function pointers (from certs, crls)
             * in the next store, so use the same offset into the next store.
             */
184
            size_t offset = (const BYTE *)contextFuncs - (LPBYTE)storeEntry->store->vtbl;
185
            WINE_STORE_LIST_ENTRY *storeNextEntry =
186
             LIST_ENTRY(storeNext, WINE_STORE_LIST_ENTRY, entry);
187
            CONTEXT_FUNCS *storeNextContexts =
188
             (CONTEXT_FUNCS*)((LPBYTE)storeNextEntry->store->vtbl + offset);
189 190

            ret = CRYPT_CollectionAdvanceEnum(store, storeNextEntry,
191
             storeNextContexts, NULL);
192 193 194 195 196 197 198 199 200 201 202
        }
        else
        {
            SetLastError(CRYPT_E_NOT_FOUND);
            ret = NULL;
        }
    }
    TRACE("returning %p\n", ret);
    return ret;
}

203 204
static BOOL Collection_addCert(WINECRYPT_CERTSTORE *store, context_t *cert,
 context_t *toReplace, context_t **ppStoreContext, BOOL use_link)
205 206
{
    BOOL ret;
207
    context_t *childContext = NULL;
208
    WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store;
209

210
    ret = CRYPT_CollectionAddContext(cs, offsetof(store_vtbl_t, certs),
211
     cert, toReplace, &childContext);
212 213
    if (ppStoreContext && childContext)
    {
214
        WINE_STORE_LIST_ENTRY *storeEntry = childContext->u.ptr;
215
        cert_t *context = (cert_t*)CRYPT_CollectionCreateContextFromChild(cs, storeEntry,
216
         childContext);
217

218
        *ppStoreContext = &context->base;
219
    }
220 221
    if (childContext)
        Context_Release(childContext);
222 223 224
    return ret;
}

225
static context_t *Collection_enumCert(WINECRYPT_CERTSTORE *store, context_t *prev)
226
{
227
    WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store;
228
    context_t *ret;
229

230
    TRACE("(%p, %p)\n", store, prev);
231 232

    EnterCriticalSection(&cs->cs);
233
    if (prev)
234
    {
235
        WINE_STORE_LIST_ENTRY *storeEntry = prev->u.ptr;
236 237

        ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
238
         &storeEntry->store->vtbl->certs, prev);
239 240 241 242 243
    }
    else
    {
        if (!list_empty(&cs->stores))
        {
244
            WINE_STORE_LIST_ENTRY *storeEntry = LIST_ENTRY(cs->stores.next,
245 246 247
             WINE_STORE_LIST_ENTRY, entry);

            ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
248
             &storeEntry->store->vtbl->certs, NULL);
249 250 251 252 253 254 255 256 257 258 259 260
        }
        else
        {
            SetLastError(CRYPT_E_NOT_FOUND);
            ret = NULL;
        }
    }
    LeaveCriticalSection(&cs->cs);
    TRACE("returning %p\n", ret);
    return ret;
}

261
static BOOL Collection_deleteCert(WINECRYPT_CERTSTORE *store, context_t *context)
262
{
263
    cert_t *cert = (cert_t*)context;
264
    cert_t *linked;
265

266
    TRACE("(%p, %p)\n", store, cert);
267

268
    linked = (cert_t*)context->linked;
269
    return CertDeleteCertificateFromStore(&linked->ctx);
270 271
}

272 273
static BOOL Collection_addCRL(WINECRYPT_CERTSTORE *store, context_t *crl,
 context_t *toReplace, context_t **ppStoreContext, BOOL use_link)
274 275
{
    BOOL ret;
276
    context_t *childContext = NULL;
277
    WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store;
278

279
    ret = CRYPT_CollectionAddContext(cs, offsetof(store_vtbl_t, crls),
280
     crl, toReplace, &childContext);
281 282
    if (ppStoreContext && childContext)
    {
283
        WINE_STORE_LIST_ENTRY *storeEntry = childContext->u.ptr;
284
        crl_t *context = (crl_t*)CRYPT_CollectionCreateContextFromChild(cs, storeEntry,
285
         childContext);
286

287
        *ppStoreContext = &context->base;
288
    }
289 290
    if (childContext)
        Context_Release(childContext);
291 292 293
    return ret;
}

294
static context_t *Collection_enumCRL(WINECRYPT_CERTSTORE *store, context_t *prev)
295
{
296
    WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store;
297
    context_t *ret;
298

299
    TRACE("(%p, %p)\n", store, prev);
300 301

    EnterCriticalSection(&cs->cs);
302
    if (prev)
303
    {
304
        WINE_STORE_LIST_ENTRY *storeEntry = prev->u.ptr;
305 306

        ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
307
         &storeEntry->store->vtbl->crls, prev);
308 309 310 311 312
    }
    else
    {
        if (!list_empty(&cs->stores))
        {
313
            WINE_STORE_LIST_ENTRY *storeEntry = LIST_ENTRY(cs->stores.next,
314 315 316
             WINE_STORE_LIST_ENTRY, entry);

            ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
317
             &storeEntry->store->vtbl->crls, NULL);
318 319 320 321 322 323 324 325 326 327 328 329
        }
        else
        {
            SetLastError(CRYPT_E_NOT_FOUND);
            ret = NULL;
        }
    }
    LeaveCriticalSection(&cs->cs);
    TRACE("returning %p\n", ret);
    return ret;
}

330
static BOOL Collection_deleteCRL(WINECRYPT_CERTSTORE *store, context_t *context)
331
{
332
    crl_t *crl = (crl_t*)context, *linked;
333

334
    TRACE("(%p, %p)\n", store, crl);
335

336
    linked = (crl_t*)context->linked;
337
    return CertDeleteCRLFromStore(&linked->ctx);
338 339
}

340 341
static BOOL Collection_addCTL(WINECRYPT_CERTSTORE *store, context_t *ctl,
 context_t *toReplace, context_t **ppStoreContext, BOOL use_link)
342 343
{
    BOOL ret;
344
    context_t *childContext = NULL;
345
    WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store;
346

347
    ret = CRYPT_CollectionAddContext(cs, offsetof(store_vtbl_t, ctls),
348
     ctl, toReplace, &childContext);
349 350
    if (ppStoreContext && childContext)
    {
351
        WINE_STORE_LIST_ENTRY *storeEntry = childContext->u.ptr;
352
        ctl_t *context = (ctl_t*)CRYPT_CollectionCreateContextFromChild(cs, storeEntry,
353
         childContext);
354

355
        *ppStoreContext = &context->base;
356
    }
357 358
    if (childContext)
        Context_Release(childContext);
359 360 361
    return ret;
}

362
static context_t *Collection_enumCTL(WINECRYPT_CERTSTORE *store, context_t *prev)
363
{
364
    WINE_COLLECTIONSTORE *cs = (WINE_COLLECTIONSTORE*)store;
365 366
    void *ret;

367
    TRACE("(%p, %p)\n", store, prev);
368 369

    EnterCriticalSection(&cs->cs);
370
    if (prev)
371
    {
372
        WINE_STORE_LIST_ENTRY *storeEntry = prev->u.ptr;
373 374

        ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
375
         &storeEntry->store->vtbl->ctls, prev);
376 377 378 379 380
    }
    else
    {
        if (!list_empty(&cs->stores))
        {
381
            WINE_STORE_LIST_ENTRY *storeEntry = LIST_ENTRY(cs->stores.next,
382 383 384
             WINE_STORE_LIST_ENTRY, entry);

            ret = CRYPT_CollectionAdvanceEnum(cs, storeEntry,
385
             &storeEntry->store->vtbl->ctls, NULL);
386 387 388 389 390 391 392 393 394 395 396 397
        }
        else
        {
            SetLastError(CRYPT_E_NOT_FOUND);
            ret = NULL;
        }
    }
    LeaveCriticalSection(&cs->cs);
    TRACE("returning %p\n", ret);
    return ret;
}

398
static BOOL Collection_deleteCTL(WINECRYPT_CERTSTORE *store, context_t *context)
399
{
400
    ctl_t *ctl = (ctl_t*)context, *linked;
401

402
    TRACE("(%p, %p)\n", store, ctl);
403

404
    linked = (ctl_t*)context->linked;
405
    return CertDeleteCTLFromStore(&linked->ctx);
406 407
}

408
static BOOL Collection_control(WINECRYPT_CERTSTORE *cert_store, DWORD dwFlags,
409 410 411
 DWORD dwCtrlType, void const *pvCtrlPara)
{
    BOOL ret;
412
    WINE_COLLECTIONSTORE *store = (WINE_COLLECTIONSTORE*)cert_store;
413
    WINE_STORE_LIST_ENTRY *entry;
414

415
    TRACE("(%p, %08x, %d, %p)\n", cert_store, dwFlags, dwCtrlType, pvCtrlPara);
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433

    if (!store)
        return TRUE;
    if (store->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
    {
        SetLastError(E_INVALIDARG);
        return FALSE;
    }
    if (store->hdr.type != StoreTypeCollection)
    {
        SetLastError(E_INVALIDARG);
        return FALSE;
    }

    ret = TRUE;
    EnterCriticalSection(&store->cs);
    LIST_FOR_EACH_ENTRY(entry, &store->stores, WINE_STORE_LIST_ENTRY, entry)
    {
434
        if (entry->store->vtbl->control)
435
        {
436
            ret = entry->store->vtbl->control(entry->store, dwFlags, dwCtrlType, pvCtrlPara);
437 438 439 440 441 442 443 444
            if (!ret)
                break;
        }
    }
    LeaveCriticalSection(&store->cs);
    return ret;
}

445
static const store_vtbl_t CollectionStoreVtbl = {
446
    Collection_addref,
447
    Collection_release,
448
    Collection_releaseContext,
449 450 451 452 453 454 455 456 457 458 459 460 461 462
    Collection_control,
    {
        Collection_addCert,
        Collection_enumCert,
        Collection_deleteCert
    }, {
        Collection_addCRL,
        Collection_enumCRL,
        Collection_deleteCRL
    }, {
        Collection_addCTL,
        Collection_enumCTL,
        Collection_deleteCTL
    }
463 464
};

465
WINECRYPT_CERTSTORE *CRYPT_CollectionOpenStore(HCRYPTPROV hCryptProv,
466 467
 DWORD dwFlags, const void *pvPara)
{
468
    WINE_COLLECTIONSTORE *store;
469 470 471 472 473 474 475 476 477 478 479 480

    if (dwFlags & CERT_STORE_DELETE_FLAG)
    {
        SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
        store = NULL;
    }
    else
    {
        store = CryptMemAlloc(sizeof(WINE_COLLECTIONSTORE));
        if (store)
        {
            memset(store, 0, sizeof(WINE_COLLECTIONSTORE));
481
            CRYPT_InitStore(&store->hdr, dwFlags, StoreTypeCollection, &CollectionStoreVtbl);
482 483 484 485 486
            InitializeCriticalSection(&store->cs);
            store->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PWINE_COLLECTIONSTORE->cs");
            list_init(&store->stores);
        }
    }
487
    return (WINECRYPT_CERTSTORE*)store;
488 489 490 491 492
}

BOOL WINAPI CertAddStoreToCollection(HCERTSTORE hCollectionStore,
 HCERTSTORE hSiblingStore, DWORD dwUpdateFlags, DWORD dwPriority)
{
493
    WINE_COLLECTIONSTORE *collection = hCollectionStore;
494
    WINECRYPT_CERTSTORE *sibling = hSiblingStore;
495
    WINE_STORE_LIST_ENTRY *entry;
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
    BOOL ret;

    TRACE("(%p, %p, %08x, %d)\n", hCollectionStore, hSiblingStore,
     dwUpdateFlags, dwPriority);

    if (!collection || !sibling)
        return TRUE;
    if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
    {
        SetLastError(E_INVALIDARG);
        return FALSE;
    }
    if (collection->hdr.type != StoreTypeCollection)
    {
        SetLastError(E_INVALIDARG);
        return FALSE;
    }
    if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
    {
        SetLastError(E_INVALIDARG);
        return FALSE;
    }

    entry = CryptMemAlloc(sizeof(WINE_STORE_LIST_ENTRY));
    if (entry)
    {
        InterlockedIncrement(&sibling->ref);
        TRACE("sibling %p's ref count is %d\n", sibling, sibling->ref);
        entry->store = sibling;
        entry->dwUpdateFlags = dwUpdateFlags;
        entry->dwPriority = dwPriority;
        list_init(&entry->entry);
        TRACE("%p: adding %p, priority %d\n", collection, entry, dwPriority);
        EnterCriticalSection(&collection->cs);
        if (dwPriority)
        {
532
            WINE_STORE_LIST_ENTRY *cursor;
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
            BOOL added = FALSE;

            LIST_FOR_EACH_ENTRY(cursor, &collection->stores,
             WINE_STORE_LIST_ENTRY, entry)
            {
                if (cursor->dwPriority < dwPriority)
                {
                    list_add_before(&cursor->entry, &entry->entry);
                    added = TRUE;
                    break;
                }
            }
            if (!added)
                list_add_tail(&collection->stores, &entry->entry);
        }
        else
            list_add_tail(&collection->stores, &entry->entry);
        LeaveCriticalSection(&collection->cs);
        ret = TRUE;
    }
    else
        ret = FALSE;
    return ret;
}

void WINAPI CertRemoveStoreFromCollection(HCERTSTORE hCollectionStore,
 HCERTSTORE hSiblingStore)
{
561
    WINE_COLLECTIONSTORE *collection = hCollectionStore;
562
    WINECRYPT_CERTSTORE *sibling = hSiblingStore;
563
    WINE_STORE_LIST_ENTRY *store, *next;
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594

    TRACE("(%p, %p)\n", hCollectionStore, hSiblingStore);

    if (!collection || !sibling)
        return;
    if (collection->hdr.dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
    {
        SetLastError(E_INVALIDARG);
        return;
    }
    if (collection->hdr.type != StoreTypeCollection)
        return;
    if (sibling->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
    {
        SetLastError(E_INVALIDARG);
        return;
    }
    EnterCriticalSection(&collection->cs);
    LIST_FOR_EACH_ENTRY_SAFE(store, next, &collection->stores,
     WINE_STORE_LIST_ENTRY, entry)
    {
        if (store->store == sibling)
        {
            list_remove(&store->entry);
            CertCloseStore(store->store, 0);
            CryptMemFree(store);
            break;
        }
    }
    LeaveCriticalSection(&collection->cs);
}