/* * wincapi.c: implementation of wincapi.h. */ #include "putty.h" #if !defined NO_SECURITY #include "putty.h" #include "ssh.h" #include "wincapi.h" DEF_WINDOWS_FUNCTION(CryptProtectMemory); bool got_crypt(void) { static bool attempted = false; static bool successful; static HMODULE crypt; if (!attempted) { attempted = true; crypt = load_system32_dll("crypt32.dll"); successful = crypt && #ifdef COVERITY /* The build toolchain I use with Coverity doesn't know * about this function, so can't type-check it */ GET_WINDOWS_FUNCTION_NO_TYPECHECK(crypt, CryptProtectMemory) #else GET_WINDOWS_FUNCTION(crypt, CryptProtectMemory) #endif ; } return successful; } #ifdef COVERITY /* * The hack I use to build for Coverity scanning, using winegcc and * Makefile.mgw, didn't provide some defines in wincrypt.h last time I * looked. Therefore, define them myself here, but enclosed in #ifdef * COVERITY to ensure I don't make up random nonsense values for any * real build. */ #ifndef CRYPTPROTECTMEMORY_BLOCK_SIZE #define CRYPTPROTECTMEMORY_BLOCK_SIZE 16 #endif #ifndef CRYPTPROTECTMEMORY_CROSS_PROCESS #define CRYPTPROTECTMEMORY_CROSS_PROCESS 1 #endif #endif char *capi_obfuscate_string(const char *realname) { char *cryptdata; int cryptlen; unsigned char digest[32]; char retbuf[65]; int i; cryptlen = strlen(realname) + 1; cryptlen += CRYPTPROTECTMEMORY_BLOCK_SIZE - 1; cryptlen /= CRYPTPROTECTMEMORY_BLOCK_SIZE; cryptlen *= CRYPTPROTECTMEMORY_BLOCK_SIZE; cryptdata = snewn(cryptlen, char); memset(cryptdata, 0, cryptlen); strcpy(cryptdata, realname); /* * CRYPTPROTECTMEMORY_CROSS_PROCESS causes CryptProtectMemory to * use the same key in all processes with this user id, meaning * that the next PuTTY process calling this function with the same * input will get the same data. * * (Contrast with CryptProtectData, which invents a new session * key every time since its API permits returning more data than * was input, so calling _that_ and hashing the output would not * be stable.) * * We don't worry too much if this doesn't work for some reason. * Omitting this step still has _some_ privacy value (in that * another user can test-hash things to confirm guesses as to * where you might be connecting to, but cannot invert SHA-256 in * the absence of any plausible guess). So we don't abort if we * can't call CryptProtectMemory at all, or if it fails. */ if (got_crypt()) p_CryptProtectMemory(cryptdata, cryptlen, CRYPTPROTECTMEMORY_CROSS_PROCESS); /* * We don't want to give away the length of the hostname either, * so having got it back out of CryptProtectMemory we now hash it. */ hash_simple(&ssh_sha256, make_ptrlen(cryptdata, cryptlen), digest); sfree(cryptdata); /* * Finally, make printable. */ for (i = 0; i < 32; i++) { sprintf(retbuf + 2*i, "%02x", digest[i]); /* the last of those will also write the trailing NUL */ } return dupstr(retbuf); } #endif /* !defined NO_SECURITY */