sharing.c 11 KB

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