uxshare.c 12 KB

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