]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winshare.c
Fix assorted memory leaks.
[PuTTY.git] / windows / winshare.c
1 /*
2  * Windows implementation of SSH connection-sharing IPC setup.
3  */
4
5 #include <stdio.h>
6 #include <assert.h>
7
8 #if !defined NO_SECURITY
9
10 #define DEFINE_PLUG_METHOD_MACROS
11 #include "tree234.h"
12 #include "putty.h"
13 #include "network.h"
14 #include "proxy.h"
15 #include "ssh.h"
16
17 #include "winsecur.h"
18
19 #ifdef COVERITY
20 /*
21  * The hack I use to build for Coverity scanning, using winegcc and
22  * Makefile.cyg, didn't provide some defines in wincrypt.h last time I
23  * looked. Therefore, define them myself here, but enclosed in #ifdef
24  * COVERITY to ensure I don't make up random nonsense values for any
25  * real build.
26  */
27 #ifndef CRYPTPROTECTMEMORY_BLOCK_SIZE
28 #define CRYPTPROTECTMEMORY_BLOCK_SIZE 16
29 #endif
30 #ifndef CRYPTPROTECTMEMORY_CROSS_PROCESS
31 #define CRYPTPROTECTMEMORY_CROSS_PROCESS 1
32 #endif
33 #endif
34
35 #define CONNSHARE_PIPE_PREFIX "\\\\.\\pipe\\putty-connshare"
36 #define CONNSHARE_MUTEX_PREFIX "Local\\putty-connshare-mutex"
37
38 static char *obfuscate_name(const char *realname)
39 {
40     /*
41      * Windows's named pipes all live in the same namespace, so one
42      * user can see what pipes another user has open. This is an
43      * undesirable privacy leak and in particular permits one user to
44      * know what username@host another user is SSHing to, so we
45      * protect that information by using CryptProtectMemory (which
46      * uses a key built in to each user's account).
47      */
48     char *cryptdata;
49     int cryptlen;
50     SHA256_State sha;
51     unsigned char lenbuf[4];
52     unsigned char digest[32];
53     char retbuf[65];
54     int i;
55
56     cryptlen = strlen(realname) + 1;
57     cryptlen += CRYPTPROTECTMEMORY_BLOCK_SIZE - 1;
58     cryptlen /= CRYPTPROTECTMEMORY_BLOCK_SIZE;
59     cryptlen *= CRYPTPROTECTMEMORY_BLOCK_SIZE;
60
61     cryptdata = snewn(cryptlen, char);
62     memset(cryptdata, 0, cryptlen);
63     strcpy(cryptdata, realname);
64
65     /*
66      * CRYPTPROTECTMEMORY_CROSS_PROCESS causes CryptProtectMemory to
67      * use the same key in all processes with this user id, meaning
68      * that the next PuTTY process calling this function with the same
69      * input will get the same data.
70      *
71      * (Contrast with CryptProtectData, which invents a new session
72      * key every time since its API permits returning more data than
73      * was input, so calling _that_ and hashing the output would not
74      * be stable.)
75      *
76      * We don't worry too much if this doesn't work for some reason.
77      * Omitting this step still has _some_ privacy value (in that
78      * another user can test-hash things to confirm guesses as to
79      * where you might be connecting to, but cannot invert SHA-256 in
80      * the absence of any plausible guess). So we don't abort if we
81      * can't call CryptProtectMemory at all, or if it fails.
82      */
83     if (got_crypt())
84         p_CryptProtectMemory(cryptdata, cryptlen,
85                              CRYPTPROTECTMEMORY_CROSS_PROCESS);
86
87     /*
88      * We don't want to give away the length of the hostname either,
89      * so having got it back out of CryptProtectMemory we now hash it.
90      */
91     SHA256_Init(&sha);
92     PUT_32BIT_MSB_FIRST(lenbuf, cryptlen);
93     SHA256_Bytes(&sha, lenbuf, 4);
94     SHA256_Bytes(&sha, cryptdata, cryptlen);
95     SHA256_Final(&sha, digest);
96
97     sfree(cryptdata);
98
99     /*
100      * Finally, make printable.
101      */
102     for (i = 0; i < 32; i++) {
103         sprintf(retbuf + 2*i, "%02x", digest[i]);
104         /* the last of those will also write the trailing NUL */
105     }
106
107     return dupstr(retbuf);
108 }
109
110 static char *make_name(const char *prefix, const char *name)
111 {
112     char *username, *retname;
113
114     username = get_username();
115     retname = dupprintf("%s.%s.%s", prefix, username, name);
116     sfree(username);
117
118     return retname;
119 }
120
121 Socket new_named_pipe_client(const char *pipename, Plug plug);
122 Socket new_named_pipe_listener(const char *pipename, Plug plug);
123
124 int platform_ssh_share(const char *pi_name, Conf *conf,
125                        Plug downplug, Plug upplug, Socket *sock,
126                        char **logtext, char **ds_err, char **us_err,
127                        int can_upstream, int can_downstream)
128 {
129     char *name, *mutexname, *pipename;
130     HANDLE mutex;
131     Socket retsock;
132     PSECURITY_DESCRIPTOR psd;
133     PACL acl;
134
135     /*
136      * Transform the platform-independent version of the connection
137      * identifier into the obfuscated version we'll use for our
138      * Windows named pipe and mutex. A side effect of doing this is
139      * that it also eliminates any characters illegal in Windows pipe
140      * names.
141      */
142     name = obfuscate_name(pi_name);
143     if (!name) {
144         *logtext = dupprintf("Unable to call CryptProtectMemory: %s",
145                              win_strerror(GetLastError()));
146         return SHARE_NONE;
147     }
148
149     /*
150      * Make a mutex name out of the connection identifier, and lock it
151      * while we decide whether to be upstream or downstream.
152      */
153     {
154         SECURITY_ATTRIBUTES sa;
155
156         mutexname = make_name(CONNSHARE_MUTEX_PREFIX, name);
157         if (!make_private_security_descriptor(MUTEX_ALL_ACCESS,
158                                               &psd, &acl, logtext)) {
159             sfree(mutexname);
160             sfree(name);
161             return SHARE_NONE;
162         }
163
164         memset(&sa, 0, sizeof(sa));
165         sa.nLength = sizeof(sa);
166         sa.lpSecurityDescriptor = psd;
167         sa.bInheritHandle = FALSE;
168
169         mutex = CreateMutex(&sa, FALSE, mutexname);
170
171         if (!mutex) {
172             *logtext = dupprintf("CreateMutex(\"%s\") failed: %s",
173                                  mutexname, win_strerror(GetLastError()));
174             sfree(mutexname);
175             sfree(name);
176             LocalFree(psd);
177             LocalFree(acl);
178             return SHARE_NONE;
179         }
180
181         sfree(mutexname);
182         LocalFree(psd);
183         LocalFree(acl);
184
185         WaitForSingleObject(mutex, INFINITE);
186     }
187
188     pipename = make_name(CONNSHARE_PIPE_PREFIX, name);
189
190     *logtext = NULL;
191
192     if (can_downstream) {
193         retsock = new_named_pipe_client(pipename, downplug);
194         if (sk_socket_error(retsock) == NULL) {
195             sfree(*logtext);
196             *logtext = pipename;
197             *sock = retsock;
198             sfree(name);
199             ReleaseMutex(mutex);
200             CloseHandle(mutex);
201             return SHARE_DOWNSTREAM;
202         }
203         sfree(*ds_err);
204         *ds_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock));
205         sk_close(retsock);
206     }
207
208     if (can_upstream) {
209         retsock = new_named_pipe_listener(pipename, upplug);
210         if (sk_socket_error(retsock) == NULL) {
211             sfree(*logtext);
212             *logtext = pipename;
213             *sock = retsock;
214             sfree(name);
215             ReleaseMutex(mutex);
216             CloseHandle(mutex);
217             return SHARE_UPSTREAM;
218         }
219         sfree(*us_err);
220         *us_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock));
221         sk_close(retsock);
222     }
223
224     /* One of the above clauses ought to have happened. */
225     assert(*logtext || *ds_err || *us_err);
226
227     sfree(pipename);
228     sfree(name);
229     ReleaseMutex(mutex);
230     CloseHandle(mutex);
231     return SHARE_NONE;
232 }
233
234 void platform_ssh_share_cleanup(const char *name)
235 {
236 }
237
238 #else /* !defined NO_SECURITY */
239
240 #include "noshare.c"
241
242 #endif /* !defined NO_SECURITY */