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