]> asedeno.scripts.mit.edu Git - PuTTY_svn.git/blob - windows/winshare.c
Switch to using SIDs in make_private_security_descriptor().
[PuTTY_svn.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
119     /*
120      * Transform the platform-independent version of the connection
121      * identifier into the obfuscated version we'll use for our
122      * Windows named pipe and mutex. A side effect of doing this is
123      * that it also eliminates any characters illegal in Windows pipe
124      * names.
125      */
126     name = obfuscate_name(pi_name);
127     if (!name) {
128         *logtext = dupprintf("Unable to call CryptProtectMemory: %s",
129                              win_strerror(GetLastError()));
130         return SHARE_NONE;
131     }
132
133     /*
134      * Make a mutex name out of the connection identifier, and lock it
135      * while we decide whether to be upstream or downstream.
136      */
137     {
138         SECURITY_ATTRIBUTES sa;
139
140         mutexname = make_name(CONNSHARE_MUTEX_PREFIX, name);
141         if (!make_private_security_descriptor(MUTEX_ALL_ACCESS,
142                                               &psd, &acl, logtext)) {
143             sfree(mutexname);
144             return SHARE_NONE;
145         }
146
147         memset(&sa, 0, sizeof(sa));
148         sa.nLength = sizeof(sa);
149         sa.lpSecurityDescriptor = psd;
150         sa.bInheritHandle = FALSE;
151
152         mutex = CreateMutex(&sa, FALSE, mutexname);
153
154         if (!mutex) {
155             *logtext = dupprintf("CreateMutex(\"%s\") failed: %s",
156                                  mutexname, win_strerror(GetLastError()));
157             sfree(mutexname);
158             LocalFree(psd);
159             LocalFree(acl);
160             return SHARE_NONE;
161         }
162
163         sfree(mutexname);
164         LocalFree(psd);
165         LocalFree(acl);
166
167         WaitForSingleObject(mutex, INFINITE);
168     }
169
170     pipename = make_name(CONNSHARE_PIPE_PREFIX, name);
171
172     *logtext = NULL;
173
174     if (can_downstream) {
175         retsock = new_named_pipe_client(pipename, downplug);
176         if (sk_socket_error(retsock) == NULL) {
177             sfree(*logtext);
178             *logtext = pipename;
179             *sock = retsock;
180             sfree(name);
181             ReleaseMutex(mutex);
182             CloseHandle(mutex);
183             return SHARE_DOWNSTREAM;
184         }
185         sfree(*ds_err);
186         *ds_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock));
187         sk_close(retsock);
188     }
189
190     if (can_upstream) {
191         retsock = new_named_pipe_listener(pipename, upplug);
192         if (sk_socket_error(retsock) == NULL) {
193             sfree(*logtext);
194             *logtext = pipename;
195             *sock = retsock;
196             sfree(name);
197             ReleaseMutex(mutex);
198             CloseHandle(mutex);
199             return SHARE_UPSTREAM;
200         }
201         sfree(*us_err);
202         *us_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock));
203         sk_close(retsock);
204     }
205
206     /* One of the above clauses ought to have happened. */
207     assert(*logtext || *ds_err || *us_err);
208
209     sfree(pipename);
210     sfree(name);
211     ReleaseMutex(mutex);
212     CloseHandle(mutex);
213     return SHARE_NONE;
214 }
215
216 void platform_ssh_share_cleanup(const char *name)
217 {
218 }
219
220 #else /* !defined NO_SECURITY */
221
222 #include "noshare.c"
223
224 #endif /* !defined NO_SECURITY */