]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/uxshare.c
Merge tag '0.66'
[PuTTY.git] / unix / uxshare.c
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  */
30 SockAddr unix_sock_addr(const char *path);
31 Socket new_unix_listener(SockAddr listenaddr, Plug plug);
32
33 static 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
45 static 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
258 int 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
354 void 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 }