2 * Windows implementation of SSH connection-sharing IPC setup.
8 #define DEFINE_PLUG_METHOD_MACROS
15 #if !defined NO_SECURITY
19 #define CONNSHARE_PIPE_PREFIX "\\\\.\\pipe\\putty-connshare"
20 #define CONNSHARE_MUTEX_PREFIX "Local\\putty-connshare-mutex"
22 static char *obfuscate_name(const char *realname)
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).
35 unsigned char lenbuf[4];
36 unsigned char digest[32];
40 cryptlen = strlen(realname) + 1;
41 cryptlen += CRYPTPROTECTMEMORY_BLOCK_SIZE - 1;
42 cryptlen /= CRYPTPROTECTMEMORY_BLOCK_SIZE;
43 cryptlen *= CRYPTPROTECTMEMORY_BLOCK_SIZE;
45 cryptdata = snewn(cryptlen, char);
46 memset(cryptdata, 0, cryptlen);
47 strcpy(cryptdata, realname);
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.
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
60 if (!p_CryptProtectMemory(cryptdata, cryptlen,
61 CRYPTPROTECTMEMORY_CROSS_PROCESS)) {
66 * We don't want to give away the length of the hostname either,
67 * so having got it back out of CryptProtectMemory we now hash it.
70 PUT_32BIT_MSB_FIRST(lenbuf, cryptlen);
71 SHA256_Bytes(&sha, lenbuf, 4);
72 SHA256_Bytes(&sha, cryptdata, cryptlen);
73 SHA256_Final(&sha, digest);
78 * Finally, make printable.
80 for (i = 0; i < 32; i++) {
81 sprintf(retbuf + 2*i, "%02x", digest[i]);
82 /* the last of those will also write the trailing NUL */
85 return dupstr(retbuf);
88 static char *make_name(const char *prefix, const char *name)
90 char *username, *retname;
92 username = get_username();
93 retname = dupprintf("%s.%s.%s", prefix, username, name);
99 Socket new_named_pipe_client(const char *pipename, Plug plug);
100 Socket new_named_pipe_listener(const char *pipename, Plug plug);
102 int platform_ssh_share(const char *pi_name, Conf *conf,
103 Plug downplug, Plug upplug, Socket *sock,
104 char **logtext, char **ds_err, char **us_err,
105 int can_upstream, int can_downstream)
107 char *name, *mutexname, *pipename;
110 PSECURITY_DESCRIPTOR psd;
115 *logtext = dupprintf("Unable to load crypt32.dll");
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
126 name = obfuscate_name(pi_name);
128 *logtext = dupprintf("Unable to call CryptProtectMemory: %s",
129 win_strerror(GetLastError()));
134 * Make a mutex name out of the connection identifier, and lock it
135 * while we decide whether to be upstream or downstream.
138 SECURITY_ATTRIBUTES sa;
140 mutexname = make_name(CONNSHARE_MUTEX_PREFIX, name);
141 if (!make_private_security_descriptor(MUTEX_ALL_ACCESS,
148 memset(&sa, 0, sizeof(sa));
149 sa.nLength = sizeof(sa);
150 sa.lpSecurityDescriptor = psd;
151 sa.bInheritHandle = FALSE;
153 mutex = CreateMutex(&sa, FALSE, mutexname);
156 *logtext = dupprintf("CreateMutex(\"%s\") failed: %s",
157 mutexname, win_strerror(GetLastError()));
160 LocalFree(networksid);
167 LocalFree(networksid);
170 WaitForSingleObject(mutex, INFINITE);
173 pipename = make_name(CONNSHARE_PIPE_PREFIX, name);
177 if (can_downstream) {
178 retsock = new_named_pipe_client(pipename, downplug);
179 if (sk_socket_error(retsock) == NULL) {
186 return SHARE_DOWNSTREAM;
189 *ds_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock));
194 retsock = new_named_pipe_listener(pipename, upplug);
195 if (sk_socket_error(retsock) == NULL) {
202 return SHARE_UPSTREAM;
205 *us_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock));
209 /* One of the above clauses ought to have happened. */
210 assert(*logtext || *ds_err || *us_err);
219 void platform_ssh_share_cleanup(const char *name)
223 #else /* !defined NO_SECURITY */
227 #endif /* !defined NO_SECURITY */