cproxy.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. * Routines to do cryptographic interaction with proxies in PuTTY.
  3. * This is in a separate module from proxy.c, so that it can be
  4. * conveniently removed in PuTTYtel by replacing this module with
  5. * the stub version nocproxy.c.
  6. */
  7. #include <assert.h>
  8. #include <ctype.h>
  9. #include <string.h>
  10. #include "putty.h"
  11. #include "ssh.h" /* For MD5 support */
  12. #include "network.h"
  13. #include "proxy.h"
  14. #include "marshal.h"
  15. const bool socks5_chap_available = true;
  16. const bool http_digest_available = true;
  17. strbuf *chap_response(ptrlen challenge, ptrlen password)
  18. {
  19. strbuf *sb = strbuf_new_nm();
  20. const ssh2_macalg *alg = &ssh_hmac_md5;
  21. enable_dit(); /* just in case main() forgot */
  22. mac_simple(alg, password, challenge, strbuf_append(sb, alg->len));
  23. return sb;
  24. }
  25. static void BinarySink_put_hex_data(BinarySink *bs, const void *vptr,
  26. size_t len)
  27. {
  28. const unsigned char *p = (const unsigned char *)vptr;
  29. const char *hexdigits = "0123456789abcdef";
  30. while (len-- > 0) {
  31. unsigned c = *p++;
  32. put_byte(bs, hexdigits[0xF & (c >> 4)]);
  33. put_byte(bs, hexdigits[0xF & (c )]);
  34. }
  35. }
  36. #define put_hex_data(bs, p, len) \
  37. BinarySink_put_hex_data(BinarySink_UPCAST(bs), p, len)
  38. const char *const httphashnames[] = {
  39. #define DECL_ARRAY(id, str, alg, bits, accepted) str,
  40. HTTP_DIGEST_HASHES(DECL_ARRAY)
  41. #undef DECL_ARRAY
  42. };
  43. const bool httphashaccepted[] = {
  44. #define DECL_ARRAY(id, str, alg, bits, accepted) accepted,
  45. HTTP_DIGEST_HASHES(DECL_ARRAY)
  46. #undef DECL_ARRAY
  47. };
  48. static const ssh_hashalg *const httphashalgs[] = {
  49. #define DECL_ARRAY(id, str, alg, bits, accepted) alg,
  50. HTTP_DIGEST_HASHES(DECL_ARRAY)
  51. #undef DECL_ARRAY
  52. };
  53. static const size_t httphashlengths[] = {
  54. #define DECL_ARRAY(id, str, alg, bits, accepted) bits/8,
  55. HTTP_DIGEST_HASHES(DECL_ARRAY)
  56. #undef DECL_ARRAY
  57. };
  58. void http_digest_response(BinarySink *bs, ptrlen username, ptrlen password,
  59. ptrlen realm, ptrlen method, ptrlen uri, ptrlen qop,
  60. ptrlen nonce, ptrlen opaque, uint32_t nonce_count,
  61. HttpDigestHash hash, bool hash_username)
  62. {
  63. unsigned char a1hash[MAX_HASH_LEN];
  64. unsigned char a2hash[MAX_HASH_LEN];
  65. unsigned char rsphash[MAX_HASH_LEN];
  66. const ssh_hashalg *alg = httphashalgs[hash];
  67. size_t hashlen = httphashlengths[hash];
  68. enable_dit(); /* just in case main() forgot */
  69. { // WINSCP
  70. unsigned char ncbuf[4];
  71. PUT_32BIT_MSB_FIRST(ncbuf, nonce_count);
  72. { // WINSCP
  73. unsigned char client_nonce_raw[33];
  74. random_read(client_nonce_raw, lenof(client_nonce_raw));
  75. { // WINSCP
  76. char client_nonce_base64[lenof(client_nonce_raw) / 3 * 4];
  77. { // WINSCP
  78. unsigned i;
  79. for (i = 0; i < lenof(client_nonce_raw)/3; i++)
  80. base64_encode_atom(client_nonce_raw + 3*i, 3,
  81. client_nonce_base64 + 4*i);
  82. /*
  83. * RFC 7616 section 3.4.2: the hash "A1" is a hash of
  84. * username:realm:password (in the absence of hash names like
  85. * "MD5-sess" which as far as I know don't sensibly apply to
  86. * proxies and HTTP CONNECT).
  87. */
  88. { // WINSCP
  89. ssh_hash *h = ssh_hash_new(alg);
  90. put_datapl(h, username);
  91. put_byte(h, ':');
  92. put_datapl(h, realm);
  93. put_byte(h, ':');
  94. put_datapl(h, password);
  95. ssh_hash_digest_nondestructive(h, a1hash);
  96. /*
  97. * RFC 7616 section 3.4.3: the hash "A2" is a hash of method:uri
  98. * (in the absence of more interesting quality-of-protection
  99. * schemes than plain "auth" - e.g. "auth-int" hashes the entire
  100. * document as well - which again I don't think make sense in the
  101. * context of proxies and CONNECT).
  102. */
  103. ssh_hash_reset(h);
  104. put_datapl(h, method);
  105. put_byte(h, ':');
  106. put_datapl(h, uri);
  107. ssh_hash_digest_nondestructive(h, a2hash);
  108. /*
  109. * RFC 7616 section 3.4.1: the overall output hash in the
  110. * "response" parameter of the authorization header is a hash of
  111. * A1:nonce:nonce-count:client-nonce:qop:A2, where A1 and A2 are
  112. * the hashes computed above.
  113. */
  114. ssh_hash_reset(h);
  115. put_hex_data(h, a1hash, hashlen);
  116. put_byte(h, ':');
  117. put_datapl(h, nonce);
  118. put_byte(h, ':');
  119. put_hex_data(h, ncbuf, 4);
  120. put_byte(h, ':');
  121. put_data(h, client_nonce_base64, lenof(client_nonce_base64));
  122. put_byte(h, ':');
  123. put_datapl(h, qop);
  124. put_byte(h, ':');
  125. put_hex_data(h, a2hash, hashlen);
  126. ssh_hash_final(h, rsphash);
  127. /*
  128. * Now construct the output header (everything after the initial
  129. * "Proxy-Authorization: Digest ") and write it to the provided
  130. * BinarySink.
  131. */
  132. put_datalit(bs, "username=\"");
  133. if (hash_username) {
  134. /*
  135. * RFC 7616 section 3.4.4: if we're hashing the username, we
  136. * actually hash username:realm (like a truncated version of
  137. * A1 above).
  138. */
  139. ssh_hash *h = ssh_hash_new(alg);
  140. put_datapl(h, username);
  141. put_byte(h, ':');
  142. put_datapl(h, realm);
  143. ssh_hash_final(h, a1hash);
  144. put_hex_data(bs, a1hash, hashlen);
  145. } else {
  146. put_datapl(bs, username);
  147. }
  148. put_datalit(bs, "\", realm=\"");
  149. put_datapl(bs, realm);
  150. put_datalit(bs, "\", uri=\"");
  151. put_datapl(bs, uri);
  152. put_datalit(bs, "\", algorithm=");
  153. put_dataz(bs, httphashnames[hash]);
  154. put_datalit(bs, ", nonce=\"");
  155. put_datapl(bs, nonce);
  156. put_datalit(bs, "\", nc=");
  157. put_hex_data(bs, ncbuf, 4);
  158. put_datalit(bs, ", cnonce=\"");
  159. put_data(bs, client_nonce_base64, lenof(client_nonce_base64));
  160. put_datalit(bs, "\", qop=");
  161. put_datapl(bs, qop);
  162. put_datalit(bs, ", response=\"");
  163. put_hex_data(bs, rsphash, hashlen);
  164. put_datalit(bs, "\"");
  165. if (opaque.ptr) {
  166. put_datalit(bs, ", opaque=\"");
  167. put_datapl(bs, opaque);
  168. put_datalit(bs, "\"");
  169. }
  170. if (hash_username) {
  171. put_datalit(bs, ", userhash=true");
  172. }
  173. smemclr(a1hash, lenof(a1hash));
  174. smemclr(a2hash, lenof(a2hash));
  175. smemclr(rsphash, lenof(rsphash));
  176. smemclr(client_nonce_raw, lenof(client_nonce_raw));
  177. smemclr(client_nonce_base64, lenof(client_nonce_base64));
  178. } // WINSCP
  179. } // WINSCP
  180. } // WINSCP
  181. } // WINSCP
  182. } // WINSCP
  183. }