filestore.c 12.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
/*
 * 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 "winnls.h"
#include "wine/debug.h"
24
#include "wine/unicode.h"
25 26 27 28 29 30 31 32 33
#include "crypt32_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(crypt);

typedef struct _WINE_FILESTOREINFO
{
    DWORD      dwOpenFlags;
    HCERTSTORE memStore;
    HANDLE     file;
34
    DWORD      type;
35 36 37 38 39
    BOOL       dirty;
} WINE_FILESTOREINFO, *PWINE_FILESTOREINFO;

static void WINAPI CRYPT_FileCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
{
40
    PWINE_FILESTOREINFO store = hCertStore;
41 42 43

    TRACE("(%p, %08x)\n", store, dwFlags);
    if (store->dirty)
44
        CertSaveStore(store->memStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
45
         store->type, CERT_STORE_SAVE_TO_FILE, store->file, 0);
46 47 48 49 50 51 52
    CloseHandle(store->file);
    CryptMemFree(store);
}

static BOOL WINAPI CRYPT_FileWriteCert(HCERTSTORE hCertStore,
 PCCERT_CONTEXT cert, DWORD dwFlags)
{
53
    PWINE_FILESTOREINFO store = hCertStore;
54 55 56 57 58 59 60 61 62

    TRACE("(%p, %p, %d)\n", hCertStore, cert, dwFlags);
    store->dirty = TRUE;
    return TRUE;
}

static BOOL WINAPI CRYPT_FileDeleteCert(HCERTSTORE hCertStore,
 PCCERT_CONTEXT pCertContext, DWORD dwFlags)
{
63
    PWINE_FILESTOREINFO store = hCertStore;
64 65 66 67 68 69 70 71 72

    TRACE("(%p, %p, %08x)\n", hCertStore, pCertContext, dwFlags);
    store->dirty = TRUE;
    return TRUE;
}

static BOOL WINAPI CRYPT_FileWriteCRL(HCERTSTORE hCertStore,
 PCCRL_CONTEXT crl, DWORD dwFlags)
{
73
    PWINE_FILESTOREINFO store = hCertStore;
74 75 76 77 78 79 80 81 82

    TRACE("(%p, %p, %d)\n", hCertStore, crl, dwFlags);
    store->dirty = TRUE;
    return TRUE;
}

static BOOL WINAPI CRYPT_FileDeleteCRL(HCERTSTORE hCertStore,
 PCCRL_CONTEXT pCrlContext, DWORD dwFlags)
{
83
    PWINE_FILESTOREINFO store = hCertStore;
84 85 86 87 88 89

    TRACE("(%p, %p, %08x)\n", hCertStore, pCrlContext, dwFlags);
    store->dirty = TRUE;
    return TRUE;
}

90 91 92
static BOOL WINAPI CRYPT_FileWriteCTL(HCERTSTORE hCertStore,
 PCCTL_CONTEXT ctl, DWORD dwFlags)
{
93
    PWINE_FILESTOREINFO store = hCertStore;
94 95 96 97 98 99 100 101 102

    TRACE("(%p, %p, %d)\n", hCertStore, ctl, dwFlags);
    store->dirty = TRUE;
    return TRUE;
}

static BOOL WINAPI CRYPT_FileDeleteCTL(HCERTSTORE hCertStore,
 PCCTL_CONTEXT pCtlContext, DWORD dwFlags)
{
103
    PWINE_FILESTOREINFO store = hCertStore;
104 105 106 107 108 109

    TRACE("(%p, %p, %08x)\n", hCertStore, pCtlContext, dwFlags);
    store->dirty = TRUE;
    return TRUE;
}

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
static BOOL CRYPT_ReadBlobFromFile(HANDLE file, PCERT_BLOB blob)
{
    BOOL ret = TRUE;

    blob->cbData = GetFileSize(file, NULL);
    if (blob->cbData)
    {
        blob->pbData = CryptMemAlloc(blob->cbData);
        if (blob->pbData)
        {
            DWORD read;

            ret = ReadFile(file, blob->pbData, blob->cbData, &read, NULL);
        }
    }
    return ret;
}

128 129 130
static BOOL WINAPI CRYPT_FileControl(HCERTSTORE hCertStore, DWORD dwFlags,
 DWORD dwCtrlType, void const *pvCtrlPara)
{
131
    PWINE_FILESTOREINFO store = hCertStore;
132 133 134 135 136 137 138 139
    BOOL ret;

    TRACE("(%p, %08x, %d, %p)\n", hCertStore, dwFlags, dwCtrlType,
     pvCtrlPara);

    switch (dwCtrlType)
    {
    case CERT_STORE_CTRL_RESYNC:
140
        store->dirty = FALSE;
141
        if (store->type == CERT_STORE_SAVE_AS_STORE)
142 143 144 145 146 147 148 149 150 151 152 153
        {
            HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
             CERT_STORE_CREATE_NEW_FLAG, NULL);

            /* FIXME: if I could translate a handle to a path, I could use
             * CryptQueryObject instead, but there's no API to do so yet.
             */
            ret = CRYPT_ReadSerializedStoreFromFile(store->file, memStore);
            if (ret)
                I_CertUpdateStore(store->memStore, memStore, 0, 0);
            CertCloseStore(memStore, 0);
        }
