1 /*
2  * wincapi.c: implementation of wincapi.h.
3  */
4 
5 #include "putty.h"
6 
7 #if !defined NO_SECURITY
8 
9 #include "putty.h"
10 #include "ssh.h"
11 
12 #include "wincapi.h"
13 
14 DEF_WINDOWS_FUNCTION(CryptProtectMemory);
15 
got_crypt(void)16 bool got_crypt(void)
17 {
18     static bool attempted = false;
19     static bool successful;
20     static HMODULE crypt;
21 
22     if (!attempted) {
23         attempted = true;
24         crypt = load_system32_dll("crypt32.dll");
25         successful = crypt &&
26             GET_WINDOWS_FUNCTION(crypt, CryptProtectMemory);
27     }
28     return successful;
29 }
30 
capi_obfuscate_string(const char * realname)31 char *capi_obfuscate_string(const char *realname)
32 {
33     char *cryptdata;
34     int cryptlen;
35     unsigned char digest[32];
36     char retbuf[65];
37     int i;
38 
39     cryptlen = strlen(realname) + 1;
40     cryptlen += CRYPTPROTECTMEMORY_BLOCK_SIZE - 1;
41     cryptlen /= CRYPTPROTECTMEMORY_BLOCK_SIZE;
42     cryptlen *= CRYPTPROTECTMEMORY_BLOCK_SIZE;
43 
44     cryptdata = snewn(cryptlen, char);
45     memset(cryptdata, 0, cryptlen);
46     strcpy(cryptdata, realname);
47 
48     /*
49      * CRYPTPROTECTMEMORY_CROSS_PROCESS causes CryptProtectMemory to
50      * use the same key in all processes with this user id, meaning
51      * that the next PuTTY process calling this function with the same
52      * input will get the same data.
53      *
54      * (Contrast with CryptProtectData, which invents a new session
55      * key every time since its API permits returning more data than
56      * was input, so calling _that_ and hashing the output would not
57      * be stable.)
58      *
59      * We don't worry too much if this doesn't work for some reason.
60      * Omitting this step still has _some_ privacy value (in that
61      * another user can test-hash things to confirm guesses as to
62      * where you might be connecting to, but cannot invert SHA-256 in
63      * the absence of any plausible guess). So we don't abort if we
64      * can't call CryptProtectMemory at all, or if it fails.
65      */
66     if (got_crypt())
67         p_CryptProtectMemory(cryptdata, cryptlen,
68                              CRYPTPROTECTMEMORY_CROSS_PROCESS);
69 
70     /*
71      * We don't want to give away the length of the hostname either,
72      * so having got it back out of CryptProtectMemory we now hash it.
73      */
74     {
75         ssh_hash *h = ssh_hash_new(&ssh_sha256);
76         put_string(h, cryptdata, cryptlen);
77         ssh_hash_final(h, digest);
78     }
79 
80     sfree(cryptdata);
81 
82     /*
83      * Finally, make printable.
84      */
85     for (i = 0; i < 32; i++) {
86         sprintf(retbuf + 2*i, "%02x", digest[i]);
87         /* the last of those will also write the trailing NUL */
88     }
89 
90     return dupstr(retbuf);
91 }
92 
93 #endif /* !defined NO_SECURITY */
94