softpub.c 34.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * Copyright 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>
19 20 21

#define NONAMELESSUNION

22 23 24
#include "windef.h"
#include "winbase.h"
#include "wintrust.h"
25
#include "mssip.h"
26
#include "softpub.h"
27 28 29 30
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(wintrust);

31 32 33 34 35 36 37 38 39 40 41 42 43
HRESULT WINAPI SoftpubDefCertInit(CRYPT_PROVIDER_DATA *data)
{
    HRESULT ret = S_FALSE;

    TRACE("(%p)\n", data);

    if (data->padwTrustStepErrors &&
     !data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_WVTINIT])
        ret = S_OK;
    TRACE("returning %08x\n", ret);
    return ret;
}

44 45 46 47 48 49 50 51 52 53 54 55
HRESULT WINAPI SoftpubInitialize(CRYPT_PROVIDER_DATA *data)
{
    HRESULT ret = S_FALSE;

    TRACE("(%p)\n", data);

    if (data->padwTrustStepErrors &&
     !data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_WVTINIT])
        ret = S_OK;
    TRACE("returning %08x\n", ret);
    return ret;
}
56

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
HRESULT WINAPI DriverInitializePolicy(CRYPT_PROVIDER_DATA *data)
{
    FIXME("stub\n");
    return S_OK;
}

HRESULT WINAPI DriverCleanupPolicy(CRYPT_PROVIDER_DATA *data)
{
    FIXME("stub\n");
    return S_OK;
}

HRESULT WINAPI DriverFinalPolicy(CRYPT_PROVIDER_DATA *data)
{
    FIXME("stub\n");
    return S_OK;
}

75
/* Assumes data->pWintrustData->u.pFile exists.  Makes sure a file handle is
76 77
 * open for the file.
 */