154 155
        else if (store->type == CERT_STORE_SAVE_AS_PKCS7)
        {
156 157 158 159 160 161 162 163 164 165 166
            CERT_BLOB blob = { 0, NULL };

            ret = CRYPT_ReadBlobFromFile(store->file, &blob);
            if (ret)
            {
                HCERTSTORE messageStore;

                ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob,
                 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED,
                 CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL,
                 &messageStore, NULL, NULL);
167 168
                I_CertUpdateStore(store->memStore, messageStore, 0, 0);
                CertCloseStore(messageStore, 0);
169 170
                CryptMemFree(blob.pbData);
            }
171 172 173 174 175 176
        }
        else
        {
            WARN("unknown type %d\n", store->type);
            ret = FALSE;
        }
177 178 179 180 181 182 183 184
        break;
    case CERT_STORE_CTRL_COMMIT:
        if (!(store->dwOpenFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG))
        {
            SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
            ret = FALSE;
        }
        else if (store->dirty)
185 186
            ret = CertSaveStore(store->memStore,
             X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
187
             store->type, CERT_STORE_SAVE_TO_FILE, store->file, 0);
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
        else
            ret = TRUE;
        break;
    default:
        FIXME("%d: stub\n", dwCtrlType);
        ret = FALSE;
    }
    return ret;
}

static void *fileProvFuncs[] = {
    CRYPT_FileCloseStore,
    NULL, /* CERT_STORE_PROV_READ_CERT_FUNC */
    CRYPT_FileWriteCert,
    CRYPT_FileDeleteCert,
    NULL, /* CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC */
    NULL, /* CERT_STORE_PROV_READ_CRL_FUNC */
    CRYPT_FileWriteCRL,
    CRYPT_FileDeleteCRL,
    NULL, /* CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC */
    NULL, /* CERT_STORE_PROV_READ_CTL_FUNC */
209 210
    CRYPT_FileWriteCTL,
    CRYPT_FileDeleteCTL,
211 212 213 214
    NULL, /* CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC */
    CRYPT_FileControl,
};

215
static PWINECRYPT_CERTSTORE CRYPT_CreateFileStore(DWORD dwFlags,
216
 HCERTSTORE memStore, HANDLE file, DWORD type)
217 218 219 220 221 222 223 224 225 226 227
{
    PWINECRYPT_CERTSTORE store = NULL;
    PWINE_FILESTOREINFO info = CryptMemAlloc(sizeof(WINE_FILESTOREINFO));

    if (info)
    {
        CERT_STORE_PROV_INFO provInfo = { 0 };

        info->dwOpenFlags = dwFlags;
        info->memStore = memStore;
        info->file = file;
228
        info->type = type;
229 230 231 232 233 234 235 236 237 238 239
        info->dirty = FALSE;
        provInfo.cbSize = sizeof(provInfo);
        provInfo.cStoreProvFunc = sizeof(fileProvFuncs) /
         sizeof(fileProvFuncs[0]);
        provInfo.rgpvStoreProvFunc = fileProvFuncs;
        provInfo.hStoreProv = info;
        store = CRYPT_ProvCreateStore(dwFlags, memStore, &provInfo);
    }
    return store;
}

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
PWINECRYPT_CERTSTORE CRYPT_FileOpenStore(HCRYPTPROV hCryptProv, DWORD dwFlags,
 const void *pvPara)
{
    PWINECRYPT_CERTSTORE store = NULL;
    HANDLE file = (HANDLE)pvPara;

    TRACE("(%ld, %08x, %p)\n", hCryptProv, dwFlags, pvPara);

    if (!pvPara)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return NULL;
    }
    if (dwFlags & CERT_STORE_DELETE_FLAG)
    {
        SetLastError(E_INVALIDARG);
        return NULL;
    }
    if ((dwFlags & CERT_STORE_READONLY_FLAG) &&
     (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG))
    {
        SetLastError(E_INVALIDARG);
        return NULL;
    }

    if (DuplicateHandle(GetCurrentProcess(), (HANDLE)pvPara,
     GetCurrentProcess(), &file, dwFlags & CERT_STORE_READONLY_FLAG ?
     GENERIC_READ : GENERIC_READ | GENERIC_WRITE, TRUE, 0))
    {
269
        HCERTSTORE memStore;
270

271
        memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
272 273 274
         CERT_STORE_CREATE_NEW_FLAG, NULL);
        if (memStore)
        {
275
            if (CRYPT_ReadSerializedStoreFromFile(file, memStore))
276
            {
277 278
                store = CRYPT_CreateFileStore(dwFlags, memStore, file,
                 CERT_STORE_SAVE_AS_STORE);
279 280 281 282
                /* File store doesn't need crypto provider, so close it */
                if (hCryptProv &&
                 !(dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG))
                    CryptReleaseContext(hCryptProv, 0);
283 284 285 286 287 288 289 290 291 292 293
            }
        }
    }
    TRACE("returning %p\n", store);
    return store;
}

