agent-client.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. * SSH agent client code.
  3. */
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <assert.h>
  7. #include <unistd.h>
  8. #include <sys/socket.h>
  9. #include <sys/un.h>
  10. #include <fcntl.h>
  11. #include "putty.h"
  12. #include "misc.h"
  13. #include "tree234.h"
  14. #include "puttymem.h"
  15. bool agent_exists(void)
  16. {
  17. const char *p = getenv("SSH_AUTH_SOCK");
  18. if (p && *p)
  19. return true;
  20. return false;
  21. }
  22. static tree234 *agent_pending_queries;
  23. struct agent_pending_query {
  24. int fd;
  25. char *retbuf;
  26. char sizebuf[4];
  27. int retsize, retlen;
  28. void (*callback)(void *, void *, int);
  29. void *callback_ctx;
  30. };
  31. static int agent_conncmp(void *av, void *bv)
  32. {
  33. agent_pending_query *a = (agent_pending_query *) av;
  34. agent_pending_query *b = (agent_pending_query *) bv;
  35. if (a->fd < b->fd)
  36. return -1;
  37. if (a->fd > b->fd)
  38. return +1;
  39. return 0;
  40. }
  41. static int agent_connfind(void *av, void *bv)
  42. {
  43. int afd = *(int *) av;
  44. agent_pending_query *b = (agent_pending_query *) bv;
  45. if (afd < b->fd)
  46. return -1;
  47. if (afd > b->fd)
  48. return +1;
  49. return 0;
  50. }
  51. /*
  52. * Attempt to read from an agent socket fd. Returns false if the
  53. * expected response is as yet incomplete; returns true if it's either
  54. * complete (conn->retbuf non-NULL and filled with something useful)
  55. * or has failed totally (conn->retbuf is NULL).
  56. */
  57. static bool agent_try_read(agent_pending_query *conn)
  58. {
  59. int ret;
  60. ret = read(conn->fd, conn->retbuf+conn->retlen,
  61. conn->retsize-conn->retlen);
  62. if (ret <= 0) {
  63. if (conn->retbuf != conn->sizebuf) sfree(conn->retbuf);
  64. conn->retbuf = NULL;
  65. conn->retlen = 0;
  66. return true;
  67. }
  68. conn->retlen += ret;
  69. if (conn->retsize == 4 && conn->retlen == 4) {
  70. conn->retsize = toint(GET_32BIT_MSB_FIRST(conn->retbuf) + 4);
  71. if (conn->retsize <= 0) {
  72. conn->retbuf = NULL;
  73. conn->retlen = 0;
  74. return true; /* way too large */
  75. }
  76. assert(conn->retbuf == conn->sizebuf);
  77. conn->retbuf = snewn(conn->retsize, char);
  78. memcpy(conn->retbuf, conn->sizebuf, 4);
  79. }
  80. if (conn->retlen < conn->retsize)
  81. return false; /* more data to come */
  82. return true;
  83. }
  84. void agent_cancel_query(agent_pending_query *conn)
  85. {
  86. uxsel_del(conn->fd);
  87. close(conn->fd);
  88. del234(agent_pending_queries, conn);
  89. if (conn->retbuf && conn->retbuf != conn->sizebuf)
  90. sfree(conn->retbuf);
  91. sfree(conn);
  92. }
  93. static void agent_select_result(int fd, int event)
  94. {
  95. agent_pending_query *conn;
  96. assert(event == SELECT_R); /* not selecting for anything but R */
  97. conn = find234(agent_pending_queries, &fd, agent_connfind);
  98. if (!conn) {
  99. uxsel_del(fd);
  100. return;
  101. }
  102. if (!agent_try_read(conn))
  103. return; /* more data to come */
  104. /*
  105. * We have now completed the agent query. Do the callback.
  106. */
  107. conn->callback(conn->callback_ctx, conn->retbuf, conn->retlen);
  108. /* Null out conn->retbuf, since ownership of that buffer has
  109. * passed to the callback. */
  110. conn->retbuf = NULL;
  111. agent_cancel_query(conn);
  112. }
  113. static const char *agent_socket_path(void)
  114. {
  115. return getenv("SSH_AUTH_SOCK");
  116. }
  117. Socket *agent_connect(Plug *plug)
  118. {
  119. const char *path = agent_socket_path();
  120. if (!path)
  121. return new_error_socket_fmt(plug, "SSH_AUTH_SOCK not set");
  122. return sk_new(unix_sock_addr(path), 0, false, false, false, false, plug);
  123. }
  124. agent_pending_query *agent_query(
  125. strbuf *query, void **out, int *outlen,
  126. void (*callback)(void *, void *, int), void *callback_ctx)
  127. {
  128. const char *name;
  129. int sock;
  130. struct sockaddr_un addr;
  131. int done;
  132. agent_pending_query *conn;
  133. name = agent_socket_path();
  134. if (!name || strlen(name) >= sizeof(addr.sun_path))
  135. goto failure;
  136. sock = socket(PF_UNIX, SOCK_STREAM, 0);
  137. if (sock < 0) {
  138. perror("socket(PF_UNIX)");
  139. exit(1);
  140. }
  141. cloexec(sock);
  142. addr.sun_family = AF_UNIX;
  143. strcpy(addr.sun_path, name);
  144. if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
  145. close(sock);
  146. goto failure;
  147. }
  148. strbuf_finalise_agent_query(query);
  149. for (done = 0; done < query->len ;) {
  150. int ret = write(sock, query->s + done,
  151. query->len - done);
  152. if (ret <= 0) {
  153. close(sock);
  154. goto failure;
  155. }
  156. done += ret;
  157. }
  158. conn = snew(agent_pending_query);
  159. conn->fd = sock;
  160. conn->retbuf = conn->sizebuf;
  161. conn->retsize = 4;
  162. conn->retlen = 0;
  163. conn->callback = callback;
  164. conn->callback_ctx = callback_ctx;
  165. if (!callback) {
  166. /*
  167. * Bodge to permit making deliberately synchronous agent
  168. * requests. Used by Unix Pageant in command-line client mode,
  169. * which is legit because it really is true that no other part
  170. * of the program is trying to get anything useful done
  171. * simultaneously. But this special case shouldn't be used in
  172. * any more general program.
  173. */
  174. no_nonblock(conn->fd);
  175. while (!agent_try_read(conn))
  176. /* empty loop body */;
  177. *out = conn->retbuf;
  178. *outlen = conn->retlen;
  179. sfree(conn);
  180. return NULL;
  181. }
  182. /*
  183. * Otherwise do it properly: add conn to the tree of agent
  184. * connections currently in flight, return 0 to indicate that the
  185. * response hasn't been received yet, and call the callback when
  186. * select_result comes back to us.
  187. */
  188. if (!agent_pending_queries)
  189. agent_pending_queries = newtree234(agent_conncmp);
  190. add234(agent_pending_queries, conn);
  191. uxsel_set(sock, SELECT_R, agent_select_result);
  192. return conn;
  193. failure:
  194. *out = NULL;
  195. *outlen = 0;
  196. return NULL;
  197. }