socks5.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. /*
  2. * SOCKS 5 proxy negotiation.
  3. */
  4. #include "putty.h"
  5. #include "network.h"
  6. #include "proxy.h"
  7. #include "socks.h"
  8. #include "sshcr.h"
  9. static inline const char *socks5_auth_name(unsigned char m)
  10. {
  11. switch (m) {
  12. case SOCKS5_AUTH_NONE: return "none";
  13. case SOCKS5_AUTH_GSSAPI: return "GSSAPI";
  14. case SOCKS5_AUTH_PASSWORD: return "password";
  15. case SOCKS5_AUTH_CHAP: return "CHAP";
  16. default: return "unknown";
  17. }
  18. }
  19. static inline const char *socks5_response_text(unsigned char m)
  20. {
  21. switch (m) {
  22. case SOCKS5_RESP_SUCCESS: return "success";
  23. case SOCKS5_RESP_FAILURE: return "unspecified failure";
  24. case SOCKS5_RESP_CONNECTION_NOT_ALLOWED_BY_RULESET:
  25. return "connection not allowed by ruleset";
  26. case SOCKS5_RESP_NETWORK_UNREACHABLE: return "network unreachable";
  27. case SOCKS5_RESP_HOST_UNREACHABLE: return "host unreachable";
  28. case SOCKS5_RESP_CONNECTION_REFUSED: return "connection refused";
  29. case SOCKS5_RESP_TTL_EXPIRED: return "TTL expired";
  30. case SOCKS5_RESP_COMMAND_NOT_SUPPORTED: return "command not supported";
  31. case SOCKS5_RESP_ADDRTYPE_NOT_SUPPORTED:
  32. return "address type not supported";
  33. default: return "unknown";
  34. }
  35. }
  36. typedef struct Socks5ProxyNegotiator {
  37. int crLine;
  38. strbuf *auth_methods_offered;
  39. unsigned char auth_method;
  40. unsigned n_chap_attrs;
  41. unsigned chap_attr, chap_attr_len;
  42. unsigned char chap_buf[256];
  43. strbuf *username, *password;
  44. prompts_t *prompts;
  45. int username_prompt_index, password_prompt_index;
  46. int response_addr_length;
  47. ProxyNegotiator pn;
  48. } Socks5ProxyNegotiator;
  49. static ProxyNegotiator *proxy_socks5_new(const ProxyNegotiatorVT *vt)
  50. {
  51. Socks5ProxyNegotiator *s = snew(Socks5ProxyNegotiator);
  52. memset(s, 0, sizeof(*s));
  53. s->pn.vt = vt;
  54. s->auth_methods_offered = strbuf_new();
  55. s->username = strbuf_new();
  56. s->password = strbuf_new_nm();
  57. return &s->pn;
  58. }
  59. static void proxy_socks5_free(ProxyNegotiator *pn)
  60. {
  61. Socks5ProxyNegotiator *s = container_of(pn, Socks5ProxyNegotiator, pn);
  62. strbuf_free(s->auth_methods_offered);
  63. strbuf_free(s->username);
  64. strbuf_free(s->password);
  65. if (s->prompts)
  66. free_prompts(s->prompts);
  67. smemclr(s, sizeof(*s));
  68. sfree(s);
  69. }
  70. static void proxy_socks5_process_queue(ProxyNegotiator *pn)
  71. {
  72. Socks5ProxyNegotiator *s = container_of(pn, Socks5ProxyNegotiator, pn);
  73. crBegin(s->crLine);
  74. /*
  75. * SOCKS 5 initial client packet:
  76. *
  77. * byte version
  78. * byte number of available auth methods
  79. * byte[] that many bytes indicating auth types
  80. */
  81. put_byte(pn->output, SOCKS5_REQUEST_VERSION);
  82. strbuf_clear(s->auth_methods_offered);
  83. /*
  84. * We have two basic kinds of authentication to offer: none at
  85. * all, and password-based systems (whether the password is sent
  86. * in cleartext or proved via CHAP).
  87. *
  88. * We always offer 'none' as an option. We offer 'password' if we
  89. * either have a username and password already from the Conf, or
  90. * we have a Seat available to ask for them interactively. If
  91. * neither, we don't offer those options in the first place.
  92. */
  93. put_byte(s->auth_methods_offered, SOCKS5_AUTH_NONE);
  94. put_dataz(s->username, conf_get_str(pn->ps->conf, CONF_proxy_username));
  95. put_dataz(s->password, conf_get_str(pn->ps->conf, CONF_proxy_password));
  96. if (pn->itr || (s->username->len && s->password->len)) {
  97. if (socks5_chap_available)
  98. put_byte(s->auth_methods_offered, SOCKS5_AUTH_CHAP);
  99. put_byte(s->auth_methods_offered, SOCKS5_AUTH_PASSWORD);
  100. }
  101. put_byte(pn->output, s->auth_methods_offered->len);
  102. put_datapl(pn->output, ptrlen_from_strbuf(s->auth_methods_offered));
  103. crReturnV;
  104. /*
  105. * SOCKS 5 initial server packet:
  106. *
  107. * byte version
  108. * byte selected auth method, or SOCKS5_AUTH_REJECTED
  109. */
  110. {
  111. unsigned char data[2];
  112. crMaybeWaitUntilV(bufchain_try_fetch_consume(pn->input, data, 2));
  113. if (data[0] != SOCKS5_REPLY_VERSION) {
  114. pn->error = dupprintf("SOCKS proxy returned unexpected "
  115. "reply version %d (expected %d)",
  116. (int)data[0], SOCKS5_REPLY_VERSION);
  117. crStopV;
  118. }
  119. if (data[1] == SOCKS5_AUTH_REJECTED) {
  120. pn->error = dupstr("SOCKS server rejected every authentication "
  121. "method we offered");
  122. crStopV;
  123. }
  124. {
  125. bool found = false;
  126. size_t i; // WINSCP
  127. for (i = 0; i < s->auth_methods_offered->len; i++)
  128. if (s->auth_methods_offered->u[i] == data[1]) {
  129. found = true;
  130. break;
  131. }
  132. if (!found) {
  133. pn->error = dupprintf("SOCKS server asked for auth method %d "
  134. "(%s), which we did not offer",
  135. (int)data[1], socks5_auth_name(data[1]));
  136. crStopV;
  137. }
  138. }
  139. s->auth_method = data[1];
  140. }
  141. /*
  142. * The 'none' auth option requires no further negotiation. If that
  143. * was the one we selected, go straight to making the connection.
  144. */
  145. if (s->auth_method == SOCKS5_AUTH_NONE)
  146. goto authenticated;
  147. /*
  148. * Otherwise, we're going to need a username and password, so this
  149. * is the moment to stop and ask for one if we don't already have
  150. * them.
  151. */
  152. if (pn->itr && (!s->username->len || !s->password->len)) {
  153. s->prompts = proxy_new_prompts(pn->ps);
  154. s->prompts->to_server = true;
  155. s->prompts->from_server = false;
  156. s->prompts->name = dupstr("SOCKS proxy authentication");
  157. if (!s->username->len) {
  158. s->username_prompt_index = s->prompts->n_prompts;
  159. add_prompt(s->prompts, dupstr("Proxy username: "), true);
  160. } else {
  161. s->username_prompt_index = -1;
  162. }
  163. if (!s->password->len) {
  164. s->password_prompt_index = s->prompts->n_prompts;
  165. add_prompt(s->prompts, dupstr("Proxy password: "), false);
  166. } else {
  167. s->password_prompt_index = -1;
  168. }
  169. while (true) {
  170. SeatPromptResult spr = seat_get_userpass_input(
  171. interactor_announce(pn->itr), s->prompts);
  172. if (spr.kind == SPRK_OK) {
  173. break;
  174. } else if (spr_is_abort(spr)) {
  175. proxy_spr_abort(pn, spr);
  176. crStopV;
  177. }
  178. crReturnV;
  179. }
  180. if (s->username_prompt_index != -1) {
  181. strbuf_clear(s->username);
  182. put_dataz(s->username,
  183. prompt_get_result_ref(
  184. s->prompts->prompts[s->username_prompt_index]));
  185. }
  186. if (s->password_prompt_index != -1) {
  187. strbuf_clear(s->password);
  188. put_dataz(s->password,
  189. prompt_get_result_ref(
  190. s->prompts->prompts[s->password_prompt_index]));
  191. }
  192. free_prompts(s->prompts);
  193. s->prompts = NULL;
  194. }
  195. /*
  196. * Now process the different auth methods that will use that
  197. * username and password. Note we can't do this using the natural
  198. * idiom of a switch statement, because there are crReturns inside
  199. * some cases.
  200. */
  201. if (s->auth_method == SOCKS5_AUTH_PASSWORD) {
  202. /*
  203. * SOCKS 5 password auth packet:
  204. *
  205. * byte version
  206. * pstring username
  207. * pstring password
  208. */
  209. put_byte(pn->output, SOCKS5_AUTH_PASSWORD_VERSION);
  210. if (!put_pstring(pn->output, s->username->s)) {
  211. pn->error = dupstr("SOCKS 5 authentication cannot support "
  212. "usernames longer than 255 chars");
  213. crStopV;
  214. }
  215. if (!put_pstring(pn->output, s->password->s)) {
  216. pn->error = dupstr("SOCKS 5 authentication cannot support "
  217. "passwords longer than 255 chars");
  218. crStopV;
  219. }
  220. /*
  221. * SOCKS 5 password reply packet:
  222. *
  223. * byte version
  224. * byte 0 for success, >0 for failure
  225. */
  226. { // WINSCP
  227. unsigned char data[2];
  228. crMaybeWaitUntilV(bufchain_try_fetch_consume(pn->input, data, 2));
  229. if (data[0] != SOCKS5_AUTH_PASSWORD_VERSION) {
  230. pn->error = dupprintf(
  231. "SOCKS 5 password reply had version number %d (expected "
  232. "%d)", (int)data[0], SOCKS5_AUTH_PASSWORD_VERSION);
  233. crStopV;
  234. }
  235. if (data[1] != 0) {
  236. pn->error = dupstr("SOCKS 5 server rejected our password");
  237. crStopV;
  238. }
  239. } // WINSCP
  240. } else if (s->auth_method == SOCKS5_AUTH_CHAP) {
  241. assert(socks5_chap_available);
  242. /*
  243. * All CHAP packets, in both directions, have the same
  244. * overall format:
  245. *
  246. * byte version
  247. * byte number of attributes
  248. *
  249. * and then for each attribute:
  250. *
  251. * byte attribute type
  252. * byte length
  253. * byte[] that many bytes of payload
  254. *
  255. * In the initial outgoing packet we send two attributes:
  256. * the list of supported algorithm names, and the
  257. * username.
  258. *
  259. * (It's possible that we ought to delay sending the
  260. * username until the second packet, in case the proxy
  261. * sent back an attribute indicating which character set
  262. * it would like us to use.)
  263. */
  264. put_byte(pn->output, SOCKS5_AUTH_CHAP_VERSION);
  265. put_byte(pn->output, 2); /* number of attributes */
  266. put_byte(pn->output, SOCKS5_AUTH_CHAP_ATTR_ALGLIST);
  267. put_byte(pn->output, 1); /* string length */
  268. put_byte(pn->output, SOCKS5_AUTH_CHAP_ALG_HMACMD5);
  269. /* Second attribute: username */
  270. {
  271. put_byte(pn->output, SOCKS5_AUTH_CHAP_ATTR_USERNAME);
  272. if (!put_pstring(pn->output, s->username->s)) {
  273. pn->error = dupstr(
  274. "SOCKS 5 CHAP authentication cannot support "
  275. "usernames longer than 255 chars");
  276. crStopV;
  277. }
  278. }
  279. while (true) {
  280. /*
  281. * Process a CHAP response packet, which has the same
  282. * overall format as the outgoing packet shown above.
  283. */
  284. unsigned char data[2];
  285. crMaybeWaitUntilV(bufchain_try_fetch_consume(
  286. pn->input, data, 2));
  287. if (data[0] != SOCKS5_AUTH_CHAP_VERSION) {
  288. pn->error = dupprintf(
  289. "SOCKS 5 CHAP reply had version number %d (expected "
  290. "%d)", (int)data[0], SOCKS5_AUTH_CHAP_VERSION);
  291. crStopV;
  292. }
  293. s->n_chap_attrs = data[1];
  294. if (s->n_chap_attrs == 0) {
  295. /*
  296. * If we receive a CHAP packet containing no
  297. * attributes, then we have nothing we didn't have
  298. * before, and can't make further progress.
  299. */
  300. pn->error = dupprintf(
  301. "SOCKS 5 CHAP reply sent no attributes");
  302. crStopV;
  303. }
  304. while (s->n_chap_attrs-- > 0) {
  305. unsigned char data[2];
  306. crMaybeWaitUntilV(bufchain_try_fetch_consume(
  307. pn->input, data, 2));
  308. s->chap_attr = data[0];
  309. s->chap_attr_len = data[1];
  310. crMaybeWaitUntilV(bufchain_try_fetch_consume(
  311. pn->input, s->chap_buf, s->chap_attr_len));
  312. if (s->chap_attr == SOCKS5_AUTH_CHAP_ATTR_STATUS) {
  313. if (s->chap_attr_len == 1 && s->chap_buf[0] == 0) {
  314. /* Status 0 means success: we are authenticated! */
  315. goto authenticated;
  316. } else {
  317. pn->error = dupstr(
  318. "SOCKS 5 CHAP authentication failed");
  319. crStopV;
  320. }
  321. } else if (s->chap_attr == SOCKS5_AUTH_CHAP_ATTR_CHALLENGE) {
  322. /* The CHAP challenge string. Send the response */
  323. strbuf *response = chap_response(
  324. make_ptrlen(s->chap_buf, s->chap_attr_len),
  325. ptrlen_from_strbuf(s->password));
  326. put_byte(pn->output, SOCKS5_AUTH_CHAP_VERSION);
  327. put_byte(pn->output, 1); /* number of attributes */
  328. put_byte(pn->output, SOCKS5_AUTH_CHAP_ATTR_RESPONSE);
  329. put_byte(pn->output, response->len);
  330. put_datapl(pn->output, ptrlen_from_strbuf(response));
  331. strbuf_free(response);
  332. } else {
  333. /* ignore all other attributes */
  334. }
  335. }
  336. }
  337. } else {
  338. unreachable("bad auth method in SOCKS 5 negotiation");
  339. }
  340. authenticated:
  341. /*
  342. * SOCKS 5 connection command:
  343. *
  344. * byte version
  345. * byte command
  346. * byte reserved (send as zero)
  347. * byte address type
  348. * byte[] address, with variable size (see below)
  349. * uint16 port
  350. */
  351. put_byte(pn->output, SOCKS5_REQUEST_VERSION);
  352. put_byte(pn->output, SOCKS_CMD_CONNECT);
  353. put_byte(pn->output, 0); /* reserved byte */
  354. switch (sk_addrtype(pn->ps->remote_addr)) {
  355. case ADDRTYPE_IPV4: {
  356. /* IPv4: address is 4 raw bytes */
  357. put_byte(pn->output, SOCKS5_ADDR_IPV4);
  358. { // WINSCP
  359. char buf[4];
  360. sk_addrcopy(pn->ps->remote_addr, buf);
  361. put_data(pn->output, buf, sizeof(buf));
  362. break;
  363. } // WINSCP
  364. }
  365. case ADDRTYPE_IPV6: {
  366. /* IPv6: address is 16 raw bytes */
  367. put_byte(pn->output, SOCKS5_ADDR_IPV6);
  368. { // WINSCP
  369. char buf[16];
  370. sk_addrcopy(pn->ps->remote_addr, buf);
  371. put_data(pn->output, buf, sizeof(buf));
  372. break;
  373. } // WINSCP
  374. }
  375. case ADDRTYPE_NAME: {
  376. /* Hostname: address is a pstring (Pascal-style string,
  377. * unterminated but with a one-byte prefix length) */
  378. put_byte(pn->output, SOCKS5_ADDR_HOSTNAME);
  379. { // WINSCP
  380. char hostname[512];
  381. sk_getaddr(pn->ps->remote_addr, hostname, lenof(hostname));
  382. if (!put_pstring(pn->output, hostname)) {
  383. pn->error = dupstr(
  384. "SOCKS 5 cannot support host names longer than 255 chars");
  385. crStopV;
  386. }
  387. } // WINSCP
  388. break;
  389. }
  390. default:
  391. unreachable("Unexpected addrtype in SOCKS 5 proxy");
  392. }
  393. put_uint16(pn->output, pn->ps->remote_port);
  394. crReturnV;
  395. /*
  396. * SOCKS 5 connection response:
  397. *
  398. * byte version
  399. * byte status
  400. * byte reserved
  401. * byte address type
  402. * byte[] address bound to (same formats as in connection request)
  403. * uint16 port
  404. *
  405. * We read the first four bytes and then decide what to do next.
  406. */
  407. {
  408. unsigned char data[4];
  409. crMaybeWaitUntilV(bufchain_try_fetch_consume(pn->input, data, 4));
  410. if (data[0] != SOCKS5_REPLY_VERSION) {
  411. pn->error = dupprintf("SOCKS proxy returned unexpected "
  412. "reply version %d (expected %d)",
  413. (int)data[0], SOCKS5_REPLY_VERSION);
  414. crStopV;
  415. }
  416. if (data[1] != SOCKS5_RESP_SUCCESS) {
  417. pn->error = dupprintf("SOCKS proxy failed to connect, error %d "
  418. "(%s)", (int)data[1],
  419. socks5_response_text(data[1]));
  420. crStopV;
  421. }
  422. /*
  423. * Process each address type to find out the size of the rest
  424. * of the packet. Note we can't do this using the natural
  425. * idiom of a switch statement, because there are crReturns
  426. * inside some cases.
  427. */
  428. if (data[3] == SOCKS5_ADDR_IPV4) {
  429. s->response_addr_length = 4;
  430. } else if (data[3] == SOCKS5_ADDR_IPV6) {
  431. s->response_addr_length = 16;
  432. } else if (data[3] == SOCKS5_ADDR_HOSTNAME) {
  433. /* Read the hostname length byte to find out how much to read */
  434. unsigned char len;
  435. crMaybeWaitUntilV(bufchain_try_fetch_consume(pn->input, &len, 1));
  436. s->response_addr_length = len;
  437. break;
  438. } else {
  439. pn->error = dupprintf("SOCKS proxy response included unknown "
  440. "address type %d", (int)data[3]);
  441. crStopV;
  442. }
  443. /* Read and ignore the address and port fields */
  444. crMaybeWaitUntilV(bufchain_try_consume(
  445. pn->input, s->response_addr_length + 2));
  446. }
  447. /* And we're done! */
  448. pn->done = true;
  449. crFinishV;
  450. }
  451. const struct ProxyNegotiatorVT socks5_proxy_negotiator_vt = {
  452. // WINSCP
  453. /*.new =*/ proxy_socks5_new,
  454. /*.process_queue =*/ proxy_socks5_process_queue,
  455. /*.free =*/ proxy_socks5_free,
  456. /*.type =*/ "SOCKS 5",
  457. };