78
static DWORD SOFTPUB_OpenFile(CRYPT_PROVIDER_DATA *data)
79
{
80
    DWORD err = ERROR_SUCCESS;
81 82 83 84 85

    /* PSDK implies that all values should be initialized to NULL, so callers
     * typically have hFile as NULL rather than INVALID_HANDLE_VALUE.  Check
     * for both.
     */
86 87
    if (!data->pWintrustData->u.pFile->hFile ||
     data->pWintrustData->u.pFile->hFile == INVALID_HANDLE_VALUE)
88
    {
89 90
        data->pWintrustData->u.pFile->hFile =
            CreateFileW(data->pWintrustData->u.pFile->pcwszFilePath, GENERIC_READ,
91
          FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
92
        if (data->pWintrustData->u.pFile->hFile != INVALID_HANDLE_VALUE)
93 94
            data->fOpenedFile = TRUE;
        else
95
            err = GetLastError();
96
    }
97
    if (!err)
98 99
        GetFileTime(data->pWintrustData->u.pFile->hFile, &data->sftSystemTime,
         NULL, NULL);
100 101
    TRACE("returning %d\n", err);
    return err;
102 103
}

104
/* Assumes data->pWintrustData->u.pFile exists.  Sets data->pPDSip->gSubject to
105 106
 * the file's subject GUID.
 */
107
static DWORD SOFTPUB_GetFileSubject(CRYPT_PROVIDER_DATA *data)
108
{
109
    DWORD err = ERROR_SUCCESS;
110

111 112 113
    if (!WVT_ISINSTRUCT(WINTRUST_FILE_INFO,
     data->pWintrustData->u.pFile->cbStruct, pgKnownSubject) ||
     !data->pWintrustData->u.pFile->pgKnownSubject)
114
    {
115
        if (!CryptSIPRetrieveSubjectGuid(
116 117
         data->pWintrustData->u.pFile->pcwszFilePath,
         data->pWintrustData->u.pFile->hFile,
118 119
         &data->u.pPDSip->gSubject))
            err = GetLastError();
120 121
    }
    else
122
        data->u.pPDSip->gSubject = *data->pWintrustData->u.pFile->pgKnownSubject;
123 124
    TRACE("returning %d\n", err);
    return err;
125 126
}

127 128
/* Assumes data->u.pPDSip exists, and its gSubject member set.
 * Allocates data->u.pPDSip->pSip and loads it, if possible.
129
 */
130
static DWORD SOFTPUB_GetSIP(CRYPT_PROVIDER_DATA *data)
131
{
132
    DWORD err = ERROR_SUCCESS;
133

134 135
    data->u.pPDSip->pSip = data->psPfns->pfnAlloc(sizeof(SIP_DISPATCH_INFO));
    if (data->u.pPDSip->pSip)
136
    {
137 138
        if (!CryptSIPLoad(&data->u.pPDSip->gSubject, 0, data->u.pPDSip->pSip))
            err = GetLastError();
139
    }
140 141 142 143
    else
        err = ERROR_OUTOFMEMORY;
    TRACE("returning %d\n", err);
    return err;
144 145
}

146 147
/* Assumes data->u.pPDSip has been loaded, and data->u.pPDSip->pSip allocated.
 * Calls data->u.pPDSip->pSip->pfGet to construct data->hMsg.
148
 */
149
static DWORD SOFTPUB_GetMessageFromFile(CRYPT_PROVIDER_DATA *data, HANDLE file,
150
 LPCWSTR filePath)
151
{
152
    DWORD err = ERROR_SUCCESS;
153 154 155 156
    BOOL ret;
    LPBYTE buf = NULL;
    DWORD size = 0;

157
    data->u.pPDSip->psSipSubjectInfo =
158
     data->psPfns->pfnAlloc(sizeof(SIP_SUBJECTINFO));
159
    if (!data->u.pPDSip->psSipSubjectInfo)
160
        return ERROR_OUTOFMEMORY;
161

162 163
    data->u.pPDSip->psSipSubjectInfo->cbSize = sizeof(SIP_SUBJECTINFO);
    data->u.pPDSip->psSipSubjectInfo->pgSubjectType = &data->u.pPDSip->gSubject;
164 165
    data->u.pPDSip->psSipSubjectInfo->hFile = file;
    data->u.pPDSip->psSipSubjectInfo->pwsFileName = filePath;
166 167
    data->u.pPDSip->psSipSubjectInfo->hProv = data->hProv;
    ret = data->u.pPDSip->pSip->pfGet(data->u.pPDSip->psSipSubjectInfo,
168 169
     &data->dwEncoding, 0, &size, 0);
    if (!ret)
170
        return TRUST_E_NOSIGNATURE;
171 172 173

    buf = data->psPfns->pfnAlloc(size);
    if (!buf)
174
        return ERROR_OUTOFMEMORY;
175

176
    ret = data->u.pPDSip->pSip->pfGet(data->u.pPDSip->psSipSubjectInfo,
177 178 179 180 181 182
     &data->dwEncoding, 0, &size, buf);
    if (ret)
    {
        data->hMsg = CryptMsgOpenToDecode(data->dwEncoding, 0, 0, data->hProv,
         NULL, NULL);
        if (data->hMsg)
183
        {
184
            ret = CryptMsgUpdate(data->hMsg, buf, size, TRUE);
185 186 187
            if (!ret)
                err = GetLastError();
        }
188
    }
189 190
    else
        err = GetLastError();
191 192

    data->psPfns->pfnFree(buf);
193 194
    TRACE("returning %d\n", err);
    return err;
195 196
}

197
static DWORD SOFTPUB_CreateStoreFromMessage(CRYPT_PROVIDER_DATA *data)
198
{
199
    DWORD err = ERROR_SUCCESS;
200 201 202 203 204 205
    HCERTSTORE store;

    store = CertOpenStore(CERT_STORE_PROV_MSG, data->dwEncoding,
     data->hProv, CERT_STORE_NO_CRYPT_RELEASE_FLAG, data->hMsg);
    if (store)
    {
206 207
        if (!data->psPfns->pfnAddStore2Chain(data, store))
            err = GetLastError();
208
        CertCloseStore(store, 0);
209
    }
210 211 212 213
    else
        err = GetLastError();
    TRACE("returning %d\n", err);
    return err;
214 215 216 217 218
}

static DWORD SOFTPUB_DecodeInnerContent(CRYPT_PROVIDER_DATA *data)
{
    BOOL ret;
219
    DWORD size, err = ERROR_SUCCESS;
220
    LPSTR oid = NULL;
221 222 223 224 225
    LPBYTE buf = NULL;

    ret = CryptMsgGetParam(data->hMsg, CMSG_INNER_CONTENT_TYPE_PARAM, 0, NULL,
     &size);
    if (!ret)
226 227
    {
        err = GetLastError();
228
        goto error;
229
    }
230 231
    oid = data->psPfns->pfnAlloc(size);
    if (!oid)
232
    {
233
        err = ERROR_OUTOFMEMORY;
234 235
        goto error;
    }
236
    ret = CryptMsgGetParam(data->hMsg, CMSG_INNER_CONTENT_TYPE_PARAM, 0, oid,
237 238
     &size);
    if (!ret)
239 240
    {
        err = GetLastError();
241
        goto error;
242
    }
243 244
    ret = CryptMsgGetParam(data->hMsg, CMSG_CONTENT_PARAM, 0, NULL, &size);
    if (!ret)
245 246
    {
        err = GetLastError();
247
        goto error;
248
    }
249 250
    buf = data->psPfns->pfnAlloc(size);
    if (!buf)
251
    {
252
        err = ERROR_OUTOFMEMORY;
253
        goto error;
254
    }
255 256
    ret = CryptMsgGetParam(data->hMsg, CMSG_CONTENT_PARAM, 0, buf, &size);
    if (!ret)
257 258
    {
        err = GetLastError();
259
        goto error;
260
    }
261 262
    ret = CryptDecodeObject(data->dwEncoding, oid, buf, size, 0, NULL, &size);
    if (!ret)
263 264
    {
        err = GetLastError();
265
        goto error;
266
    }
267 268
    data->u.pPDSip->psIndirectData = data->psPfns->pfnAlloc(size);
    if (!data->u.pPDSip->psIndirectData)
269
    {
270
        err = ERROR_OUTOFMEMORY;
271
        goto error;
272
    }
273 274
    ret = CryptDecodeObject(data->dwEncoding, oid, buf, size, 0,
     data->u.pPDSip->psIndirectData, &size);
275 276
    if (!ret)
        err = GetLastError();
277 278

error:
279
    TRACE("returning %d\n", err);
280 281
    data->psPfns->pfnFree(oid);
    data->psPfns->pfnFree(buf);
282
    return err;
283 284
}

285
static DWORD SOFTPUB_LoadCertMessage(CRYPT_PROVIDER_DATA *data)
286
{
287
    DWORD err = ERROR_SUCCESS;
288

289
    if (data->pWintrustData->u.pCert &&
290 291
     WVT_IS_CBSTRUCT_GT_MEMBEROFFSET(WINTRUST_CERT_INFO,
     data->pWintrustData->u.pCert->cbStruct, psCertContext))
292
    {
293
        if (data->psPfns)
294
        {
295 296
            CRYPT_PROVIDER_SGNR signer = { sizeof(signer), { 0 } };
            DWORD i;
297
            BOOL ret;
298 299 300 301

            /* Add a signer with nothing but the time to verify, so we can
             * add a cert to it
             */
302 303 304
            if (WVT_ISINSTRUCT(WINTRUST_CERT_INFO,
             data->pWintrustData->u.pCert->cbStruct, psftVerifyAsOf) &&
             data->pWintrustData->u.pCert->psftVerifyAsOf)
305 306 307 308 309 310 311 312 313 314
                data->sftSystemTime = signer.sftVerifyAsOf;
            else
            {
                SYSTEMTIME sysTime;

                GetSystemTime(&sysTime);
                SystemTimeToFileTime(&sysTime, &signer.sftVerifyAsOf);
            }
            ret = data->psPfns->pfnAddSgnr2Chain(data, FALSE, 0, &signer);
            if (ret)
315 316 317
            {
                ret = data->psPfns->pfnAddCert2Chain(data, 0, FALSE, 0,
                 data->pWintrustData->u.pCert->psCertContext);
318 319 320 321 322 323
                if (WVT_ISINSTRUCT(WINTRUST_CERT_INFO,
                 data->pWintrustData->u.pCert->cbStruct, pahStores))
                        for (i = 0;
                         ret && i < data->pWintrustData->u.pCert->chStores; i++)
                            ret = data->psPfns->pfnAddStore2Chain(data,
                             data->pWintrustData->u.pCert->pahStores[i]);
324
            }
325 326
            if (!ret)
                err = GetLastError();
327
        }
328 329
    }
    else
330 331
        err = ERROR_INVALID_PARAMETER;
    return err;
332 333
}

334
static DWORD SOFTPUB_LoadFileMessage(CRYPT_PROVIDER_DATA *data)
335
{
336
    DWORD err = ERROR_SUCCESS;
337 338 339

    if (!data->pWintrustData->u.pFile)
    {
340
        err = ERROR_INVALID_PARAMETER;
341 342
        goto error;
    }
343 344
    err = SOFTPUB_OpenFile(data);
    if (err)
345
        goto error;
346 347
    err = SOFTPUB_GetFileSubject(data);
    if (err)
348
        goto error;
349 350
    err = SOFTPUB_GetSIP(data);
    if (err)
351
        goto error;
352 353 354
    err = SOFTPUB_GetMessageFromFile(data, data->pWintrustData->u.pFile->hFile,
     data->pWintrustData->u.pFile->pcwszFilePath);
    if (err)
355
        goto error;
356 357
    err = SOFTPUB_CreateStoreFromMessage(data);
    if (err)
358
        goto error;
359 360
    err = SOFTPUB_DecodeInnerContent(data);

361
error:
362 363 364 365 366 367 368 369
    if (err && data->fOpenedFile && data->pWintrustData->u.pFile)
    {
        /* The caller won't expect the file to be open on failure, so close it.
         */
        CloseHandle(data->pWintrustData->u.pFile->hFile);
        data->pWintrustData->u.pFile->hFile = INVALID_HANDLE_VALUE;
        data->fOpenedFile = FALSE;
    }
370
    return err;
371 372
}

373
static DWORD SOFTPUB_LoadCatalogMessage(CRYPT_PROVIDER_DATA *data)
374
{
375
    DWORD err;
376 377 378 379 380 381 382 383 384 385 386
    HANDLE catalog = INVALID_HANDLE_VALUE;

    if (!data->pWintrustData->u.pCatalog)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    catalog = CreateFileW(data->pWintrustData->u.pCatalog->pcwszCatalogFilePath,
     GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
     NULL);
    if (catalog == INVALID_HANDLE_VALUE)
387 388
        return GetLastError();
    if (!CryptSIPRetrieveSubjectGuid(
389
     data->pWintrustData->u.pCatalog->pcwszCatalogFilePath, catalog,
390 391 392
     &data->u.pPDSip->gSubject))
    {
        err = GetLastError();
393
        goto error;
394
    }
395 396
    err = SOFTPUB_GetSIP(data);
    if (err)
397
        goto error;
398 399
    err = SOFTPUB_GetMessageFromFile(data, catalog,
     data->pWintrustData->u.pCatalog->pcwszCatalogFilePath);
400
    if (err)
401
        goto error;
402 403
    err = SOFTPUB_CreateStoreFromMessage(data);
    if (err)
404
        goto error;
405
    err = SOFTPUB_DecodeInnerContent(data);
406 407 408
    /* FIXME: this loads the catalog file, but doesn't validate the member. */
error:
    CloseHandle(catalog);
409
    return err;
410 411
}

412 413
HRESULT WINAPI SoftpubLoadMessage(CRYPT_PROVIDER_DATA *data)
{
414
    DWORD err = ERROR_SUCCESS;
415 416 417 418 419 420 421 422 423

    TRACE("(%p)\n", data);

    if (!data->padwTrustStepErrors)
        return S_FALSE;

    switch (data->pWintrustData->dwUnionChoice)
    {
    case WTD_CHOICE_CERT:
424
        err = SOFTPUB_LoadCertMessage(data);
425 426
        break;
    case WTD_CHOICE_FILE:
427
        err = SOFTPUB_LoadFileMessage(data);
428
        break;
429
    case WTD_CHOICE_CATALOG:
430
        err = SOFTPUB_LoadCatalogMessage(data);
431
        break;
432 433
    default:
        FIXME("unimplemented for %d\n", data->pWintrustData->dwUnionChoice);
434
        err = ERROR_INVALID_PARAMETER;
435 436
    }

437
    if (err)
438
        data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = err;
439
    TRACE("returning %d (%08x)\n", !err ? S_OK : S_FALSE,
440
     data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV]);
441
    return !err ? S_OK : S_FALSE;
442
}
443

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
static CMSG_SIGNER_INFO *WINTRUST_GetSigner(CRYPT_PROVIDER_DATA *data,
 DWORD signerIdx)
{
    BOOL ret;
    CMSG_SIGNER_INFO *signerInfo = NULL;
    DWORD size;

    ret = CryptMsgGetParam(data->hMsg, CMSG_SIGNER_INFO_PARAM, signerIdx,
     NULL, &size);
    if (ret)
    {
        signerInfo = data->psPfns->pfnAlloc(size);
        if (signerInfo)
        {
            ret = CryptMsgGetParam(data->hMsg, CMSG_SIGNER_INFO_PARAM,
             signerIdx, signerInfo, &size);
            if (!ret)
            {
                data->psPfns->pfnFree(signerInfo);
                signerInfo = NULL;
            }
        }
        else
            SetLastError(ERROR_OUTOFMEMORY);
    }
    return signerInfo;
}

