uxagentc.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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. int 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_connections;
  23. struct agent_connection {
  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. struct agent_connection *a = (struct agent_connection *) av;
  34. struct agent_connection *b = (struct agent_connection *) 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. struct agent_connection *b = (struct agent_connection *) 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 0 if the expected
  53. * response is as yet incomplete; returns 1 if it's either complete
  54. * (conn->retbuf non-NULL and filled with something useful) or has
  55. * failed totally (conn->retbuf is NULL).
  56. */
  57. static int agent_try_read(struct agent_connection *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 1;
  67. }
  68. conn->retlen += ret;
  69. if (conn->retsize == 4 && conn->retlen == 4) {
  70. conn->retsize = toint(GET_32BIT(conn->retbuf) + 4);
  71. if (conn->retsize <= 0) {
  72. conn->retbuf = NULL;
  73. conn->retlen = 0;
  74. return -1; /* 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 0; /* more data to come */
  82. return 1;
  83. }
  84. static int agent_select_result(int fd, int event)
  85. {
  86. struct agent_connection *conn;
  87. assert(event == 1); /* not selecting for anything but R */
  88. conn = find234(agent_connections, &fd, agent_connfind);
  89. if (!conn) {
  90. uxsel_del(fd);
  91. return 1;
  92. }
  93. if (!agent_try_read(conn))
  94. return 0; /* more data to come */
  95. /*
  96. * We have now completed the agent query. Do the callback, and
  97. * clean up. (Of course we don't free retbuf, since ownership
  98. * of that passes to the callback.)
  99. */
  100. conn->callback(conn->callback_ctx, conn->retbuf, conn->retlen);
  101. uxsel_del(fd);
  102. close(fd);
  103. del234(agent_connections, conn);
  104. sfree(conn);
  105. return 0;
  106. }
  107. int agent_query(void *in, int inlen, void **out, int *outlen,
  108. void (*callback)(void *, void *, int), void *callback_ctx)
  109. {
  110. char *name;
  111. int sock;
  112. struct sockaddr_un addr;
  113. int done;
  114. struct agent_connection *conn;
  115. name = getenv("SSH_AUTH_SOCK");
  116. if (!name)
  117. goto failure;
  118. sock = socket(PF_UNIX, SOCK_STREAM, 0);
  119. if (sock < 0) {
  120. perror("socket(PF_UNIX)");
  121. exit(1);
  122. }
  123. cloexec(sock);
  124. addr.sun_family = AF_UNIX;
  125. strncpy(addr.sun_path, name, sizeof(addr.sun_path));
  126. if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
  127. close(sock);
  128. goto failure;
  129. }
  130. for (done = 0; done < inlen ;) {
  131. int ret = write(sock, (char *)in + done, inlen - done);
  132. if (ret <= 0) {
  133. close(sock);
  134. goto failure;
  135. }
  136. done += ret;
  137. }
  138. conn = snew(struct agent_connection);
  139. conn->fd = sock;
  140. conn->retbuf = conn->sizebuf;
  141. conn->retsize = 4;
  142. conn->retlen = 0;
  143. conn->callback = callback;
  144. conn->callback_ctx = callback_ctx;
  145. if (!callback) {
  146. /*
  147. * Bodge to permit making deliberately synchronous agent
  148. * requests. Used by Unix Pageant in command-line client mode,
  149. * which is legit because it really is true that no other part
  150. * of the program is trying to get anything useful done
  151. * simultaneously. But this special case shouldn't be used in
  152. * any more general program.
  153. */
  154. no_nonblock(conn->fd);
  155. while (!agent_try_read(conn))
  156. /* empty loop body */;
  157. *out = conn->retbuf;
  158. *outlen = conn->retlen;
  159. sfree(conn);
  160. return 1;
  161. }
  162. /*
  163. * Otherwise do it properly: add conn to the tree of agent
  164. * connections currently in flight, return 0 to indicate that the
  165. * response hasn't been received yet, and call the callback when
  166. * select_result comes back to us.
  167. */
  168. if (!agent_connections)
  169. agent_connections = newtree234(agent_conncmp);
  170. add234(agent_connections, conn);
  171. uxsel_set(sock, 1, agent_select_result);
  172. return 0;
  173. failure:
  174. *out = NULL;
  175. *outlen = 0;
  176. return 1;
  177. }