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

Last change on this file since 3185 was 3185, checked in by jrpelegrina, 3 years ago

Update new version: 3.15.02

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 "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.cyg, 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
39static 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
111static 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
122Socket new_named_pipe_client(const char *pipename, Plug plug);
123Socket new_named_pipe_listener(const char *pipename, Plug plug);
124
125int 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
235void 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 */
Note: See TracBrowser for help on using the repository browser.