472
static DWORD WINTRUST_SaveSigner(CRYPT_PROVIDER_DATA *data, DWORD signerIdx)
473
{
474
    DWORD err;
475 476 477 478 479 480 481
    CMSG_SIGNER_INFO *signerInfo = WINTRUST_GetSigner(data, signerIdx);

    if (signerInfo)
    {
        CRYPT_PROVIDER_SGNR sgnr = { sizeof(sgnr), { 0 } };

        sgnr.psSigner = signerInfo;
482
        sgnr.sftVerifyAsOf = data->sftSystemTime;
483 484 485 486
        if (!data->psPfns->pfnAddSgnr2Chain(data, FALSE, signerIdx, &sgnr))
            err = GetLastError();
        else
            err = ERROR_SUCCESS;
487 488
    }
    else
489 490
        err = GetLastError();
    return err;
491 492
}

493 494 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
static CERT_INFO *WINTRUST_GetSignerCertInfo(CRYPT_PROVIDER_DATA *data,
 DWORD signerIdx)
{
    BOOL ret;
    CERT_INFO *certInfo = NULL;
    DWORD size;

    ret = CryptMsgGetParam(data->hMsg, CMSG_SIGNER_CERT_INFO_PARAM, signerIdx,
     NULL, &size);
    if (ret)
    {
        certInfo = data->psPfns->pfnAlloc(size);
        if (certInfo)
        {
            ret = CryptMsgGetParam(data->hMsg, CMSG_SIGNER_CERT_INFO_PARAM,
             signerIdx, certInfo, &size);
            if (!ret)
            {
                data->psPfns->pfnFree(certInfo);
                certInfo = NULL;
            }
        }
        else
            SetLastError(ERROR_OUTOFMEMORY);
    }
    return certInfo;
}

