/* * Copyright 2009 Henri Verbeet for CodeWeavers * * 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 "config.h" #include "wine/port.h" #include #ifdef HAVE_COMMONCRYPTO_COMMONDIGEST_H #include #include #elif defined(SONAME_LIBGNUTLS) #include #include #endif #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "ntsecapi.h" #include "bcrypt.h" #include "wine/debug.h" #include "wine/library.h" #include "wine/unicode.h" WINE_DEFAULT_DEBUG_CHANNEL(bcrypt); static HINSTANCE instance; #if defined(HAVE_GNUTLS_HASH) && !defined(HAVE_COMMONCRYPTO_COMMONDIGEST_H) WINE_DECLARE_DEBUG_CHANNEL(winediag); static void *libgnutls_handle; #define MAKE_FUNCPTR(f) static typeof(f) * p##f MAKE_FUNCPTR(gnutls_global_deinit); MAKE_FUNCPTR(gnutls_global_init); MAKE_FUNCPTR(gnutls_global_set_log_function); MAKE_FUNCPTR(gnutls_global_set_log_level); MAKE_FUNCPTR(gnutls_hash); MAKE_FUNCPTR(gnutls_hash_deinit); MAKE_FUNCPTR(gnutls_hash_init); MAKE_FUNCPTR(gnutls_hmac); MAKE_FUNCPTR(gnutls_hmac_deinit); MAKE_FUNCPTR(gnutls_hmac_init); MAKE_FUNCPTR(gnutls_perror); #undef MAKE_FUNCPTR static void gnutls_log( int level, const char *msg ) { TRACE( "<%d> %s", level, msg ); } static BOOL gnutls_initialize(void) { int ret; if (!(libgnutls_handle = wine_dlopen( SONAME_LIBGNUTLS, RTLD_NOW, NULL, 0 ))) { ERR_(winediag)( "failed to load libgnutls, no support for crypto hashes\n" ); return FALSE; } #define LOAD_FUNCPTR(f) \ if (!(p##f = wine_dlsym( libgnutls_handle, #f, NULL, 0 ))) \ { \ ERR( "failed to load %s\n", #f ); \ goto fail; \ } LOAD_FUNCPTR(gnutls_global_deinit) LOAD_FUNCPTR(gnutls_global_init) LOAD_FUNCPTR(gnutls_global_set_log_function) LOAD_FUNCPTR(gnutls_global_set_log_level) LOAD_FUNCPTR(gnutls_hash); LOAD_FUNCPTR(gnutls_hash_deinit); LOAD_FUNCPTR(gnutls_hash_init); LOAD_FUNCPTR(gnutls_hmac); LOAD_FUNCPTR(gnutls_hmac_deinit); LOAD_FUNCPTR(gnutls_hmac_init); LOAD_FUNCPTR(gnutls_perror) #undef LOAD_FUNCPTR if ((ret = pgnutls_global_init()) != GNUTLS_E_SUCCESS) { pgnutls_perror( ret ); goto fail; } if (TRACE_ON( bcrypt )) { pgnutls_global_set_log_level( 4 ); pgnutls_global_set_log_function( gnutls_log ); } return TRUE; fail: wine_dlclose( libgnutls_handle, NULL, 0 ); libgnutls_handle = NULL; return FALSE; } static void gnutls_uninitialize(void) { pgnutls_global_deinit(); wine_dlclose( libgnutls_handle, NULL, 0 ); libgnutls_handle = NULL; } #endif /* HAVE_GNUTLS_HASH && !HAVE_COMMONCRYPTO_COMMONDIGEST_H */ NTSTATUS WINAPI BCryptEnumAlgorithms(ULONG dwAlgOperations, ULONG *pAlgCount, BCRYPT_ALGORITHM_IDENTIFIER **ppAlgList, ULONG dwFlags) { FIXME("%08x, %p, %p, %08x - stub\n", dwAlgOperations, pAlgCount, ppAlgList, dwFlags); *ppAlgList=NULL; *pAlgCount=0; return STATUS_NOT_IMPLEMENTED; } NTSTATUS WINAPI BCryptGenRandom(BCRYPT_ALG_HANDLE algorithm, UCHAR *buffer, ULONG count, ULONG flags) { const DWORD supported_flags = BCRYPT_USE_SYSTEM_PREFERRED_RNG; TRACE("%p, %p, %u, %08x - semi-stub\n", algorithm, buffer, count, flags); if (!algorithm) { /* It's valid to call without an algorithm if BCRYPT_USE_SYSTEM_PREFERRED_RNG * is set. In this case the preferred system RNG is used. */ if (!(flags & BCRYPT_USE_SYSTEM_PREFERRED_RNG)) return STATUS_INVALID_HANDLE; } if (!buffer) return STATUS_INVALID_PARAMETER; if (flags & ~supported_flags) FIXME("unsupported flags %08x\n", flags & ~supported_flags); if (algorithm) FIXME("ignoring selected algorithm\n"); /* When zero bytes are requested the function returns success too. */ if (!count) return STATUS_SUCCESS; if (flags & BCRYPT_USE_SYSTEM_PREFERRED_RNG) { if (RtlGenRandom(buffer, count)) return STATUS_SUCCESS; } FIXME("called with unsupported parameters, returning error\n"); return STATUS_NOT_IMPLEMENTED; } #define MAGIC_ALG (('A' << 24) | ('L' << 16) | ('G' << 8) | '0') #define MAGIC_HASH (('H' << 24) | ('A' << 16) | ('S' << 8) | 'H') struct object { ULONG magic; }; enum alg_id { ALG_ID_MD5, ALG_ID_SHA1, ALG_ID_SHA256, ALG_ID_SHA384, ALG_ID_SHA512 }; static const struct { ULONG hash_length; const WCHAR *alg_name; } alg_props[] = { /* ALG_ID_MD5 */ { 16, BCRYPT_MD5_ALGORITHM }, /* ALG_ID_SHA1 */ { 20, BCRYPT_SHA1_ALGORITHM }, /* ALG_ID_SHA256 */ { 32, BCRYPT_SHA256_ALGORITHM }, /* ALG_ID_SHA384 */ { 48, BCRYPT_SHA384_ALGORITHM }, /* ALG_ID_SHA512 */ { 64, BCRYPT_SHA512_ALGORITHM } }; struct algorithm { struct object hdr; enum alg_id id; BOOL hmac; }; NTSTATUS WINAPI BCryptOpenAlgorithmProvider( BCRYPT_ALG_HANDLE *handle, LPCWSTR id, LPCWSTR implementation, DWORD flags ) { struct algorithm *alg; enum alg_id alg_id; const DWORD supported_flags = BCRYPT_ALG_HANDLE_HMAC_FLAG; TRACE( "%p, %s, %s, %08x\n", handle, wine_dbgstr_w(id), wine_dbgstr_w(implementation), flags ); if (!handle || !id) return STATUS_INVALID_PARAMETER; if (flags & ~supported_flags) { FIXME( "unsupported flags %08x\n", flags & ~supported_flags); return STATUS_NOT_IMPLEMENTED; } if (!strcmpW( id, BCRYPT_SHA1_ALGORITHM )) alg_id = ALG_ID_SHA1; else if (!strcmpW( id, BCRYPT_MD5_ALGORITHM )) alg_id = ALG_ID_MD5; else if (!strcmpW( id, BCRYPT_SHA256_ALGORITHM )) alg_id = ALG_ID_SHA256; else if (!strcmpW( id, BCRYPT_SHA384_ALGORITHM )) alg_id = ALG_ID_SHA384; else if (!strcmpW( id, BCRYPT_SHA512_ALGORITHM )) alg_id = ALG_ID_SHA512; else { FIXME( "algorithm %s not supported\n", debugstr_w(id) ); return STATUS_NOT_IMPLEMENTED; } if (implementation && strcmpW( implementation, MS_PRIMITIVE_PROVIDER )) { FIXME( "implementation %s not supported\n", debugstr_w(implementation) ); return STATUS_NOT_IMPLEMENTED; } if (!(alg = HeapAlloc( GetProcessHeap(), 0, sizeof(*alg) ))) return STATUS_NO_MEMORY; alg->hdr.magic = MAGIC_ALG; alg->id = alg_id; alg->hmac = flags & BCRYPT_ALG_HANDLE_HMAC_FLAG; *handle = alg; return STATUS_SUCCESS; } NTSTATUS WINAPI BCryptCloseAlgorithmProvider( BCRYPT_ALG_HANDLE handle, DWORD flags ) { struct algorithm *alg = handle; TRACE( "%p, %08x\n", handle, flags ); if (!alg || alg->hdr.magic != MAGIC_ALG) return STATUS_INVALID_HANDLE; HeapFree( GetProcessHeap(), 0, alg ); return STATUS_SUCCESS; } NTSTATUS WINAPI BCryptGetFipsAlgorithmMode(BOOLEAN *enabled) { FIXME("%p - semi-stub\n", enabled); if (!enabled) return STATUS_INVALID_PARAMETER; *enabled = FALSE; return STATUS_SUCCESS; } #ifdef HAVE_COMMONCRYPTO_COMMONDIGEST_H struct hash { struct object hdr; enum alg_id alg_id; BOOL hmac; union { CC_MD5_CTX md5_ctx; CC_SHA1_CTX sha1_ctx; CC_SHA256_CTX sha256_ctx; CC_SHA512_CTX sha512_ctx; CCHmacContext hmac_ctx; } u; }; static NTSTATUS hash_init( struct hash *hash ) { switch (hash->alg_id) { case ALG_ID_MD5: CC_MD5_Init( &hash->u.md5_ctx ); break; case ALG_ID_SHA1: CC_SHA1_Init( &hash->u.sha1_ctx ); break; case ALG_ID_SHA256: CC_SHA256_Init( &hash->u.sha256_ctx ); break; case ALG_ID_SHA384: CC_SHA384_Init( &hash->u.sha512_ctx ); break; case ALG_ID_SHA512: CC_SHA512_Init( &hash->u.sha512_ctx ); break; default: ERR( "unhandled id %u\n", hash->alg_id ); return STATUS_NOT_IMPLEMENTED; } return STATUS_SUCCESS; } static NTSTATUS hmac_init( struct hash *hash, UCHAR *key, ULONG key_size ) { CCHmacAlgorithm cc_algorithm; switch (hash->alg_id) { case ALG_ID_MD5: cc_algorithm = kCCHmacAlgMD5; break; case ALG_ID_SHA1: cc_algorithm = kCCHmacAlgSHA1; break; case ALG_ID_SHA256: cc_algorithm = kCCHmacAlgSHA256; break; case ALG_ID_SHA384: cc_algorithm = kCCHmacAlgSHA384; break; case ALG_ID_SHA512: cc_algorithm = kCCHmacAlgSHA512; break; default: ERR( "unhandled id %u\n", hash->alg_id ); return STATUS_NOT_IMPLEMENTED; } CCHmacInit( &hash->u.hmac_ctx, cc_algorithm, key, key_size ); return STATUS_SUCCESS; } static NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) { switch (hash->alg_id) { case ALG_ID_MD5: CC_MD5_Update( &hash->u.md5_ctx, input, size ); break; case ALG_ID_SHA1: CC_SHA1_Update( &hash->u.sha1_ctx, input, size ); break; case ALG_ID_SHA256: CC_SHA256_Update( &hash->u.sha256_ctx, input, size ); break; case ALG_ID_SHA384: CC_SHA384_Update( &hash->u.sha512_ctx, input, size ); break; case ALG_ID_SHA512: CC_SHA512_Update( &hash->u.sha512_ctx, input, size ); break; default: ERR( "unhandled id %u\n", hash->alg_id ); return STATUS_NOT_IMPLEMENTED; } return STATUS_SUCCESS; } static NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size ) { CCHmacUpdate( &hash->u.hmac_ctx, input, size ); return STATUS_SUCCESS; } static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size ) { switch (hash->alg_id) { case ALG_ID_MD5: CC_MD5_Final( output, &hash->u.md5_ctx ); break; case ALG_ID_SHA1: CC_SHA1_Final( output, &hash->u.sha1_ctx ); break; case ALG_ID_SHA256: CC_SHA256_Final( output, &hash->u.sha256_ctx ); break; case ALG_ID_SHA384: CC_SHA384_Final( output, &hash->u.sha512_ctx ); break; case ALG_ID_SHA512: CC_SHA512_Final( output, &hash->u.sha512_ctx ); break; default: ERR( "unhandled id %u\n", hash->alg_id ); break; } return STATUS_SUCCESS; } static NTSTATUS hmac_finish( struct hash *hash, UCHAR *output, ULONG size ) { CCHmacFinal( &hash->u.hmac_ctx, output ); return STATUS_SUCCESS; } #elif defined(HAVE_GNUTLS_HASH) struct hash { struct object hdr; enum alg_id alg_id; BOOL hmac; union { gnutls_hash_hd_t hash_handle; gnutls_hmac_hd_t hmac_handle; } u; }; static NTSTATUS hash_init( struct hash *hash ) { gnutls_digest_algorithm_t alg; if (!libgnutls_handle) return STATUS_INTERNAL_ERROR; switch (hash->alg_id) { case ALG_ID_MD5: alg = GNUTLS_DIG_MD5; break; case ALG_ID_SHA1: alg = GNUTLS_DIG_SHA1; break; case ALG_ID_SHA256: alg = GNUTLS_DIG_SHA256; break; case ALG_ID_SHA384: alg = GNUTLS_DIG_SHA384; break; case ALG_ID_SHA512: alg = GNUTLS_DIG_SHA512; break; default: ERR( "unhandled id %u\n", hash->alg_id ); return STATUS_NOT_IMPLEMENTED; } if (pgnutls_hash_init( &hash->u.hash_handle, alg )) return STATUS_INTERNAL_ERROR; return STATUS_SUCCESS; } static NTSTATUS hmac_init( struct hash *hash, UCHAR *key, ULONG key_size ) { gnutls_mac_algorithm_t alg; if (!libgnutls_handle) return STATUS_INTERNAL_ERROR; switch (hash->alg_id) { case ALG_ID_MD5: alg = GNUTLS_MAC_MD5; break; case ALG_ID_SHA1: alg = GNUTLS_MAC_SHA1; break; case ALG_ID_SHA256: alg = GNUTLS_MAC_SHA256; break; case ALG_ID_SHA384: alg = GNUTLS_MAC_SHA384; break; case ALG_ID_SHA512: alg = GNUTLS_MAC_SHA512; break; default: ERR( "unhandled id %u\n", hash->alg_id ); return STATUS_NOT_IMPLEMENTED; } if (pgnutls_hmac_init( &hash->u.hmac_handle, alg, key, key_size )) return STATUS_INTERNAL_ERROR; return STATUS_SUCCESS; } static NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) { if (pgnutls_hash( hash->u.hash_handle, input, size )) return STATUS_INTERNAL_ERROR; return STATUS_SUCCESS; } static NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size ) { if (pgnutls_hmac( hash->u.hmac_handle, input, size )) return STATUS_INTERNAL_ERROR; return STATUS_SUCCESS; } static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size ) { pgnutls_hash_deinit( hash->u.hash_handle, output ); return STATUS_SUCCESS; } static NTSTATUS hmac_finish( struct hash *hash, UCHAR *output, ULONG size ) { pgnutls_hmac_deinit( hash->u.hmac_handle, output ); return STATUS_SUCCESS; } #else struct hash { struct object hdr; BOOL hmac; enum alg_id alg_id; }; static NTSTATUS hash_init( struct hash *hash ) { ERR( "support for hashes not available at build time\n" ); return STATUS_NOT_IMPLEMENTED; } static NTSTATUS hmac_init( struct hash *hash, UCHAR *key, ULONG key_size ) { ERR( "support for hashes not available at build time\n" ); return STATUS_NOT_IMPLEMENTED; } static NTSTATUS hash_update( struct hash *hash, UCHAR *input, ULONG size ) { ERR( "support for hashes not available at build time\n" ); return STATUS_NOT_IMPLEMENTED; } static NTSTATUS hmac_update( struct hash *hash, UCHAR *input, ULONG size ) { ERR( "support for hashes not available at build time\n" ); return STATUS_NOT_IMPLEMENTED; } static NTSTATUS hash_finish( struct hash *hash, UCHAR *output, ULONG size ) { ERR( "support for hashes not available at build time\n" ); return STATUS_NOT_IMPLEMENTED; } static NTSTATUS hmac_finish( struct hash *hash, UCHAR *output, ULONG size ) { ERR( "support for hashes not available at build time\n" ); return STATUS_NOT_IMPLEMENTED; } #endif #define OBJECT_LENGTH_MD5 274 #define OBJECT_LENGTH_SHA1 278 #define OBJECT_LENGTH_SHA256 286 #define OBJECT_LENGTH_SHA384 382 #define OBJECT_LENGTH_SHA512 382 static NTSTATUS generic_alg_property( enum alg_id id, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) { if (!strcmpW( prop, BCRYPT_HASH_LENGTH )) { *ret_size = sizeof(ULONG); if (size < sizeof(ULONG)) return STATUS_BUFFER_TOO_SMALL; if(buf) *(ULONG*)buf = alg_props[id].hash_length; return STATUS_SUCCESS; } if (!strcmpW( prop, BCRYPT_ALGORITHM_NAME )) { *ret_size = (strlenW(alg_props[id].alg_name)+1)*sizeof(WCHAR); if (size < *ret_size) return STATUS_BUFFER_TOO_SMALL; if(buf) memcpy(buf, alg_props[id].alg_name, *ret_size); return STATUS_SUCCESS; } return STATUS_NOT_IMPLEMENTED; } static NTSTATUS get_alg_property( enum alg_id id, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) { NTSTATUS status; ULONG value; status = generic_alg_property( id, prop, buf, size, ret_size ); if (status != STATUS_NOT_IMPLEMENTED) return status; switch (id) { case ALG_ID_MD5: if (!strcmpW( prop, BCRYPT_OBJECT_LENGTH )) { value = OBJECT_LENGTH_MD5; break; } FIXME( "unsupported md5 algorithm property %s\n", debugstr_w(prop) ); return STATUS_NOT_IMPLEMENTED; case ALG_ID_SHA1: if (!strcmpW( prop, BCRYPT_OBJECT_LENGTH )) { value = OBJECT_LENGTH_SHA1; break; } FIXME( "unsupported sha1 algorithm property %s\n", debugstr_w(prop) ); return STATUS_NOT_IMPLEMENTED; case ALG_ID_SHA256: if (!strcmpW( prop, BCRYPT_OBJECT_LENGTH )) { value = OBJECT_LENGTH_SHA256; break; } FIXME( "unsupported sha256 algorithm property %s\n", debugstr_w(prop) ); return STATUS_NOT_IMPLEMENTED; case ALG_ID_SHA384: if (!strcmpW( prop, BCRYPT_OBJECT_LENGTH )) { value = OBJECT_LENGTH_SHA384; break; } FIXME( "unsupported sha384 algorithm property %s\n", debugstr_w(prop) ); return STATUS_NOT_IMPLEMENTED; case ALG_ID_SHA512: if (!strcmpW( prop, BCRYPT_OBJECT_LENGTH )) { value = OBJECT_LENGTH_SHA512; break; } FIXME( "unsupported sha512 algorithm property %s\n", debugstr_w(prop) ); return STATUS_NOT_IMPLEMENTED; default: FIXME( "unsupported algorithm %u\n", id ); return STATUS_NOT_IMPLEMENTED; } if (size < sizeof(ULONG)) { *ret_size = sizeof(ULONG); return STATUS_BUFFER_TOO_SMALL; } if (buf) *(ULONG *)buf = value; *ret_size = sizeof(ULONG); return STATUS_SUCCESS; } static NTSTATUS get_hash_property( enum alg_id id, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) { NTSTATUS status; status = generic_alg_property( id, prop, buf, size, ret_size ); if (status == STATUS_NOT_IMPLEMENTED) FIXME( "unsupported property %s\n", debugstr_w(prop) ); return status; } NTSTATUS WINAPI BCryptGetProperty( BCRYPT_HANDLE handle, LPCWSTR prop, UCHAR *buffer, ULONG count, ULONG *res, ULONG flags ) { struct object *object = handle; TRACE( "%p, %s, %p, %u, %p, %08x\n", handle, wine_dbgstr_w(prop), buffer, count, res, flags ); if (!object) return STATUS_INVALID_HANDLE; if (!prop || !res) return STATUS_INVALID_PARAMETER; switch (object->magic) { case MAGIC_ALG: { const struct algorithm *alg = (const struct algorithm *)object; return get_alg_property( alg->id, prop, buffer, count, res ); } case MAGIC_HASH: { const struct hash *hash = (const struct hash *)object; return get_hash_property( hash->alg_id, prop, buffer, count, res ); } default: WARN( "unknown magic %08x\n", object->magic ); return STATUS_INVALID_HANDLE; } } NTSTATUS WINAPI BCryptCreateHash( BCRYPT_ALG_HANDLE algorithm, BCRYPT_HASH_HANDLE *handle, UCHAR *object, ULONG objectlen, UCHAR *secret, ULONG secretlen, ULONG flags ) { struct algorithm *alg = algorithm; struct hash *hash; NTSTATUS status; TRACE( "%p, %p, %p, %u, %p, %u, %08x - stub\n", algorithm, handle, object, objectlen, secret, secretlen, flags ); if (flags) { FIXME( "unimplemented flags %08x\n", flags ); return STATUS_NOT_IMPLEMENTED; } if (!alg || alg->hdr.magic != MAGIC_ALG) return STATUS_INVALID_HANDLE; if (object) FIXME( "ignoring object buffer\n" ); if (!(hash = HeapAlloc( GetProcessHeap(), 0, sizeof(*hash) ))) return STATUS_NO_MEMORY; hash->hdr.magic = MAGIC_HASH; hash->alg_id = alg->id; hash->hmac = alg->hmac; if (hash->hmac) { status = hmac_init( hash, secret, secretlen ); } else { status = hash_init( hash ); } if (status != STATUS_SUCCESS) { HeapFree( GetProcessHeap(), 0, hash ); return status; } *handle = hash; return STATUS_SUCCESS; } NTSTATUS WINAPI BCryptDestroyHash( BCRYPT_HASH_HANDLE handle ) { struct hash *hash = handle; TRACE( "%p\n", handle ); if (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_HANDLE; HeapFree( GetProcessHeap(), 0, hash ); return STATUS_SUCCESS; } NTSTATUS WINAPI BCryptHashData( BCRYPT_HASH_HANDLE handle, UCHAR *input, ULONG size, ULONG flags ) { struct hash *hash = handle; TRACE( "%p, %p, %u, %08x\n", handle, input, size, flags ); if (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_HANDLE; if (!input) return STATUS_SUCCESS; if (hash->hmac) { return hmac_update( hash, input, size ); } else { return hash_update( hash, input, size ); } } NTSTATUS WINAPI BCryptFinishHash( BCRYPT_HASH_HANDLE handle, UCHAR *output, ULONG size, ULONG flags ) { struct hash *hash = handle; TRACE( "%p, %p, %u, %08x\n", handle, output, size, flags ); if (!hash || hash->hdr.magic != MAGIC_HASH) return STATUS_INVALID_HANDLE; if (!output) return STATUS_INVALID_PARAMETER; if (hash->hmac) { return hmac_finish( hash, output, size ); } else { return hash_finish( hash, output, size ); } } BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) { switch (reason) { case DLL_PROCESS_ATTACH: instance = hinst; DisableThreadLibraryCalls( hinst ); #if defined(HAVE_GNUTLS_HASH) && !defined(HAVE_COMMONCRYPTO_COMMONDIGEST_H) gnutls_initialize(); #endif break; case DLL_PROCESS_DETACH: if (reserved) break; #if defined(HAVE_GNUTLS_HASH) && !defined(HAVE_COMMONCRYPTO_COMMONDIGEST_H) gnutls_uninitialize(); #endif break; } return TRUE; }