source: filezilla/trunk/fuentes/.pc/02_PIPE_BUF.patch/src/putty/unix/uxshare.c @ 130

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

First release to xenial

File size: 11.9 KB
Line 
1/*
2 * Unix implementation of SSH connection-sharing IPC setup.
3 */
4
5#include <stdio.h>
6#include <assert.h>
7#include <errno.h>
8#include <limits.h>
9
10#include <unistd.h>
11#include <fcntl.h>
12#include <sys/stat.h>
13#include <sys/types.h>
14#include <sys/file.h>
15
16#define DEFINE_PLUG_METHOD_MACROS
17#include "tree234.h"
18#include "putty.h"
19#include "network.h"
20#include "proxy.h"
21#include "ssh.h"
22
23#define CONNSHARE_SOCKETDIR_PREFIX "/tmp/putty-connshare"
24#define SALT_FILENAME "salt"
25#define SALT_SIZE 64
26
27/*
28 * Functions provided by uxnet.c to help connection sharing.
29 */
30SockAddr unix_sock_addr(const char *path);
31Socket new_unix_listener(SockAddr listenaddr, Plug plug);
32
33static char *make_parentdir_name(void)
34{
35    char *username, *parent;
36
37    username = get_username();
38    parent = dupprintf("%s.%s", CONNSHARE_SOCKETDIR_PREFIX, username);
39    sfree(username);
40    assert(*parent == '/');
41
42    return parent;
43}
44
45static char *make_dirname(const char *pi_name, char **logtext)
46{
47    char *name, *parentdirname, *dirname, *err;
48
49    /*
50     * First, create the top-level directory for all shared PuTTY
51     * connections owned by this user.
52     */
53    parentdirname = make_parentdir_name();
54    if ((err = make_dir_and_check_ours(parentdirname)) != NULL) {
55        *logtext = err;
56        sfree(parentdirname);
57        return NULL;
58    }
59
60    /*
61     * Transform the platform-independent version of the connection
62     * identifier into the name we'll actually use for the directory
63     * containing the Unix socket.
64     *
65     * We do this by hashing the identifier with some user-specific
66     * secret information, to avoid the privacy leak of having
67     * "user@host" strings show up in 'netstat -x'. (Irritatingly, the
68     * full pathname of a Unix-domain socket _does_ show up in the
69     * 'netstat -x' output, at least on Linux, even if that socket is
70     * in a directory not readable to the user running netstat. You'd
71     * think putting things inside an 0700 directory would hide their
72     * names from other users, but no.)
73     *
74     * The secret information we use to salt the hash lives in a file
75     * inside the top-level directory we just created, so we must
76     * first create that file (with some fresh random data in it) if
77     * it's not already been done by a previous PuTTY.
78     */
79    {
80        unsigned char saltbuf[SALT_SIZE];
81        char *saltname;
82        int saltfd, i, ret;
83
84        saltname = dupprintf("%s/%s", parentdirname, SALT_FILENAME);
85        saltfd = open(saltname, O_RDONLY);
86        if (saltfd < 0) {
87            char *tmpname;
88            int pid;
89
90            if (errno != ENOENT) {
91                *logtext = dupprintf("%s: open: %s", saltname,
92                                     strerror(errno));
93                sfree(saltname);
94                sfree(parentdirname);
95                return NULL;
96            }
97
98            /*
99             * The salt file doesn't already exist, so try to create
100             * it. Another process may be attempting the same thing
101             * simultaneously, so we must do this carefully: we write
102             * a salt file under a different name, then hard-link it
103             * into place, which guarantees that we won't change the
104             * contents of an existing salt file.
105             */
106            pid = getpid();
107            for (i = 0;; i++) {
108                tmpname = dupprintf("%s/%s.tmp.%d.%d",
109                                    parentdirname, SALT_FILENAME, pid, i);
110                saltfd = open(tmpname, O_WRONLY | O_EXCL | O_CREAT, 0400);
111                if (saltfd >= 0)
112                    break;
113                if (errno != EEXIST) {
114                    *logtext = dupprintf("%s: open: %s", tmpname,
115                                         strerror(errno));
116                    sfree(tmpname);
117                    sfree(saltname);
118                    sfree(parentdirname);
119                    return NULL;
120                }
121                sfree(tmpname);        /* go round and try again with i+1 */
122            }
123            /*
124             * Invent some random data.
125             */
126            for (i = 0; i < SALT_SIZE; i++) {
127                saltbuf[i] = random_byte();
128            }
129            ret = write(saltfd, saltbuf, SALT_SIZE);
130            /* POSIX atomicity guarantee: because we wrote less than
131             * PIPE_BUF bytes, the write either completed in full or
132             * failed. */
133            assert(SALT_SIZE < PIPE_BUF);
134            assert(ret < 0 || ret == SALT_SIZE);
135            if (ret < 0) {
136                close(saltfd);
137                *logtext = dupprintf("%s: write: %s", tmpname,
138                                     strerror(errno));
139                sfree(tmpname);
140                sfree(saltname);
141                sfree(parentdirname);
142                return NULL;
143            }
144            if (close(saltfd) < 0) {
145                *logtext = dupprintf("%s: close: %s", tmpname,
146                                     strerror(errno));
147                sfree(tmpname);
148                sfree(saltname);
149                sfree(parentdirname);
150                return NULL;
151            }
152
153            /*
154             * Now attempt to hard-link our temp file into place. We
155             * tolerate EEXIST as an outcome, because that just means
156             * another PuTTY got their attempt in before we did (and
157             * we only care that there is a valid salt file we can
158             * agree on, no matter who created it).
159             */
160            if (link(tmpname, saltname) < 0 && errno != EEXIST) {
161                *logtext = dupprintf("%s: link: %s", saltname,
162                                     strerror(errno));
163                sfree(tmpname);
164                sfree(saltname);
165                sfree(parentdirname);
166                return NULL;
167            }
168
169            /*
170             * Whether that succeeded or not, get rid of our temp file.
171             */
172            if (unlink(tmpname) < 0) {
173                *logtext = dupprintf("%s: unlink: %s", tmpname,
174                                     strerror(errno));
175                sfree(tmpname);
176                sfree(saltname);
177                sfree(parentdirname);
178                return NULL;
179            }
180
181            /*
182             * And now we've arranged for there to be a salt file, so
183             * we can try to open it for reading again and this time
184             * expect it to work.
185             */
186            sfree(tmpname);
187
188            saltfd = open(saltname, O_RDONLY);
189            if (saltfd < 0) {
190                *logtext = dupprintf("%s: open: %s", saltname,
191                                     strerror(errno));
192                sfree(saltname);
193                sfree(parentdirname);
194                return NULL;
195            }
196        }
197
198        for (i = 0; i < SALT_SIZE; i++) {
199            ret = read(saltfd, saltbuf, SALT_SIZE);
200            if (ret <= 0) {
201                close(saltfd);
202                *logtext = dupprintf("%s: read: %s", saltname,
203                                     ret == 0 ? "unexpected EOF" :
204                                     strerror(errno));
205                sfree(saltname);
206                sfree(parentdirname);
207                return NULL;
208            }
209            assert(0 < ret && ret <= SALT_SIZE - i);
210            i += ret;
211        }
212
213        close(saltfd);
214        sfree(saltname);
215
216        /*
217         * Now we've got our salt, hash it with the connection
218         * identifier to produce our actual socket name.
219         */
220        {
221            SHA256_State sha;
222            unsigned len;
223            unsigned char lenbuf[4];
224            unsigned char digest[32];
225            char retbuf[65];
226
227            SHA256_Init(&sha);
228            PUT_32BIT(lenbuf, SALT_SIZE);
229            SHA256_Bytes(&sha, lenbuf, 4);
230            SHA256_Bytes(&sha, saltbuf, SALT_SIZE);
231            len = strlen(pi_name);
232            PUT_32BIT(lenbuf, len);
233            SHA256_Bytes(&sha, lenbuf, 4);
234            SHA256_Bytes(&sha, pi_name, len);
235            SHA256_Final(&sha, digest);
236
237            /*
238             * And make it printable.
239             */
240            for (i = 0; i < 32; i++) {
241                sprintf(retbuf + 2*i, "%02x", digest[i]);
242                /* the last of those will also write the trailing NUL */
243            }
244
245            name = dupstr(retbuf);
246        }
247
248        smemclr(saltbuf, sizeof(saltbuf));
249    }
250
251    dirname = dupprintf("%s/%s", parentdirname, name);
252    sfree(parentdirname);
253    sfree(name);
254
255    return dirname;
256}
257
258int platform_ssh_share(const char *pi_name, Conf *conf,
259                       Plug downplug, Plug upplug, Socket *sock,
260                       char **logtext, char **ds_err, char **us_err,
261                       int can_upstream, int can_downstream)
262{
263    char *dirname, *lockname, *sockname, *err;
264    int lockfd;
265    Socket retsock;
266
267    /*
268     * Sort out what we're going to call the directory in which we
269     * keep the socket. This has the side effect of potentially
270     * creating its top-level containing dir and/or the salt file
271     * within that, if they don't already exist.
272     */
273    dirname = make_dirname(pi_name, logtext);
274    if (!dirname) {
275        return SHARE_NONE;
276    }
277
278    /*
279     * Now make sure the subdirectory exists.
280     */
281    if ((err = make_dir_and_check_ours(dirname)) != NULL) {
282        *logtext = err;
283        sfree(dirname);
284        return SHARE_NONE;
285    }
286
287    /*
288     * Acquire a lock on a file in that directory.
289     */
290    lockname = dupcat(dirname, "/lock", (char *)NULL);
291    lockfd = open(lockname, O_CREAT | O_RDWR | O_TRUNC, 0600);
292    if (lockfd < 0) {
293        *logtext = dupprintf("%s: open: %s", lockname, strerror(errno));
294        sfree(dirname);
295        sfree(lockname);
296        return SHARE_NONE;
297    }
298    if (flock(lockfd, LOCK_EX) < 0) {
299        *logtext = dupprintf("%s: flock(LOCK_EX): %s",
300                             lockname, strerror(errno));
301        sfree(dirname);
302        sfree(lockname);
303        close(lockfd);
304        return SHARE_NONE;
305    }
306
307    sockname = dupprintf("%s/socket", dirname);
308
309    *logtext = NULL;
310
311    if (can_downstream) {
312        retsock = new_connection(unix_sock_addr(sockname),
313                                 "", 0, 0, 1, 0, 0, downplug, conf);
314        if (sk_socket_error(retsock) == NULL) {
315            sfree(*logtext);
316            *logtext = sockname;
317            *sock = retsock;
318            sfree(dirname);
319            sfree(lockname);
320            close(lockfd);
321            return SHARE_DOWNSTREAM;
322        }
323        sfree(*ds_err);
324        *ds_err = dupprintf("%s: %s", sockname, sk_socket_error(retsock));
325        sk_close(retsock);
326    }
327
328    if (can_upstream) {
329        retsock = new_unix_listener(unix_sock_addr(sockname), upplug);
330        if (sk_socket_error(retsock) == NULL) {
331            sfree(*logtext);
332            *logtext = sockname;
333            *sock = retsock;
334            sfree(dirname);
335            sfree(lockname);
336            close(lockfd);
337            return SHARE_UPSTREAM;
338        }
339        sfree(*us_err);
340        *us_err = dupprintf("%s: %s", sockname, sk_socket_error(retsock));
341        sk_close(retsock);
342    }
343
344    /* One of the above clauses ought to have happened. */
345    assert(*logtext || *ds_err || *us_err);
346
347    sfree(dirname);
348    sfree(lockname);
349    sfree(sockname);
350    close(lockfd);
351    return SHARE_NONE;
352}
353
354void platform_ssh_share_cleanup(const char *name)
355{
356    char *dirname, *filename, *logtext;
357
358    dirname = make_dirname(name, &logtext);
359    if (!dirname) {
360        sfree(logtext);                /* we can't do much with this */
361        return;
362    }
363
364    filename = dupcat(dirname, "/socket", (char *)NULL);
365    remove(filename);
366    sfree(filename);
367
368    filename = dupcat(dirname, "/lock", (char *)NULL);
369    remove(filename);
370    sfree(filename);
371
372    rmdir(dirname);
373
374    /*
375     * We deliberately _don't_ clean up the parent directory
376     * /tmp/putty-connshare.<username>, because if we leave it around
377     * then it reduces the ability for other users to be a nuisance by
378     * putting their own directory in the way of it. Also, the salt
379     * file in it can be reused.
380     */
381
382    sfree(dirname);
383}
Note: See TracBrowser for help on using the repository browser.