521
static DWORD WINTRUST_VerifySigner(CRYPT_PROVIDER_DATA *data, DWORD signerIdx)
522
{
523
    DWORD err;
524 525 526 527
    CERT_INFO *certInfo = WINTRUST_GetSignerCertInfo(data, signerIdx);

    if (certInfo)
    {
528
        PCCERT_CONTEXT subject = CertGetSubjectCertificateFromStore(
529
         data->pahStores[0], data->dwEncoding, certInfo);
530 531

        if (subject)
532
        {
533 534 535
            CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA para = { sizeof(para), 0,
             signerIdx, CMSG_VERIFY_SIGNER_CERT, (LPVOID)subject };

536 537 538
            if (!CryptMsgControl(data->hMsg, 0, CMSG_CTRL_VERIFY_SIGNATURE_EX,
             &para))
                err = TRUST_E_CERT_SIGNATURE;
539
            else
540
            {
541 542
                data->psPfns->pfnAddCert2Chain(data, signerIdx, FALSE, 0,
                 subject);
543 544
                err = ERROR_SUCCESS;
            }
545
            CertFreeCertificateContext(subject);
546 547
        }
        else
548
            err = TRUST_E_NO_SIGNER_CERT;
549 550 551
        data->psPfns->pfnFree(certInfo);
    }
    else
552 553
        err = GetLastError();
    return err;
554 555
}

556 557
HRESULT WINAPI SoftpubLoadSignature(CRYPT_PROVIDER_DATA *data)
{
558
    DWORD err;
559 560 561 562 563 564

    TRACE("(%p)\n", data);

    if (!data->padwTrustStepErrors)
        return S_FALSE;

565
    if (data->hMsg)
566
    {
567
        DWORD signerCount, size;
568

569
        size = sizeof(signerCount);
570 571
        if (CryptMsgGetParam(data->hMsg, CMSG_SIGNER_COUNT_PARAM, 0,
         &signerCount, &size))
572
        {
573 574
            DWORD i;

575
            err = ERROR_SUCCESS;
576
            for (i = 0; !err && i < signerCount; i++)
577
            {
578
                if (!(err = WINTRUST_SaveSigner(data, i)))
579
                    err = WINTRUST_VerifySigner(data, i);
580
            }
581
        }
582
        else
583
            err = TRUST_E_NOSIGNATURE;
584 585
    }
    else
586 587 588 589
        err = ERROR_SUCCESS;
    if (err)
        data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_SIGPROV] = err;
    return !err ? S_OK : S_FALSE;
590
}
591

592 593 594 595 596 597 598 599 600 601 602 603 604 605
static DWORD WINTRUST_TrustStatusToConfidence(DWORD errorStatus)
{
    DWORD confidence = 0;

    confidence = 0;
    if (!(errorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID))
        confidence |= CERT_CONFIDENCE_SIG;
    if (!(errorStatus & CERT_TRUST_IS_NOT_TIME_VALID))
        confidence |= CERT_CONFIDENCE_TIME;
    if (!(errorStatus & CERT_TRUST_IS_NOT_TIME_NESTED))
        confidence |= CERT_CONFIDENCE_TIMENEST;
    return confidence;
}

