diff --git a/dlls/crypt32/crypt32_private.h b/dlls/crypt32/crypt32_private.h index dbd19e6c914..49ef40052e0 100644 --- a/dlls/crypt32/crypt32_private.h +++ b/dlls/crypt32/crypt32_private.h @@ -460,11 +460,17 @@ void init_empty_store(void) DECLSPEC_HIDDEN; /* Unix interface */ +struct cert_store_data; + struct unix_funcs { BOOL (WINAPI *enum_root_certs)( void *buffer, SIZE_T size, SIZE_T *needed ); - BOOL (WINAPI *import_cert_store)( CRYPT_DATA_BLOB *pfx, const WCHAR *password, DWORD flags, - void **key_ret, void ***chain_ret, DWORD *count_ret ); + BOOL (WINAPI *open_cert_store)( CRYPT_DATA_BLOB *pfx, const WCHAR *password, + struct cert_store_data **data_ret ); + NTSTATUS (WINAPI *import_store_key)( struct cert_store_data *data, void *buf, DWORD *buf_size ); + NTSTATUS (WINAPI *import_store_cert)( struct cert_store_data *data, unsigned int index, + void *buf, DWORD *buf_size ); + void (WINAPI *close_cert_store)( struct cert_store_data *data ); }; extern const struct unix_funcs *unix_funcs DECLSPEC_HIDDEN; diff --git a/dlls/crypt32/pfx.c b/dlls/crypt32/pfx.c index ecb78310750..eca4c788784 100644 --- a/dlls/crypt32/pfx.c +++ b/dlls/crypt32/pfx.c @@ -17,7 +17,10 @@ */ #include +#include +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "wincrypt.h" @@ -29,13 +32,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(crypt); -static HCRYPTPROV import_key( void *key, DWORD flags ) +static HCRYPTPROV import_key( struct cert_store_data *data, DWORD flags ) { HCRYPTPROV prov = 0; HCRYPTKEY cryptkey; DWORD size, acquire_flags; + void *key; + + if (unix_funcs->import_store_key( data, NULL, &size ) != STATUS_BUFFER_TOO_SMALL) return 0; - size = HeapSize( GetProcessHeap(), 0, key ); acquire_flags = (flags & CRYPT_MACHINE_KEYSET) | CRYPT_NEWKEYSET; if (!CryptAcquireContextW( &prov, NULL, MS_ENHANCED_PROV_W, PROV_RSA_FULL, acquire_flags )) { @@ -49,13 +54,17 @@ static HCRYPTPROV import_key( void *key, DWORD flags ) } } - if (!CryptImportKey( prov, key, size, 0, flags & CRYPT_EXPORTABLE, &cryptkey )) + key = malloc( size ); + if (unix_funcs->import_store_key( data, key, &size ) || + !CryptImportKey( prov, key, size, 0, flags & CRYPT_EXPORTABLE, &cryptkey )) { WARN( "CryptImportKey failed %08x\n", GetLastError() ); CryptReleaseContext( prov, 0 ); + free( key ); return 0; } CryptDestroyKey( cryptkey ); + free( key ); return prov; } @@ -132,10 +141,10 @@ static BOOL set_key_prov_info( const void *ctx, HCRYPTPROV prov ) HCERTSTORE WINAPI PFXImportCertStore( CRYPT_DATA_BLOB *pfx, const WCHAR *password, DWORD flags ) { - void *key, **chain; - DWORD i, chain_len = 0; + DWORD i = 0, size; HCERTSTORE store = NULL; HCRYPTPROV prov = 0; + struct cert_store_data *data = NULL; if (!pfx) { @@ -147,15 +156,14 @@ HCERTSTORE WINAPI PFXImportCertStore( CRYPT_DATA_BLOB *pfx, const WCHAR *passwor FIXME( "flags %08x not supported\n", flags ); return NULL; } - if (!unix_funcs->import_cert_store) + if (!unix_funcs->open_cert_store) { FIXME( "(%p, %p, %08x)\n", pfx, password, flags ); return NULL; } - if (!unix_funcs->import_cert_store( pfx, password, flags, &key, &chain, &chain_len )) return NULL; + if (!unix_funcs->open_cert_store( pfx, password, &data )) return NULL; - prov = import_key( key, flags ); - heap_free( key ); + prov = import_key( data, flags ); if (!prov) goto error; if (!(store = CertOpenStore( CERT_STORE_PROV_MEMORY, 0, 0, 0, NULL ))) @@ -164,14 +172,17 @@ HCERTSTORE WINAPI PFXImportCertStore( CRYPT_DATA_BLOB *pfx, const WCHAR *passwor goto error; } - if (chain_len > 1) FIXME( "handle certificate chain\n" ); - for (i = 0; i < chain_len; i++) + for (;;) { - const void *ctx; - size_t size = HeapSize( GetProcessHeap(), 0, chain[i] ); + const void *ctx = NULL; + void *cert; - if (!(ctx = CertCreateContext( CERT_STORE_CERTIFICATE_CONTEXT, X509_ASN_ENCODING, - chain[i], size, 0, NULL ))) + if (unix_funcs->import_store_cert( data, i, NULL, &size ) != STATUS_BUFFER_TOO_SMALL) break; + cert = malloc( size ); + if (!unix_funcs->import_store_cert( data, i++, cert, &size )) + ctx = CertCreateContext( CERT_STORE_CERTIFICATE_CONTEXT, X509_ASN_ENCODING, cert, size, 0, NULL ); + free( cert ); + if (!ctx) { WARN( "CertCreateContext failed %08x\n", GetLastError() ); goto error; @@ -199,15 +210,13 @@ HCERTSTORE WINAPI PFXImportCertStore( CRYPT_DATA_BLOB *pfx, const WCHAR *passwor } CertFreeCertificateContext( ctx ); } - while (chain_len) heap_free( chain[--chain_len] ); - heap_free( chain ); + unix_funcs->close_cert_store( data ); return store; error: CryptReleaseContext( prov, 0 ); CertCloseStore( store, 0 ); - while (chain_len) heap_free( chain[--chain_len] ); - heap_free( chain ); + if (data) unix_funcs->close_cert_store( data ); return NULL; } diff --git a/dlls/crypt32/unixlib.c b/dlls/crypt32/unixlib.c index 702cccb5615..b3cfd95771c 100644 --- a/dlls/crypt32/unixlib.c +++ b/dlls/crypt32/unixlib.c @@ -155,38 +155,37 @@ static void gnutls_uninitialize(void) #define RSA_MAGIC_KEY ('R' | ('S' << 8) | ('A' << 16) | ('2' << 24)) #define RSA_PUBEXP 65537 -static DWORD import_key( gnutls_x509_privkey_t key, DWORD flags, void **data_ret ) +struct cert_store_data +{ + gnutls_pkcs12_t p12; + gnutls_x509_privkey_t key; + gnutls_x509_crt_t *chain; + unsigned int key_bitlen; + unsigned int chain_len; +}; + +static NTSTATUS WINAPI import_store_key( struct cert_store_data *data, void *buf, DWORD *buf_size ) { int i, ret; - unsigned int bitlen; + unsigned int bitlen = data->key_bitlen; gnutls_datum_t m, e, d, p, q, u, e1, e2; BLOBHEADER *hdr; RSAPUBKEY *rsakey; - BYTE *buf, *src, *dst; + BYTE *src, *dst; DWORD size; - *data_ret = NULL; - - if ((ret = pgnutls_x509_privkey_get_pk_algorithm2( key, &bitlen )) < 0) - { - pgnutls_perror( ret ); - return 0; - } - - if (ret != GNUTLS_PK_RSA) - { - FIXME( "key algorithm %u not supported\n", ret ); - return 0; - } - - if ((ret = pgnutls_x509_privkey_export_rsa_raw2( key, &m, &e, &d, &p, &q, &u, &e1, &e2 )) < 0) - { - pgnutls_perror( ret ); - return 0; - } - size = sizeof(*hdr) + sizeof(*rsakey) + (bitlen * 9 / 16); - if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, size ))) goto done; + if (!buf || *buf_size < size) + { + *buf_size = size; + return STATUS_BUFFER_TOO_SMALL; + } + + if ((ret = pgnutls_x509_privkey_export_rsa_raw2( data->key, &m, &e, &d, &p, &q, &u, &e1, &e2 )) < 0) + { + pgnutls_perror( ret ); + return STATUS_INVALID_PARAMETER; + } hdr = (BLOBHEADER *)buf; hdr->bType = PRIVATEKEYBLOB; @@ -235,8 +234,6 @@ static DWORD import_key( gnutls_x509_privkey_t key, DWORD flags, void **data_ret else src = d.data; for (i = bitlen / 8 - 1; i >= 0; i--) *dst++ = src[i]; - *data_ret = buf; - done: free( m.data ); free( e.data ); @@ -246,8 +243,7 @@ done: free( u.data ); free( e1.data ); free( e2.data ); - if (!*data_ret) RtlFreeHeap( GetProcessHeap(), 0, buf ); - return size; + return STATUS_SUCCESS; } static char *password_to_ascii( const WCHAR *str ) @@ -265,16 +261,18 @@ static char *password_to_ascii( const WCHAR *str ) return ret; } -static BOOL WINAPI import_cert_store( CRYPT_DATA_BLOB *pfx, const WCHAR *password, DWORD flags, - void **key_ret, void ***chain_ret, DWORD *count_ret ) +static BOOL WINAPI open_cert_store( CRYPT_DATA_BLOB *pfx, const WCHAR *password, + struct cert_store_data **data_ret ) { gnutls_pkcs12_t p12; gnutls_datum_t pfx_data; gnutls_x509_privkey_t key; gnutls_x509_crt_t *chain; - unsigned int chain_len, i; + unsigned int chain_len; + unsigned int bitlen; char *pwd = NULL; int ret; + struct cert_store_data *store_data; if (password && !(pwd = password_to_ascii( password ))) return FALSE; @@ -287,27 +285,25 @@ static BOOL WINAPI import_cert_store( CRYPT_DATA_BLOB *pfx, const WCHAR *passwor if ((ret = pgnutls_pkcs12_simple_parse( p12, pwd ? pwd : "", &key, &chain, &chain_len, NULL, NULL, NULL, 0 )) < 0) goto error; - if (!import_key( key, flags, key_ret )) goto error; + if ((ret = pgnutls_x509_privkey_get_pk_algorithm2( key, &bitlen )) < 0) + goto error; - *chain_ret = RtlAllocateHeap( GetProcessHeap(), 0, chain_len * sizeof(*chain_ret) ); - *count_ret = chain_len; - for (i = 0; i < chain_len; i++) + free( pwd ); + + if (ret != GNUTLS_PK_RSA) { - size_t size = 0; - - if ((ret = pgnutls_x509_crt_export( chain[i], GNUTLS_X509_FMT_DER, NULL, &size )) != GNUTLS_E_SHORT_MEMORY_BUFFER) - goto error; - - (*chain_ret)[i] = RtlAllocateHeap( GetProcessHeap(), 0, size ); - if ((ret = pgnutls_x509_crt_export( chain[i], GNUTLS_X509_FMT_DER, (*chain_ret)[i], &size )) < 0) - { - i++; - while (i) RtlFreeHeap( GetProcessHeap(), 0, (*chain_ret)[--i] ); - RtlFreeHeap( GetProcessHeap(), 0, *chain_ret ); - goto error; - } + FIXME( "key algorithm %u not supported\n", ret ); + pgnutls_pkcs12_deinit( p12 ); + return FALSE; } - pgnutls_pkcs12_deinit( p12 ); + + store_data = malloc( sizeof(*store_data) ); + store_data->p12 = p12; + store_data->key = key; + store_data->chain = chain; + store_data->key_bitlen = bitlen; + store_data->chain_len = chain_len; + *data_ret = store_data; return TRUE; error: @@ -317,6 +313,34 @@ error: return FALSE; } +static NTSTATUS WINAPI import_store_cert( struct cert_store_data *data, unsigned int index, + void *buf, DWORD *buf_size ) +{ + size_t size = 0; + int ret; + + if (index >= data->chain_len) return STATUS_NO_MORE_ENTRIES; + + if ((ret = pgnutls_x509_crt_export( data->chain[index], GNUTLS_X509_FMT_DER, NULL, &size )) != GNUTLS_E_SHORT_MEMORY_BUFFER) + return STATUS_INVALID_PARAMETER; + + if (!buf || *buf_size < size) + { + *buf_size = size; + return STATUS_BUFFER_TOO_SMALL; + } + if ((ret = pgnutls_x509_crt_export( data->chain[index], GNUTLS_X509_FMT_DER, buf, &size )) < 0) + return STATUS_INVALID_PARAMETER; + + return STATUS_SUCCESS; +} + +static void WINAPI close_cert_store( struct cert_store_data *data ) +{ + pgnutls_pkcs12_deinit( data->p12 ); + free( data ); +} + #endif /* SONAME_LIBGNUTLS */ struct root_cert @@ -649,7 +673,13 @@ NTSTATUS CDECL __wine_init_unix_lib( HMODULE module, DWORD reason, const void *p { case DLL_PROCESS_ATTACH: #ifdef SONAME_LIBGNUTLS - if (gnutls_initialize()) funcs.import_cert_store = import_cert_store; + if (gnutls_initialize()) + { + funcs.open_cert_store = open_cert_store; + funcs.import_store_key = import_store_key; + funcs.import_store_cert = import_store_cert; + funcs.close_cert_store = close_cert_store; + } #endif *(const struct unix_funcs **)ptr_out = &funcs; break;