collectionstore.c 17 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;
118 119

        EnterCriticalSection(&store->cs);
120
        LIST_FOR_EACH_ENTRY(entry, &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
        child = contextFuncs->enumContext(storeEntry->store, child);
        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 177 178 179 180 181 182
    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.
             */
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 193 194 195 196 197 198 199 200 201
        }
        else
        {
            SetLastError(CRYPT_E_NOT_FOUND);
            ret = NULL;
        }
    }
    TRACE("returning %p\n", ret);
    return ret;
}

202 203
static BOOL Collection_addCert(WINECRYPT_CERTSTORE *store, context_t *cert,
 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 212
    if (ppStoreContext && childContext)
    {
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 220
    if (childContext)
        Context_Release(childContext);
221 222 223
    return ret;
}

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 240 241 242
    }
    else
    {
        if (!list_empty(&cs->stores))
        {
243
            WINE_STORE_LIST_ENTRY *storeEntry = LIST_ENTRY(cs->stores.next,
244 245 246
             WINE_STORE_LIST_ENTRY, entry);

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

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 272
static BOOL Collection_addCRL(WINECRYPT_CERTSTORE *store, context_t *crl,
 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 281
    if (ppStoreContext && childContext)
    {
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 289
    if (childContext)
        Context_Release(childContext);
290 291 292
    return ret;
}

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 309 310 311
    }
    else
    {
        if (!list_empty(&cs->stores))
        {
312
            WINE_STORE_LIST_ENTRY *storeEntry = LIST_ENTRY(cs->stores.next,
313 314 315
             WINE_STORE_LIST_ENTRY, entry);

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

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 340
static BOOL Collection_addCTL(WINECRYPT_CERTSTORE *store, context_t *ctl,
 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 349
    if (ppStoreContext && childContext)
    {
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 357
    if (childContext)
        Context_Release(childContext);
358 359 360
    return ret;
}

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

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 377 378 379
    }
    else
    {
        if (!list_empty(&cs->stores))
        {
380
            WINE_STORE_LIST_ENTRY *storeEntry = LIST_ENTRY(cs->stores.next,
381 382 383
             WINE_STORE_LIST_ENTRY, entry);

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

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 409 410
 DWORD dwCtrlType, void const *pvCtrlPara)
{
    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 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432

    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)
    {
433
        if (entry->store->vtbl->control)
434
        {
435
            ret = entry->store->vtbl->control(entry->store, dwFlags, dwCtrlType, pvCtrlPara);
436 437 438 439 440 441 442 443
            if (!ret)
                break;
        }
    }
    LeaveCriticalSection(&store->cs);
    return ret;
}

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

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

    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));
480
            CRYPT_InitStore(&store->hdr, dwFlags, StoreTypeCollection, &CollectionStoreVtbl);
481 482 483 484 485
            InitializeCriticalSection(&store->cs);
            store->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PWINE_COLLECTIONSTORE->cs");
            list_init(&store->stores);
        }
    }
486
    return (WINECRYPT_CERTSTORE*)store;
487 488 489 490 491
}

BOOL WINAPI CertAddStoreToCollection(HCERTSTORE hCollectionStore,
 HCERTSTORE hSiblingStore, DWORD dwUpdateFlags, DWORD dwPriority)
{
492
    WINE_COLLECTIONSTORE *collection = hCollectionStore;
493
    WINECRYPT_CERTSTORE *sibling = hSiblingStore;
494
    WINE_STORE_LIST_ENTRY *entry;
495 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
    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;
        TRACE("%p: adding %p, priority %d\n", collection, entry, dwPriority);
        EnterCriticalSection(&collection->cs);
        if (dwPriority)
        {
530
            WINE_STORE_LIST_ENTRY *cursor;
531 532 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
            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)
{
559
    WINE_COLLECTIONSTORE *collection = hCollectionStore;
560
    WINECRYPT_CERTSTORE *sibling = hSiblingStore;
561
    WINE_STORE_LIST_ENTRY *store;
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579

    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);
580
    LIST_FOR_EACH_ENTRY(store, &collection->stores, WINE_STORE_LIST_ENTRY, entry)
581 582 583 584 585 586 587 588 589 590 591
    {
        if (store->store == sibling)
        {
            list_remove(&store->entry);
            CertCloseStore(store->store, 0);
            CryptMemFree(store);
            break;
        }
    }
    LeaveCriticalSection(&collection->cs);
}