606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
BOOL WINAPI SoftpubCheckCert(CRYPT_PROVIDER_DATA *data, DWORD idxSigner,
 BOOL fCounterSignerChain, DWORD idxCounterSigner)
{
    BOOL ret;

    TRACE("(%p, %d, %d, %d)\n", data, idxSigner, fCounterSignerChain,
     idxCounterSigner);

    if (fCounterSignerChain)
    {
        FIXME("unimplemented for counter signers\n");
        ret = FALSE;
    }
    else
    {
        PCERT_SIMPLE_CHAIN simpleChain =
         data->pasSigners[idxSigner].pChainContext->rgpChain[0];
        DWORD i;

        ret = TRUE;
        for (i = 0; i < simpleChain->cElement; i++)
        {
            /* Set confidence */
629 630 631
            data->pasSigners[idxSigner].pasCertChain[i].dwConfidence =
             WINTRUST_TrustStatusToConfidence(
             simpleChain->rgpElement[i]->TrustStatus.dwErrorStatus);
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
            /* Set additional flags */
            if (!(simpleChain->rgpElement[i]->TrustStatus.dwErrorStatus &
             CERT_TRUST_IS_UNTRUSTED_ROOT))
                data->pasSigners[idxSigner].pasCertChain[i].fTrustedRoot = TRUE;
            if (simpleChain->rgpElement[i]->TrustStatus.dwInfoStatus &
             CERT_TRUST_IS_SELF_SIGNED)
                data->pasSigners[idxSigner].pasCertChain[i].fSelfSigned = TRUE;
            if (simpleChain->rgpElement[i]->TrustStatus.dwErrorStatus &
             CERT_TRUST_IS_CYCLIC)
                data->pasSigners[idxSigner].pasCertChain[i].fIsCyclic = TRUE;
        }
    }
    return ret;
}

647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
static DWORD WINTRUST_TrustStatusToError(DWORD errorStatus)
{
    DWORD error;

    if (errorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID)
        error = TRUST_E_CERT_SIGNATURE;
    else if (errorStatus & CERT_TRUST_IS_UNTRUSTED_ROOT)
        error = CERT_E_UNTRUSTEDROOT;
    else if (errorStatus & CERT_TRUST_IS_NOT_TIME_VALID)
        error = CERT_E_EXPIRED;
    else if (errorStatus & CERT_TRUST_IS_NOT_TIME_NESTED)
        error = CERT_E_VALIDITYPERIODNESTING;
    else if (errorStatus & CERT_TRUST_IS_REVOKED)
        error = CERT_E_REVOKED;
    else if (errorStatus & CERT_TRUST_IS_OFFLINE_REVOCATION ||
     errorStatus & CERT_TRUST_REVOCATION_STATUS_UNKNOWN)
        error = CERT_E_REVOCATION_FAILURE;
    else if (errorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE)
        error = CERT_E_WRONG_USAGE;
    else if (errorStatus & CERT_TRUST_IS_CYCLIC)
        error = CERT_E_CHAINING;
    else if (errorStatus & CERT_TRUST_INVALID_EXTENSION)
        error = CERT_E_CRITICAL;
    else if (errorStatus & CERT_TRUST_INVALID_POLICY_CONSTRAINTS)
        error = CERT_E_INVALID_POLICY;
    else if (errorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS)
        error = TRUST_E_BASIC_CONSTRAINTS;
    else if (errorStatus & CERT_TRUST_INVALID_NAME_CONSTRAINTS ||
     errorStatus & CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT ||
     errorStatus & CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT ||
     errorStatus & CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT ||
     errorStatus & CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT)
        error = CERT_E_INVALID_NAME;
    else if (errorStatus & CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY)
        error = CERT_E_INVALID_POLICY;
    else if (errorStatus)
    {
        FIXME("unknown error status %08x\n", errorStatus);
        error = TRUST_E_SYSTEM_ERROR;
    }
    else
        error = S_OK;
    return error;
}

692
static DWORD WINTRUST_CopyChain(CRYPT_PROVIDER_DATA *data, DWORD signerIdx)
693
{
694
    DWORD err, i;
695 696 697
    PCERT_SIMPLE_CHAIN simpleChain =
     data->pasSigners[signerIdx].pChainContext->rgpChain[0];

698 699 700
    data->pasSigners[signerIdx].pasCertChain[0].dwConfidence =
     WINTRUST_TrustStatusToConfidence(
     simpleChain->rgpElement[0]->TrustStatus.dwErrorStatus);
701 702
    data->pasSigners[signerIdx].pasCertChain[0].pChainElement =
     simpleChain->rgpElement[0];
703 704
    err = ERROR_SUCCESS;
    for (i = 1; !err && i < simpleChain->cElement; i++)
705
    {
706 707
        if (data->psPfns->pfnAddCert2Chain(data, signerIdx, FALSE, 0,
         simpleChain->rgpElement[i]->pCertContext))
708
        {
709 710
            data->pasSigners[signerIdx].pasCertChain[i].pChainElement =
             simpleChain->rgpElement[i];
711 712 713 714
            data->pasSigners[signerIdx].pasCertChain[i].dwConfidence =
             WINTRUST_TrustStatusToConfidence(
             simpleChain->rgpElement[i]->TrustStatus.dwErrorStatus);
        }
715 716
        else
            err = GetLastError();
717
    }
718 719 720 721
    data->pasSigners[signerIdx].pasCertChain[simpleChain->cElement - 1].dwError
     = WINTRUST_TrustStatusToError(
     simpleChain->rgpElement[simpleChain->cElement - 1]->
     TrustStatus.dwErrorStatus);
722
    return err;
723 724
}