PWINECRYPT_CERTSTORE CRYPT_FileNameOpenStoreW(HCRYPTPROV hCryptProv,
 DWORD dwFlags, const void *pvPara)
{
    HCERTSTORE store = 0;
294
    LPCWSTR fileName = pvPara;
295 296 297 298 299 300 301 302 303 304
    DWORD access, create;
    HANDLE file;

    TRACE("(%ld, %08x, %s)\n", hCryptProv, dwFlags, debugstr_w(fileName));

    if (!fileName)
    {
        SetLastError(ERROR_PATH_NOT_FOUND);
        return NULL;
    }
305 306 307 308 309 310
    if ((dwFlags & CERT_STORE_READONLY_FLAG) &&
     (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG))
    {
        SetLastError(E_INVALIDARG);
        return NULL;
    }
311 312 313 314 315 316 317 318 319 320 321 322 323 324

    access = GENERIC_READ;
    if (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG)
        access |= GENERIC_WRITE;
    if (dwFlags & CERT_STORE_CREATE_NEW_FLAG)
        create = CREATE_NEW;
    else if (dwFlags & CERT_STORE_OPEN_EXISTING_FLAG)
        create = OPEN_EXISTING;
    else
        create = OPEN_ALWAYS;
    file = CreateFileW(fileName, access, FILE_SHARE_READ, NULL, create,
     FILE_ATTRIBUTE_NORMAL, NULL);
    if (file != INVALID_HANDLE_VALUE)
    {
325 326
        HCERTSTORE memStore = NULL;
        DWORD size = GetFileSize(file, NULL), type = 0;
327

328 329
        /* If the file isn't empty, try to get the type from the file itself */
        if (size)
330
        {
331 332 333 334 335 336 337 338 339
            DWORD contentType;
            BOOL ret;

            /* Close the file so CryptQueryObject can succeed.. */
            CloseHandle(file);
            ret = CryptQueryObject(CERT_QUERY_OBJECT_FILE, fileName,
             CERT_QUERY_CONTENT_FLAG_CERT |
             CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
             CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED,
340
             CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &contentType, NULL,
341 342
             &memStore, NULL, NULL);
            if (ret)
343
            {
344 345 346 347 348 349 350
                if (contentType == CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED)
                    type = CERT_STORE_SAVE_AS_PKCS7;
                else
                    type = CERT_STORE_SAVE_AS_STORE;
                /* and reopen the file. */
                file = CreateFileW(fileName, access, FILE_SHARE_READ, NULL,
                 create, FILE_ATTRIBUTE_NORMAL, NULL);
351
            }
352 353 354 355 356 357 358 359
        }
        else
        {
            static const WCHAR spc[] = { 's','p','c',0 };
            static const WCHAR p7c[] = { 'p','7','c',0 };
            LPCWSTR ext = strrchrW(fileName, '.');

            if (ext)
360
            {
361 362 363
                ext++;
                if (!lstrcmpiW(ext, spc) || !lstrcmpiW(ext, p7c))
                    type = CERT_STORE_SAVE_AS_PKCS7;
364
            }
365 366 367 368 369 370 371 372 373 374 375
            if (!type)
                type = CERT_STORE_SAVE_AS_STORE;
            memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
             CERT_STORE_CREATE_NEW_FLAG, NULL);
        }
        if (memStore)
        {
            store = CRYPT_CreateFileStore(dwFlags, memStore, file, type);
            /* File store doesn't need crypto provider, so close it */
            if (hCryptProv && !(dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG))
                CryptReleaseContext(hCryptProv, 0);
376
        }
377
    }
378
    return store;
379 380 381 382 383 384 385 386 387
}

PWINECRYPT_CERTSTORE CRYPT_FileNameOpenStoreA(HCRYPTPROV hCryptProv,
 DWORD dwFlags, const void *pvPara)
{
    int len;
    PWINECRYPT_CERTSTORE ret = NULL;

    TRACE("(%ld, %08x, %s)\n", hCryptProv, dwFlags,
388
     debugstr_a(pvPara));
389 390 391 392 393 394

    if (!pvPara)
    {
        SetLastError(ERROR_FILE_NOT_FOUND);
        return NULL;
    }
395
    len = MultiByteToWideChar(CP_ACP, 0, pvPara, -1, NULL, 0);
396 397 398 399 400 401
    if (len)
    {
        LPWSTR storeName = CryptMemAlloc(len * sizeof(WCHAR));

        if (storeName)
        {
402
            MultiByteToWideChar(CP_ACP, 0, pvPara, -1, storeName, len);
403 404 405 406 407 408
            ret = CRYPT_FileNameOpenStoreW(hCryptProv, dwFlags, storeName);
            CryptMemFree(storeName);
        }
    }
    return ret;
}