http_p.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. /*
  2. * HTTP CONNECT proxy negotiation.
  3. */
  4. #include "putty.h"
  5. #include "network.h"
  6. #include "proxy.h"
  7. #include "sshcr.h"
  8. static bool read_line(bufchain *input, strbuf *output, bool is_header)
  9. {
  10. char c;
  11. while (bufchain_try_fetch(input, &c, 1)) {
  12. if (is_header && output->len > 0 &&
  13. output->s[output->len - 1] == '\n') {
  14. /*
  15. * A newline terminates the header, provided we're sure it
  16. * is _not_ followed by a space or a tab.
  17. */
  18. if (c != ' ' && c != '\t')
  19. goto done; /* we have a complete header line */
  20. } else {
  21. put_byte(output, c);
  22. bufchain_consume(input, 1);
  23. if (!is_header && output->len > 0 &&
  24. output->s[output->len - 1] == '\n') {
  25. /* If we're looking for just a line, not an HTTP
  26. * header, then any newline terminates it. */
  27. goto done;
  28. }
  29. }
  30. }
  31. return false;
  32. done:
  33. strbuf_chomp(output, '\n');
  34. strbuf_chomp(output, '\r');
  35. return true;
  36. }
  37. /* Types of HTTP authentication, in preference order. */
  38. typedef enum HttpAuthType {
  39. AUTH_ERROR, /* if an HttpAuthDetails was never satisfactorily filled in */
  40. AUTH_NONE, /* if no auth header is seen, assume no auth required */
  41. AUTH_BASIC, /* username + password sent in clear (only keyless base64) */
  42. AUTH_DIGEST, /* cryptographic hash, most preferred if available */
  43. } HttpAuthType;
  44. typedef struct HttpAuthDetails {
  45. HttpAuthType auth_type;
  46. bool digest_nonce_was_stale;
  47. HttpDigestHash digest_hash;
  48. strbuf *realm, *nonce, *opaque, *error;
  49. bool got_opaque;
  50. bool hash_username;
  51. } HttpAuthDetails;
  52. typedef struct HttpProxyNegotiator {
  53. int crLine;
  54. strbuf *response, *header, *token;
  55. int http_status_pos;
  56. size_t header_pos;
  57. strbuf *username, *password;
  58. int http_status;
  59. bool connection_close;
  60. HttpAuthDetails *next_auth;
  61. bool try_auth_from_conf;
  62. strbuf *uri;
  63. uint32_t nonce_count;
  64. prompts_t *prompts;
  65. int username_prompt_index, password_prompt_index;
  66. size_t content_length, chunk_length;
  67. bool chunked_transfer;
  68. ProxyNegotiator pn;
  69. } HttpProxyNegotiator;
  70. static inline HttpAuthDetails *auth_error(HttpAuthDetails *d,
  71. const char *fmt, ...)
  72. {
  73. d->auth_type = AUTH_ERROR;
  74. put_fmt(d->error, "Unable to parse auth header from HTTP proxy");
  75. if (fmt) {
  76. va_list ap;
  77. va_start(ap, fmt);
  78. put_datalit(d->error, ": ");
  79. put_fmtv(d->error, fmt, ap);
  80. va_end(ap);
  81. }
  82. return d;
  83. }
  84. static HttpAuthDetails *http_auth_details_new(void)
  85. {
  86. HttpAuthDetails *d = snew(HttpAuthDetails);
  87. memset(d, 0, sizeof(*d));
  88. d->realm = strbuf_new();
  89. d->nonce = strbuf_new();
  90. d->opaque = strbuf_new();
  91. d->error = strbuf_new();
  92. return d;
  93. }
  94. static void http_auth_details_free(HttpAuthDetails *d)
  95. {
  96. strbuf_free(d->realm);
  97. strbuf_free(d->nonce);
  98. strbuf_free(d->opaque);
  99. strbuf_free(d->error);
  100. sfree(d);
  101. }
  102. static ProxyNegotiator *proxy_http_new(const ProxyNegotiatorVT *vt)
  103. {
  104. HttpProxyNegotiator *s = snew(HttpProxyNegotiator);
  105. memset(s, 0, sizeof(*s));
  106. s->pn.vt = vt;
  107. s->response = strbuf_new();
  108. s->header = strbuf_new();
  109. s->token = strbuf_new();
  110. s->username = strbuf_new();
  111. s->password = strbuf_new_nm();
  112. s->uri = strbuf_new();
  113. s->nonce_count = 0;
  114. /*
  115. * Always start with a CONNECT request containing no auth. If the
  116. * proxy rejects that, it will tell us what kind of auth it would
  117. * prefer.
  118. */
  119. s->next_auth = http_auth_details_new();
  120. s->next_auth->auth_type = AUTH_NONE;
  121. return &s->pn;
  122. }
  123. static void proxy_http_free(ProxyNegotiator *pn)
  124. {
  125. HttpProxyNegotiator *s = container_of(pn, HttpProxyNegotiator, pn);
  126. strbuf_free(s->response);
  127. strbuf_free(s->header);
  128. strbuf_free(s->token);
  129. strbuf_free(s->username);
  130. strbuf_free(s->password);
  131. strbuf_free(s->uri);
  132. http_auth_details_free(s->next_auth);
  133. if (s->prompts)
  134. free_prompts(s->prompts);
  135. sfree(s);
  136. }
  137. #define HTTP_HEADER_LIST(X) \
  138. X(HDR_CONNECTION, "Connection") \
  139. X(HDR_CONTENT_LENGTH, "Content-Length") \
  140. X(HDR_TRANSFER_ENCODING, "Transfer-Encoding") \
  141. X(HDR_PROXY_AUTHENTICATE, "Proxy-Authenticate") \
  142. X(HDR_PROXY_CONNECTION, "Proxy-Connection") \
  143. /* end of list */
  144. typedef enum HttpHeader {
  145. #define ENUM_DEF(id, string) id,
  146. HTTP_HEADER_LIST(ENUM_DEF)
  147. #undef ENUM_DEF
  148. HDR_UNKNOWN
  149. } HttpHeader;
  150. static inline bool is_whitespace(char c)
  151. {
  152. return (c == ' ' || c == '\t' || c == '\n');
  153. }
  154. static inline bool is_separator(char c)
  155. {
  156. return (c == '(' || c == ')' || c == '<' || c == '>' || c == '@' ||
  157. c == ',' || c == ';' || c == ':' || c == '\\' || c == '"' ||
  158. c == '/' || c == '[' || c == ']' || c == '?' || c == '=' ||
  159. c == '{' || c == '}');
  160. }
  161. #define HTTP_SEPARATORS
  162. static bool get_end_of_header(HttpProxyNegotiator *s)
  163. {
  164. size_t pos = s->header_pos;
  165. while (pos < s->header->len && is_whitespace(s->header->s[pos]))
  166. pos++;
  167. if (pos == s->header->len) {
  168. s->header_pos = pos;
  169. return true;
  170. }
  171. return false;
  172. }
  173. static bool get_token(HttpProxyNegotiator *s)
  174. {
  175. size_t pos = s->header_pos;
  176. while (pos < s->header->len && is_whitespace(s->header->s[pos]))
  177. pos++;
  178. if (pos == s->header->len)
  179. return false; /* end of string */
  180. if (is_separator(s->header->s[pos]))
  181. return false;
  182. strbuf_clear(s->token);
  183. while (pos < s->header->len &&
  184. !is_whitespace(s->header->s[pos]) &&
  185. !is_separator(s->header->s[pos]))
  186. put_byte(s->token, s->header->s[pos++]);
  187. s->header_pos = pos;
  188. return true;
  189. }
  190. static bool get_separator(HttpProxyNegotiator *s, char sep)
  191. {
  192. size_t pos = s->header_pos;
  193. while (pos < s->header->len && is_whitespace(s->header->s[pos]))
  194. pos++;
  195. if (pos == s->header->len)
  196. return false; /* end of string */
  197. if (s->header->s[pos] != sep)
  198. return false;
  199. s->header_pos = ++pos;
  200. return true;
  201. }
  202. static bool get_quoted_string(HttpProxyNegotiator *s)
  203. {
  204. size_t pos = s->header_pos;
  205. while (pos < s->header->len && is_whitespace(s->header->s[pos]))
  206. pos++;
  207. if (pos == s->header->len)
  208. return false; /* end of string */
  209. if (s->header->s[pos] != '"')
  210. return false;
  211. pos++;
  212. strbuf_clear(s->token);
  213. while (pos < s->header->len && s->header->s[pos] != '"') {
  214. if (s->header->s[pos] == '\\') {
  215. /* Backslash makes the next char literal, even if it's " or \ */
  216. pos++;
  217. if (pos == s->header->len)
  218. return false; /* unexpected end of string */
  219. }
  220. put_byte(s->token, s->header->s[pos++]);
  221. }
  222. if (pos == s->header->len)
  223. return false; /* no closing quote */
  224. pos++;
  225. s->header_pos = pos;
  226. return true;
  227. }
  228. static HttpAuthDetails *parse_http_auth_header(HttpProxyNegotiator *s)
  229. {
  230. HttpAuthDetails *d = http_auth_details_new();
  231. /* Default hash for HTTP Digest is MD5, if none specified explicitly */
  232. d->digest_hash = HTTP_DIGEST_MD5;
  233. if (!get_token(s))
  234. return auth_error(d, "parse error");
  235. if (!stricmp(s->token->s, "Basic")) {
  236. /* For Basic authentication, we don't need anything else. The
  237. * realm string is not required for the protocol. */
  238. d->auth_type = AUTH_BASIC;
  239. return d;
  240. }
  241. if (!stricmp(s->token->s, "Digest")) {
  242. /* Parse all the additional parts of the Digest header. */
  243. if (!http_digest_available)
  244. return auth_error(d, "Digest authentication not supported");
  245. /* Parse the rest of the Digest header */
  246. while (true) {
  247. if (!get_token(s))
  248. return auth_error(d, "parse error in Digest header");
  249. if (!stricmp(s->token->s, "realm")) {
  250. if (!get_separator(s, '=') ||
  251. !get_quoted_string(s))
  252. return auth_error(d, "parse error in Digest realm field");
  253. put_datapl(d->realm, ptrlen_from_strbuf(s->token));
  254. } else if (!stricmp(s->token->s, "nonce")) {
  255. if (!get_separator(s, '=') ||
  256. !get_quoted_string(s))
  257. return auth_error(d, "parse error in Digest nonce field");
  258. put_datapl(d->nonce, ptrlen_from_strbuf(s->token));
  259. } else if (!stricmp(s->token->s, "opaque")) {
  260. if (!get_separator(s, '=') ||
  261. !get_quoted_string(s))
  262. return auth_error(d, "parse error in Digest opaque field");
  263. put_datapl(d->opaque,
  264. ptrlen_from_strbuf(s->token));
  265. d->got_opaque = true;
  266. } else if (!stricmp(s->token->s, "stale")) {
  267. if (!get_separator(s, '=') ||
  268. !get_token(s))
  269. return auth_error(d, "parse error in Digest stale field");
  270. d->digest_nonce_was_stale = !stricmp(
  271. s->token->s, "true");
  272. } else if (!stricmp(s->token->s, "userhash")) {
  273. if (!get_separator(s, '=') ||
  274. !get_token(s))
  275. return auth_error(d, "parse error in Digest userhash "
  276. "field");
  277. d->hash_username = !stricmp(s->token->s, "true");
  278. } else if (!stricmp(s->token->s, "algorithm")) {
  279. if (!get_separator(s, '=') ||
  280. (!get_token(s) && !get_quoted_string(s)))
  281. return auth_error(d, "parse error in Digest algorithm "
  282. "field");
  283. { // WINSCP
  284. bool found = false;
  285. size_t i;
  286. for (i = 0; i < N_HTTP_DIGEST_HASHES; i++) {
  287. if (!stricmp(s->token->s, httphashnames[i])) {
  288. found = true;
  289. break;
  290. }
  291. }
  292. if (!found) {
  293. /* We don't even recognise the name */
  294. return auth_error(d, "Digest hash algorithm '%s' not "
  295. "recognised", s->token->s);
  296. }
  297. if (!httphashaccepted[i]) {
  298. /* We do recognise the name but we
  299. * don't like it (see comment in cproxy.h) */
  300. return auth_error(d, "Digest hash algorithm '%s' not "
  301. "supported", s->token->s);
  302. }
  303. d->digest_hash = i;
  304. } // WINSCP
  305. } else if (!stricmp(s->token->s, "qop")) {
  306. if (!get_separator(s, '=') ||
  307. !get_quoted_string(s))
  308. return auth_error(d, "parse error in Digest qop field");
  309. if (stricmp(s->token->s, "auth"))
  310. return auth_error(d, "quality-of-protection type '%s' not "
  311. "supported", s->token->s);
  312. } else {
  313. /* Ignore any other auth-param */
  314. if (!get_separator(s, '=') ||
  315. (!get_quoted_string(s) && !get_token(s)))
  316. return auth_error(d, "parse error in Digest header");
  317. }
  318. if (get_end_of_header(s))
  319. break;
  320. if (!get_separator(s, ','))
  321. return auth_error(d, "parse error in Digest header");
  322. }
  323. d->auth_type = AUTH_DIGEST;
  324. return d;
  325. }
  326. return auth_error(d, "authentication type '%s' not supported",
  327. s->token->s);
  328. }
  329. static void proxy_http_process_queue(ProxyNegotiator *pn)
  330. {
  331. HttpProxyNegotiator *s = container_of(pn, HttpProxyNegotiator, pn);
  332. crBegin(s->crLine);
  333. /*
  334. * Initialise our username and password strbufs from the Conf.
  335. */
  336. put_dataz(s->username, conf_get_str(pn->ps->conf, CONF_proxy_username));
  337. put_dataz(s->password, conf_get_str(pn->ps->conf, CONF_proxy_password));
  338. if (s->username->len || s->password->len)
  339. s->try_auth_from_conf = true;
  340. /*
  341. * Set up the host:port string we're trying to connect to, also
  342. * used as the URI string in HTTP Digest auth.
  343. */
  344. {
  345. char dest[512];
  346. sk_getaddr(pn->ps->remote_addr, dest, lenof(dest));
  347. put_fmt(s->uri, "%s:%d", dest, pn->ps->remote_port);
  348. }
  349. while (true) {
  350. /*
  351. * Standard prefix for the HTTP CONNECT request.
  352. */
  353. put_fmt(pn->output,
  354. "CONNECT %s HTTP/1.1\r\n"
  355. "Host: %s\r\n", s->uri->s, s->uri->s);
  356. /*
  357. * Add an auth header, if we're planning to this time round.
  358. */
  359. if (s->next_auth->auth_type == AUTH_BASIC) {
  360. put_datalit(pn->output, "Proxy-Authorization: Basic ");
  361. { // WINSCP
  362. strbuf *base64_input = strbuf_new_nm();
  363. put_datapl(base64_input, ptrlen_from_strbuf(s->username));
  364. put_byte(base64_input, ':');
  365. put_datapl(base64_input, ptrlen_from_strbuf(s->password));
  366. { // WINSCP
  367. char base64_output[4];
  368. size_t i, e; // WINSCP
  369. for (i = 0, e = base64_input->len; i < e; i += 3) {
  370. base64_encode_atom(base64_input->u + i,
  371. e-i > 3 ? 3 : e-i, base64_output);
  372. put_data(pn->output, base64_output, 4);
  373. }
  374. strbuf_free(base64_input);
  375. smemclr(base64_output, sizeof(base64_output));
  376. put_datalit(pn->output, "\r\n");
  377. } // WINSCP
  378. } // WINSCP
  379. } else if (s->next_auth->auth_type == AUTH_DIGEST) {
  380. put_datalit(pn->output, "Proxy-Authorization: Digest ");
  381. /* If we have a fresh nonce, reset the
  382. * nonce count. Otherwise, keep incrementing it. */
  383. if (!ptrlen_eq_ptrlen(ptrlen_from_strbuf(s->token),
  384. ptrlen_from_strbuf(s->next_auth->nonce)))
  385. s->nonce_count = 0;
  386. http_digest_response(BinarySink_UPCAST(pn->output),
  387. ptrlen_from_strbuf(s->username),
  388. ptrlen_from_strbuf(s->password),
  389. ptrlen_from_strbuf(s->next_auth->realm),
  390. PTRLEN_LITERAL("CONNECT"),
  391. ptrlen_from_strbuf(s->uri),
  392. PTRLEN_LITERAL("auth"),
  393. ptrlen_from_strbuf(s->next_auth->nonce),
  394. (s->next_auth->got_opaque ?
  395. ptrlen_from_strbuf(s->next_auth->opaque) :
  396. make_ptrlen(NULL, 0)),
  397. ++s->nonce_count, s->next_auth->digest_hash,
  398. s->next_auth->hash_username);
  399. put_datalit(pn->output, "\r\n");
  400. }
  401. /*
  402. * Blank line to terminate the HTTP request.
  403. */
  404. put_datalit(pn->output, "\r\n");
  405. crReturnV;
  406. s->content_length = 0;
  407. s->chunked_transfer = false;
  408. s->connection_close = false;
  409. /*
  410. * Read and parse the HTTP status line, and check if it's a 2xx
  411. * for success.
  412. */
  413. strbuf_clear(s->response);
  414. crMaybeWaitUntilV(read_line(pn->input, s->response, false));
  415. {
  416. int maj_ver, min_ver, n_scanned;
  417. n_scanned = sscanf(
  418. s->response->s, "HTTP/%d.%d %n%d",
  419. &maj_ver, &min_ver, &s->http_status_pos, &s->http_status);
  420. if (n_scanned < 3) {
  421. pn->error = dupstr("HTTP response was absent or malformed");
  422. crStopV;
  423. }
  424. if (maj_ver < 1 || (maj_ver == 1 && min_ver < 1)) {
  425. /* Before HTTP/1.1, connections close by default */
  426. s->connection_close = true;
  427. }
  428. }
  429. if (s->http_status == 407) {
  430. /*
  431. * If this is going to be an auth request, we expect to
  432. * see at least one Proxy-Authorization header offering us
  433. * auth options. Start by preloading s->next_auth with a
  434. * fallback error message, which will be used if nothing
  435. * better is available.
  436. */
  437. http_auth_details_free(s->next_auth);
  438. s->next_auth = http_auth_details_new();
  439. auth_error(s->next_auth, "no Proxy-Authorization header seen in "
  440. "HTTP 407 Proxy Authentication Required response");
  441. }
  442. /*
  443. * Read the HTTP response header section.
  444. */
  445. do {
  446. strbuf_clear(s->header);
  447. crMaybeWaitUntilV(read_line(pn->input, s->header, true));
  448. s->header_pos = 0;
  449. if (!get_token(s)) {
  450. /* Possibly we ought to panic if we see an HTTP header
  451. * we can't make any sense of at all? But whatever,
  452. * ignore it and hope the next one makes more sense */
  453. continue;
  454. }
  455. /* Parse the header name */
  456. { // WINSCP
  457. HttpHeader hdr = HDR_UNKNOWN;
  458. {
  459. #define CHECK_HEADER(id, string) \
  460. if (!stricmp(s->token->s, string)) hdr = id;
  461. HTTP_HEADER_LIST(CHECK_HEADER);
  462. #undef CHECK_HEADER
  463. }
  464. if (!get_separator(s, ':'))
  465. continue;
  466. if (hdr == HDR_CONTENT_LENGTH) {
  467. if (!get_token(s))
  468. continue;
  469. s->content_length = strtoumax(s->token->s, NULL, 10);
  470. } else if (hdr == HDR_TRANSFER_ENCODING) {
  471. /*
  472. * The Transfer-Encoding header value should be a
  473. * comma-separated list of keywords including
  474. * "chunked", "deflate" and "gzip". We parse it in the
  475. * most superficial way, by just looking for "chunked"
  476. * and ignoring everything else.
  477. *
  478. * It's OK to do that because we're not actually
  479. * _using_ the error document - we only have to skip
  480. * over it to find the end of the HTTP response. So we
  481. * don't care if it's gzipped or not.
  482. */
  483. while (get_token(s)) {
  484. if (!stricmp(s->token->s, "chunked"))
  485. s->chunked_transfer = true;
  486. }
  487. } else if (hdr == HDR_CONNECTION ||
  488. hdr == HDR_PROXY_CONNECTION) {
  489. if (!get_token(s))
  490. continue;
  491. if (!stricmp(s->token->s, "close"))
  492. s->connection_close = true;
  493. else if (!stricmp(s->token->s, "keep-alive"))
  494. s->connection_close = false;
  495. } else if (hdr == HDR_PROXY_AUTHENTICATE) {
  496. HttpAuthDetails *auth = parse_http_auth_header(s);
  497. /*
  498. * See if we prefer this set of auth details to the
  499. * previous one we had (either from a previous auth
  500. * header, or the fallback when no auth header is
  501. * provided at all).
  502. */
  503. bool change;
  504. if (auth->auth_type != s->next_auth->auth_type) {
  505. /* Use the preference order implied by the enum */
  506. change = auth->auth_type > s->next_auth->auth_type;
  507. } else if (auth->auth_type == AUTH_DIGEST &&
  508. auth->digest_hash != s->next_auth->digest_hash) {
  509. /* Choose based on the hash functions */
  510. change = auth->digest_hash > s->next_auth->digest_hash;
  511. } else {
  512. /*
  513. * If in doubt, go with the later one of the
  514. * headers.
  515. *
  516. * The main reason for this is so that an error in
  517. * interpreting an auth header will supersede the
  518. * default error we preload saying 'no header
  519. * found', because that would be a particularly
  520. * bad error to report if there _was_ one.
  521. *
  522. * But we're in a tie-breaking situation by now,
  523. * so there's no other reason to choose - we might
  524. * as well apply the same policy everywhere else
  525. * too.
  526. */
  527. change = true;
  528. }
  529. if (change) {
  530. http_auth_details_free(s->next_auth);
  531. s->next_auth = auth;
  532. } else {
  533. http_auth_details_free(auth);
  534. }
  535. }
  536. } // WINSCP
  537. } while (s->header->len > 0);
  538. /* Read and ignore the entire response document */
  539. if (!s->chunked_transfer) {
  540. /* Simple approach: read exactly Content-Length bytes */
  541. crMaybeWaitUntilV(bufchain_try_consume(
  542. pn->input, s->content_length));
  543. } else {
  544. /* Chunked transfer: read a sequence of
  545. * <hex length>\r\n<data>\r\n chunks, terminating in one with
  546. * zero length */
  547. do {
  548. /*
  549. * Expect a chunk length
  550. */
  551. s->chunk_length = 0;
  552. while (true) {
  553. char c;
  554. crMaybeWaitUntilV(bufchain_try_fetch_consume(
  555. pn->input, &c, 1));
  556. if (c == '\r') {
  557. continue;
  558. } else if (c == '\n') {
  559. break;
  560. } else if ('0' <= c && c <= '9') {
  561. s->chunk_length = s->chunk_length*16 + (c-'0');
  562. } else if ('A' <= c && c <= 'F') {
  563. s->chunk_length = s->chunk_length*16 + (c-'A'+10);
  564. } else if ('a' <= c && c <= 'f') {
  565. s->chunk_length = s->chunk_length*16 + (c-'a'+10);
  566. } else {
  567. pn->error = dupprintf(
  568. "Received bad character 0x%02X in chunk length "
  569. "during HTTP chunked transfer encoding",
  570. (unsigned)(unsigned char)c);
  571. crStopV;
  572. }
  573. }
  574. /*
  575. * Expect that many bytes of chunked data
  576. */
  577. crMaybeWaitUntilV(bufchain_try_consume(
  578. pn->input, s->chunk_length));
  579. /* Now expect \r\n */
  580. {
  581. char buf[2];
  582. crMaybeWaitUntilV(bufchain_try_fetch_consume(
  583. pn->input, buf, 2));
  584. if (memcmp(buf, "\r\n", 2)) {
  585. pn->error = dupprintf(
  586. "Missing CRLF after chunk "
  587. "during HTTP chunked transfer encoding");
  588. crStopV;
  589. }
  590. }
  591. } while (s->chunk_length);
  592. }
  593. if (200 <= s->http_status && s->http_status < 300) {
  594. /* Any 2xx HTTP response means we're done */
  595. goto authenticated;
  596. } else if (s->http_status == 407) {
  597. /* 407 is Proxy Authentication Required, which we may be
  598. * able to do something about. */
  599. if (s->connection_close) {
  600. /* If we got 407 + connection closed, reconnect before
  601. * sending our next request. */
  602. pn->reconnect = true;
  603. }
  604. /* If the best we can do is report some kind of error from
  605. * a Proxy-Auth header (or an error saying there wasn't
  606. * one at all), and no successful parsing of an auth
  607. * header superseded that, then just throw that error and
  608. * die. */
  609. if (s->next_auth->auth_type == AUTH_ERROR) {
  610. pn->error = dupstr(s->next_auth->error->s);
  611. crStopV;
  612. }
  613. /* If we have auth details from the Conf and haven't tried
  614. * them yet, that's our first step. */
  615. if (s->try_auth_from_conf) {
  616. s->try_auth_from_conf = false;
  617. continue;
  618. }
  619. /* If the server sent us stale="true" in a Digest auth
  620. * header, that means we _don't_ need to request a new
  621. * password yet; just try again with the existing details
  622. * and the fresh nonce it sent us. */
  623. if (s->next_auth->digest_nonce_was_stale)
  624. continue;
  625. /* Either we never had a password in the first place, or
  626. * the one we already presented was rejected. We can only
  627. * proceed from here if we have a way to ask the user
  628. * questions. */
  629. if (!pn->itr) {
  630. pn->error = dupprintf("HTTP proxy requested authentication "
  631. "which we do not have");
  632. crStopV;
  633. }
  634. /*
  635. * Send some prompts to the user. We'll assume the
  636. * password is always required (since it's just been
  637. * rejected, even if we did send one before), and we'll
  638. * prompt for the username only if we don't have one from
  639. * the Conf.
  640. */
  641. s->prompts = proxy_new_prompts(pn->ps);
  642. s->prompts->to_server = true;
  643. s->prompts->from_server = false;
  644. s->prompts->name = dupstr("HTTP proxy authentication");
  645. if (!s->username->len) {
  646. s->username_prompt_index = s->prompts->n_prompts;
  647. add_prompt(s->prompts, dupstr("Proxy username: "), true);
  648. } else {
  649. s->username_prompt_index = -1;
  650. }
  651. s->password_prompt_index = s->prompts->n_prompts;
  652. add_prompt(s->prompts, dupstr("Proxy password: "), false);
  653. while (true) {
  654. SeatPromptResult spr = seat_get_userpass_input(
  655. interactor_announce(pn->itr), s->prompts);
  656. if (spr.kind == SPRK_OK) {
  657. break;
  658. } else if (spr_is_abort(spr)) {
  659. proxy_spr_abort(pn, spr);
  660. crStopV;
  661. }
  662. crReturnV;
  663. }
  664. if (s->username_prompt_index != -1) {
  665. strbuf_clear(s->username);
  666. put_dataz(s->username,
  667. prompt_get_result_ref(
  668. s->prompts->prompts[s->username_prompt_index]));
  669. }
  670. strbuf_clear(s->password);
  671. put_dataz(s->password,
  672. prompt_get_result_ref(
  673. s->prompts->prompts[s->password_prompt_index]));
  674. free_prompts(s->prompts);
  675. s->prompts = NULL;
  676. } else {
  677. /* Any other HTTP response is treated as permanent failure */
  678. pn->error = dupprintf("HTTP response %s",
  679. s->response->s + s->http_status_pos);
  680. crStopV;
  681. }
  682. }
  683. authenticated:
  684. /*
  685. * Success! Hand over to the main connection.
  686. */
  687. pn->done = true;
  688. crFinishV;
  689. }
  690. const struct ProxyNegotiatorVT http_proxy_negotiator_vt = {
  691. // WINSCP
  692. /*.new =*/ proxy_http_new,
  693. /*.process_queue =*/ proxy_http_process_queue,
  694. /*.free =*/ proxy_http_free,
  695. /*.type =*/ "HTTP",
  696. };