725 726 727 728 729 730
static void WINTRUST_CreateChainPolicyCreateInfo(
 const CRYPT_PROVIDER_DATA *data, PWTD_GENERIC_CHAIN_POLICY_CREATE_INFO info,
 PCERT_CHAIN_PARA chainPara)
{
    chainPara->cbSize = sizeof(CERT_CHAIN_PARA);
    if (data->pRequestUsage)
731
        chainPara->RequestedUsage = *data->pRequestUsage;
732 733 734 735 736
    else
    {
        chainPara->RequestedUsage.dwType = 0;
        chainPara->RequestedUsage.Usage.cUsageIdentifier = 0;
    }
737 738 739 740 741 742 743 744 745 746 747 748 749 750
    info->u.cbSize = sizeof(WTD_GENERIC_CHAIN_POLICY_CREATE_INFO);
    info->hChainEngine = NULL;
    info->pChainPara = chainPara;
    if (data->dwProvFlags & CPD_REVOCATION_CHECK_END_CERT)
        info->dwFlags = CERT_CHAIN_REVOCATION_CHECK_END_CERT;
    else if (data->dwProvFlags & CPD_REVOCATION_CHECK_CHAIN)
        info->dwFlags = CERT_CHAIN_REVOCATION_CHECK_CHAIN;
    else if (data->dwProvFlags & CPD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT)
        info->dwFlags = CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
    else
        info->dwFlags = 0;
    info->pvReserved = NULL;
}

751
static DWORD WINTRUST_CreateChainForSigner(CRYPT_PROVIDER_DATA *data,
752 753 754
 DWORD signer, PWTD_GENERIC_CHAIN_POLICY_CREATE_INFO createInfo,
 PCERT_CHAIN_PARA chainPara)
{
755
    DWORD err = ERROR_SUCCESS;
756
    HCERTSTORE store = NULL;
757

758 759 760 761 762 763 764 765 766 767 768
    if (data->chStores)
    {
        store = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
         CERT_STORE_CREATE_NEW_FLAG, NULL);
        if (store)
        {
            DWORD i;

            for (i = 0; i < data->chStores; i++)
                CertAddStoreToCollection(store, data->pahStores[i], 0, 0);
        }
769
        else
770
            err = GetLastError();
771
    }
772
    if (!err)
773 774 775 776 777
    {
        /* Expect the end certificate for each signer to be the only cert in
         * the chain:
         */
        if (data->pasSigners[signer].csCertChain)
778
        {
779 780
            BOOL ret;

781 782 783 784 785 786 787
            /* Create a certificate chain for each signer */
            ret = CertGetCertificateChain(createInfo->hChainEngine,
             data->pasSigners[signer].pasCertChain[0].pCert,
             &data->pasSigners[signer].sftVerifyAsOf, store,
             chainPara, createInfo->dwFlags, createInfo->pvReserved,
             &data->pasSigners[signer].pChainContext);
            if (ret)
788
            {
789
                if (data->pasSigners[signer].pChainContext->cChain != 1)
790
                {
791
                    FIXME("unimplemented for more than 1 simple chain\n");
792
                    err = E_NOTIMPL;
793
                }
794 795
                else
                {
796 797 798
                    if (!(err = WINTRUST_CopyChain(data, signer)))
                    {
                        if (data->psPfns->pfnCertCheckPolicy)
799
                        {
800 801
                            ret = data->psPfns->pfnCertCheckPolicy(data, signer,
                             FALSE, 0);
802 803 804
                            if (!ret)
                                err = GetLastError();
                        }
805 806 807 808
                        else
                            TRACE(
                             "no cert check policy, skipping policy check\n");
                    }
809
                }
810
            }
811 812
            else
                err = GetLastError();
813
        }
814
        CertCloseStore(store, 0);
815
    }
816
    return err;
817 818
}

819 820
HRESULT WINAPI WintrustCertificateTrust(CRYPT_PROVIDER_DATA *data)
{
821
    DWORD err;
822

Juan Lang's avatar
Juan Lang committed
823 824
    TRACE("(%p)\n", data);

825
    if (!data->csSigners)
826
        err = TRUST_E_NOSIGNATURE;
827 828 829
    else
    {
        DWORD i;
830 831
        WTD_GENERIC_CHAIN_POLICY_CREATE_INFO createInfo;
        CERT_CHAIN_PARA chainPara;
832

833
        WINTRUST_CreateChainPolicyCreateInfo(data, &createInfo, &chainPara);
834 835 836
        err = ERROR_SUCCESS;
        for (i = 0; !err && i < data->csSigners; i++)
            err = WINTRUST_CreateChainForSigner(data, i, &createInfo,
837
             &chainPara);
838
    }
839 840 841
    if (err)
        data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_CERTPROV] = err;
    TRACE("returning %d (%08x)\n", !err ? S_OK : S_FALSE,
Juan Lang's avatar
Juan Lang committed
842
     data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_CERTPROV]);
843
    return !err ? S_OK : S_FALSE;
844
}
845

