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