source: filezilla/trunk/fuentes/src/putty/windows/winshare.c @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 4 years ago

First release to xenial

File size: 7.1 KB
Line 
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
38static 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
110static 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
121Socket new_named_pipe_client(const char *pipename, Plug plug);
122Socket new_named_pipe_listener(const char *pipename, Plug plug);
123
124int 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
234void 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 */
Note: See TracBrowser for help on using the repository browser.