846 847
HRESULT WINAPI GenericChainCertificateTrust(CRYPT_PROVIDER_DATA *data)
{
848
    DWORD err;
849
    WTD_GENERIC_CHAIN_POLICY_DATA *policyData =
850
     data->pWintrustData->pPolicyCallbackData;
851 852 853 854 855 856

    TRACE("(%p)\n", data);

    if (policyData && policyData->u.cbSize !=
     sizeof(WTD_GENERIC_CHAIN_POLICY_CREATE_INFO))
    {
857
        err = ERROR_INVALID_PARAMETER;
858 859 860
        goto end;
    }
    if (!data->csSigners)
861
        err = TRUST_E_NOSIGNATURE;
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878
    else
    {
        DWORD i;
        WTD_GENERIC_CHAIN_POLICY_CREATE_INFO createInfo, *pCreateInfo;
        CERT_CHAIN_PARA chainPara, *pChainPara;

        if (policyData)
        {
            pCreateInfo = policyData->pSignerChainInfo;
            pChainPara = pCreateInfo->pChainPara;
        }
        else
        {
            WINTRUST_CreateChainPolicyCreateInfo(data, &createInfo, &chainPara);
            pChainPara = &chainPara;
            pCreateInfo = &createInfo;
        }
879 880 881
        err = ERROR_SUCCESS;
        for (i = 0; !err && i < data->csSigners; i++)
            err = WINTRUST_CreateChainForSigner(data, i, pCreateInfo,
882 883 884 885
             pChainPara);
    }

end:
886 887 888
    if (err)
        data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_CERTPROV] = err;
    TRACE("returning %d (%08x)\n", !err ? S_OK : S_FALSE,
889
     data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_CERTPROV]);
890
    return !err ? S_OK : S_FALSE;
891 892
}

893 894 895 896 897
HRESULT WINAPI SoftpubAuthenticode(CRYPT_PROVIDER_DATA *data)
{
    BOOL ret;
    CERT_CHAIN_POLICY_STATUS policyStatus = { sizeof(policyStatus), 0 };

Juan Lang's avatar
Juan Lang committed
898 899
    TRACE("(%p)\n", data);

900 901 902 903 904 905 906 907 908 909 910 911 912 913 914
    if (data->pWintrustData->dwUIChoice != WTD_UI_NONE)
        FIXME("unimplemented for UI choice %d\n",
         data->pWintrustData->dwUIChoice);
    if (!data->csSigners)
    {
        ret = FALSE;
        policyStatus.dwError = TRUST_E_NOSIGNATURE;
    }
    else
    {
        DWORD i;

        ret = TRUE;
        for (i = 0; ret && i < data->csSigners; i++)
        {
915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
            BYTE hash[20];
            DWORD size = sizeof(hash);

            /* First make sure cert isn't disallowed */
            if ((ret = CertGetCertificateContextProperty(
             data->pasSigners[i].pasCertChain[0].pCert,
             CERT_SIGNATURE_HASH_PROP_ID, hash, &size)))
            {
                static const WCHAR disallowedW[] =
                 { 'D','i','s','a','l','l','o','w','e','d',0 };
                HCERTSTORE disallowed = CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
                 X509_ASN_ENCODING, 0, CERT_SYSTEM_STORE_CURRENT_USER,
                 disallowedW);

                if (disallowed)
                {
                    PCCERT_CONTEXT found = CertFindCertificateInStore(
                     disallowed, X509_ASN_ENCODING, 0, CERT_FIND_SIGNATURE_HASH,
                     hash, NULL);

                    if (found)
                    {
                        /* Disallowed!  Can't verify it. */
                        policyStatus.dwError = TRUST_E_SUBJECT_NOT_TRUSTED;
                        ret = FALSE;
                        CertFreeCertificateContext(found);
                    }
                    CertCloseStore(disallowed, 0);
                }
            }
            if (ret)
            {
                CERT_CHAIN_POLICY_PARA policyPara = { sizeof(policyPara), 0 };

                if (data->dwRegPolicySettings & WTPF_TRUSTTEST)
                    policyPara.dwFlags |= CERT_CHAIN_POLICY_TRUST_TESTROOT_FLAG;
                if (data->dwRegPolicySettings & WTPF_TESTCANBEVALID)
                    policyPara.dwFlags |= CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG;
                if (data->dwRegPolicySettings & WTPF_IGNOREEXPIRATION)
                    policyPara.dwFlags |=
                     CERT_CHAIN_POLICY_IGNORE_NOT_TIME_VALID_FLAG |
                     CERT_CHAIN_POLICY_IGNORE_CTL_NOT_TIME_VALID_FLAG |
                     CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG;
                if (data->dwRegPolicySettings & WTPF_IGNOREREVOKATION)
                    policyPara.dwFlags |=
                     CERT_CHAIN_POLICY_IGNORE_END_REV_UNKNOWN_FLAG |
                     CERT_CHAIN_POLICY_IGNORE_CTL_SIGNER_REV_UNKNOWN_FLAG |
                     CERT_CHAIN_POLICY_IGNORE_CA_REV_UNKNOWN_FLAG |
                     CERT_CHAIN_POLICY_IGNORE_ROOT_REV_UNKNOWN_FLAG;
                CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_AUTHENTICODE,
                 data->pasSigners[i].pChainContext, &policyPara, &policyStatus);
                if (policyStatus.dwError != NO_ERROR)
                    ret = FALSE;
            }
969 970 971 972 973
        }
    }
    if (!ret)
        data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_POLICYPROV] =
         policyStatus.dwError;
Juan Lang's avatar
Juan Lang committed
974 975
    TRACE("returning %d (%08x)\n", ret ? S_OK : S_FALSE,
     data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_POLICYPROV]);
976 977 978
    return ret ? S_OK : S_FALSE;
}

979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
static HRESULT WINAPI WINTRUST_DefaultPolicy(CRYPT_PROVIDER_DATA *pProvData,
 DWORD dwStepError, DWORD dwRegPolicySettings, DWORD cSigner,
 PWTD_GENERIC_CHAIN_POLICY_SIGNER_INFO rgpSigner, void *pvPolicyArg)
{
    DWORD i;
    CERT_CHAIN_POLICY_STATUS policyStatus = { sizeof(policyStatus), 0 };

    for (i = 0; !policyStatus.dwError && i < cSigner; i++)
    {
        CERT_CHAIN_POLICY_PARA policyPara = { sizeof(policyPara), 0 };

        if (dwRegPolicySettings & WTPF_IGNOREEXPIRATION)
            policyPara.dwFlags |=
             CERT_CHAIN_POLICY_IGNORE_NOT_TIME_VALID_FLAG |
             CERT_CHAIN_POLICY_IGNORE_CTL_NOT_TIME_VALID_FLAG |
             CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG;
        if (dwRegPolicySettings & WTPF_IGNOREREVOKATION)
            policyPara.dwFlags |=
             CERT_CHAIN_POLICY_IGNORE_END_REV_UNKNOWN_FLAG |
             CERT_CHAIN_POLICY_IGNORE_CTL_SIGNER_REV_UNKNOWN_FLAG |
             CERT_CHAIN_POLICY_IGNORE_CA_REV_UNKNOWN_FLAG |
             CERT_CHAIN_POLICY_IGNORE_ROOT_REV_UNKNOWN_FLAG;
        CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASE,
         rgpSigner[i].pChainContext, &policyPara, &policyStatus);
    }
    return policyStatus.dwError;
}

HRESULT WINAPI GenericChainFinalProv(CRYPT_PROVIDER_DATA *data)
{
    HRESULT err = NO_ERROR; /* not a typo, MS confused the types */
    WTD_GENERIC_CHAIN_POLICY_DATA *policyData =
1011
     data->pWintrustData->pPolicyCallbackData;
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061

    TRACE("(%p)\n", data);

    if (data->pWintrustData->dwUIChoice != WTD_UI_NONE)
        FIXME("unimplemented for UI choice %d\n",
         data->pWintrustData->dwUIChoice);
    if (!data->csSigners)
        err = TRUST_E_NOSIGNATURE;
    else
    {
        PFN_WTD_GENERIC_CHAIN_POLICY_CALLBACK policyCallback;
        void *policyArg;
        WTD_GENERIC_CHAIN_POLICY_SIGNER_INFO *signers = NULL;

        if (policyData)
        {
            policyCallback = policyData->pfnPolicyCallback;
            policyArg = policyData->pvPolicyArg;
        }
        else
        {
            policyCallback = WINTRUST_DefaultPolicy;
            policyArg = NULL;
        }
        if (data->csSigners)
        {
            DWORD i;

            signers = data->psPfns->pfnAlloc(
             data->csSigners * sizeof(WTD_GENERIC_CHAIN_POLICY_SIGNER_INFO));
            if (signers)
            {
                for (i = 0; i < data->csSigners; i++)
                {
                    signers[i].u.cbSize =
                     sizeof(WTD_GENERIC_CHAIN_POLICY_SIGNER_INFO);
                    signers[i].pChainContext =
                     data->pasSigners[i].pChainContext;
                    signers[i].dwSignerType = data->pasSigners[i].dwSignerType;
                    signers[i].pMsgSignerInfo = data->pasSigners[i].psSigner;
                    signers[i].dwError = data->pasSigners[i].dwError;
                    if (data->pasSigners[i].csCounterSigners)
                        FIXME("unimplemented for counter signers\n");
                    signers[i].cCounterSigner = 0;
                    signers[i].rgpCounterSigner = NULL;
                }
            }
            else
                err = ERROR_OUTOFMEMORY;
        }
1062
        if (err == NO_ERROR)
1063 1064 1065 1066
            err = policyCallback(data, TRUSTERROR_STEP_FINAL_POLICYPROV,
             data->dwRegPolicySettings, data->csSigners, signers, policyArg);
        data->psPfns->pfnFree(signers);
    }
1067
    if (err != NO_ERROR)
1068
        data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_POLICYPROV] = err;
1069
    TRACE("returning %d (%08x)\n", err == NO_ERROR ? S_OK : S_FALSE,
1070 1071 1072 1073
     data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_POLICYPROV]);
    return err == NO_ERROR ? S_OK : S_FALSE;
}

1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102
HRESULT WINAPI SoftpubCleanup(CRYPT_PROVIDER_DATA *data)
{
    DWORD i, j;

    for (i = 0; i < data->csSigners; i++)
    {
        for (j = 0; j < data->pasSigners[i].csCertChain; j++)
            CertFreeCertificateContext(data->pasSigners[i].pasCertChain[j].pCert);
        data->psPfns->pfnFree(data->pasSigners[i].pasCertChain);
        data->psPfns->pfnFree(data->pasSigners[i].psSigner);
        CertFreeCertificateChain(data->pasSigners[i].pChainContext);
    }
    data->psPfns->pfnFree(data->pasSigners);

    for (i = 0; i < data->chStores; i++)
        CertCloseStore(data->pahStores[i], 0);
    data->psPfns->pfnFree(data->pahStores);

    if (data->u.pPDSip)
    {
        data->psPfns->pfnFree(data->u.pPDSip->pSip);
        data->psPfns->pfnFree(data->u.pPDSip->pCATSip);
        data->psPfns->pfnFree(data->u.pPDSip->psSipSubjectInfo);
        data->psPfns->pfnFree(data->u.pPDSip->psSipCATSubjectInfo);
        data->psPfns->pfnFree(data->u.pPDSip->psIndirectData);
    }

    CryptMsgClose(data->hMsg);

1103 1104
    if (data->fOpenedFile &&
     data->pWintrustData->dwUnionChoice == WTD_CHOICE_FILE)
1105 1106 1107 1108
        CloseHandle(data->pWintrustData->u.pFile->hFile);

    return S_OK;
}
1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120

HRESULT WINAPI HTTPSCertificateTrust(CRYPT_PROVIDER_DATA *data)
{
    FIXME("(%p)\n", data);
    return S_OK;
}

HRESULT WINAPI HTTPSFinalProv(CRYPT_PROVIDER_DATA *data)
{
    FIXME("(%p)\n", data);
    return S_OK;
}