http.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782
  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. bool found = false;
  284. size_t i;
  285. for (i = 0; i < N_HTTP_DIGEST_HASHES; i++) {
  286. if (!stricmp(s->token->s, httphashnames[i])) {
  287. found = true;
  288. break;
  289. }
  290. }
  291. if (!found) {
  292. /* We don't even recognise the name */
  293. return auth_error(d, "Digest hash algorithm '%s' not "
  294. "recognised", s->token->s);
  295. }
  296. if (!httphashaccepted[i]) {
  297. /* We do recognise the name but we
  298. * don't like it (see comment in cproxy.h) */
  299. return auth_error(d, "Digest hash algorithm '%s' not "
  300. "supported", s->token->s);
  301. }
  302. d->digest_hash = i;
  303. } else if (!stricmp(s->token->s, "qop")) {
  304. if (!get_separator(s, '=') ||
  305. !get_quoted_string(s))
  306. return auth_error(d, "parse error in Digest qop field");
  307. if (stricmp(s->token->s, "auth"))
  308. return auth_error(d, "quality-of-protection type '%s' not "
  309. "supported", s->token->s);
  310. } else {
  311. /* Ignore any other auth-param */
  312. if (!get_separator(s, '=') ||
  313. (!get_quoted_string(s) && !get_token(s)))
  314. return auth_error(d, "parse error in Digest header");
  315. }
  316. if (get_end_of_header(s))
  317. break;
  318. if (!get_separator(s, ','))
  319. return auth_error(d, "parse error in Digest header");
  320. }
  321. d->auth_type = AUTH_DIGEST;
  322. return d;
  323. }
  324. return auth_error(d, "authentication type '%s' not supported",
  325. s->token->s);
  326. }
  327. static void proxy_http_process_queue(ProxyNegotiator *pn)
  328. {
  329. HttpProxyNegotiator *s = container_of(pn, HttpProxyNegotiator, pn);
  330. crBegin(s->crLine);
  331. /*
  332. * Initialise our username and password strbufs from the Conf.
  333. */
  334. put_dataz(s->username, conf_get_str(pn->ps->conf, CONF_proxy_username));
  335. put_dataz(s->password, conf_get_str(pn->ps->conf, CONF_proxy_password));
  336. if (s->username->len || s->password->len)
  337. s->try_auth_from_conf = true;
  338. /*
  339. * Set up the host:port string we're trying to connect to, also
  340. * used as the URI string in HTTP Digest auth.
  341. */
  342. {
  343. char dest[512];
  344. sk_getaddr(pn->ps->remote_addr, dest, lenof(dest));
  345. put_fmt(s->uri, "%s:%d", dest, pn->ps->remote_port);
  346. }
  347. while (true) {
  348. /*
  349. * Standard prefix for the HTTP CONNECT request.
  350. */
  351. put_fmt(pn->output,
  352. "CONNECT %s HTTP/1.1\r\n"
  353. "Host: %s\r\n", s->uri->s, s->uri->s);
  354. /*
  355. * Add an auth header, if we're planning to this time round.
  356. */
  357. if (s->next_auth->auth_type == AUTH_BASIC) {
  358. put_datalit(pn->output, "Proxy-Authorization: Basic ");
  359. strbuf *base64_input = strbuf_new_nm();
  360. put_datapl(base64_input, ptrlen_from_strbuf(s->username));
  361. put_byte(base64_input, ':');
  362. put_datapl(base64_input, ptrlen_from_strbuf(s->password));
  363. char base64_output[4];
  364. for (size_t i = 0, e = base64_input->len; i < e; i += 3) {
  365. base64_encode_atom(base64_input->u + i,
  366. e-i > 3 ? 3 : e-i, base64_output);
  367. put_data(pn->output, base64_output, 4);
  368. }
  369. strbuf_free(base64_input);
  370. smemclr(base64_output, sizeof(base64_output));
  371. put_datalit(pn->output, "\r\n");
  372. } else if (s->next_auth->auth_type == AUTH_DIGEST) {
  373. put_datalit(pn->output, "Proxy-Authorization: Digest ");
  374. /* If we have a fresh nonce, reset the
  375. * nonce count. Otherwise, keep incrementing it. */
  376. if (!ptrlen_eq_ptrlen(ptrlen_from_strbuf(s->token),
  377. ptrlen_from_strbuf(s->next_auth->nonce)))
  378. s->nonce_count = 0;
  379. http_digest_response(BinarySink_UPCAST(pn->output),
  380. ptrlen_from_strbuf(s->username),
  381. ptrlen_from_strbuf(s->password),
  382. ptrlen_from_strbuf(s->next_auth->realm),
  383. PTRLEN_LITERAL("CONNECT"),
  384. ptrlen_from_strbuf(s->uri),
  385. PTRLEN_LITERAL("auth"),
  386. ptrlen_from_strbuf(s->next_auth->nonce),
  387. (s->next_auth->got_opaque ?
  388. ptrlen_from_strbuf(s->next_auth->opaque) :
  389. make_ptrlen(NULL, 0)),
  390. ++s->nonce_count, s->next_auth->digest_hash,
  391. s->next_auth->hash_username);
  392. put_datalit(pn->output, "\r\n");
  393. }
  394. /*
  395. * Blank line to terminate the HTTP request.
  396. */
  397. put_datalit(pn->output, "\r\n");
  398. crReturnV;
  399. s->content_length = 0;
  400. s->chunked_transfer = false;
  401. s->connection_close = false;
  402. /*
  403. * Read and parse the HTTP status line, and check if it's a 2xx
  404. * for success.
  405. */
  406. strbuf_clear(s->response);
  407. crMaybeWaitUntilV(read_line(pn->input, s->response, false));
  408. {
  409. int maj_ver, min_ver, n_scanned;
  410. n_scanned = sscanf(
  411. s->response->s, "HTTP/%d.%d %n%d",
  412. &maj_ver, &min_ver, &s->http_status_pos, &s->http_status);
  413. if (n_scanned < 3) {
  414. pn->error = dupstr("HTTP response was absent or malformed");
  415. crStopV;
  416. }
  417. if (maj_ver < 1 || (maj_ver == 1 && min_ver < 1)) {
  418. /* Before HTTP/1.1, connections close by default */
  419. s->connection_close = true;
  420. }
  421. }
  422. if (s->http_status == 407) {
  423. /*
  424. * If this is going to be an auth request, we expect to
  425. * see at least one Proxy-Authorization header offering us
  426. * auth options. Start by preloading s->next_auth with a
  427. * fallback error message, which will be used if nothing
  428. * better is available.
  429. */
  430. http_auth_details_free(s->next_auth);
  431. s->next_auth = http_auth_details_new();
  432. auth_error(s->next_auth, "no Proxy-Authorization header seen in "
  433. "HTTP 407 Proxy Authentication Required response");
  434. }
  435. /*
  436. * Read the HTTP response header section.
  437. */
  438. do {
  439. strbuf_clear(s->header);
  440. crMaybeWaitUntilV(read_line(pn->input, s->header, true));
  441. s->header_pos = 0;
  442. if (!get_token(s)) {
  443. /* Possibly we ought to panic if we see an HTTP header
  444. * we can't make any sense of at all? But whatever,
  445. * ignore it and hope the next one makes more sense */
  446. continue;
  447. }
  448. /* Parse the header name */
  449. HttpHeader hdr = HDR_UNKNOWN;
  450. {
  451. #define CHECK_HEADER(id, string) \
  452. if (!stricmp(s->token->s, string)) hdr = id;
  453. HTTP_HEADER_LIST(CHECK_HEADER);
  454. #undef CHECK_HEADER
  455. }
  456. if (!get_separator(s, ':'))
  457. continue;
  458. if (hdr == HDR_CONTENT_LENGTH) {
  459. if (!get_token(s))
  460. continue;
  461. s->content_length = strtoumax(s->token->s, NULL, 10);
  462. } else if (hdr == HDR_TRANSFER_ENCODING) {
  463. /*
  464. * The Transfer-Encoding header value should be a
  465. * comma-separated list of keywords including
  466. * "chunked", "deflate" and "gzip". We parse it in the
  467. * most superficial way, by just looking for "chunked"
  468. * and ignoring everything else.
  469. *
  470. * It's OK to do that because we're not actually
  471. * _using_ the error document - we only have to skip
  472. * over it to find the end of the HTTP response. So we
  473. * don't care if it's gzipped or not.
  474. */
  475. while (get_token(s)) {
  476. if (!stricmp(s->token->s, "chunked"))
  477. s->chunked_transfer = true;
  478. }
  479. } else if (hdr == HDR_CONNECTION ||
  480. hdr == HDR_PROXY_CONNECTION) {
  481. if (!get_token(s))
  482. continue;
  483. if (!stricmp(s->token->s, "close"))
  484. s->connection_close = true;
  485. else if (!stricmp(s->token->s, "keep-alive"))
  486. s->connection_close = false;
  487. } else if (hdr == HDR_PROXY_AUTHENTICATE) {
  488. HttpAuthDetails *auth = parse_http_auth_header(s);
  489. /*
  490. * See if we prefer this set of auth details to the
  491. * previous one we had (either from a previous auth
  492. * header, or the fallback when no auth header is
  493. * provided at all).
  494. */
  495. bool change;
  496. if (auth->auth_type != s->next_auth->auth_type) {
  497. /* Use the preference order implied by the enum */
  498. change = auth->auth_type > s->next_auth->auth_type;
  499. } else if (auth->auth_type == AUTH_DIGEST &&
  500. auth->digest_hash != s->next_auth->digest_hash) {
  501. /* Choose based on the hash functions */
  502. change = auth->digest_hash > s->next_auth->digest_hash;
  503. } else {
  504. /*
  505. * If in doubt, go with the later one of the
  506. * headers.
  507. *
  508. * The main reason for this is so that an error in
  509. * interpreting an auth header will supersede the
  510. * default error we preload saying 'no header
  511. * found', because that would be a particularly
  512. * bad error to report if there _was_ one.
  513. *
  514. * But we're in a tie-breaking situation by now,
  515. * so there's no other reason to choose - we might
  516. * as well apply the same policy everywhere else
  517. * too.
  518. */
  519. change = true;
  520. }
  521. if (change) {
  522. http_auth_details_free(s->next_auth);
  523. s->next_auth = auth;
  524. } else {
  525. http_auth_details_free(auth);
  526. }
  527. }
  528. } while (s->header->len > 0);
  529. /* Read and ignore the entire response document */
  530. if (!s->chunked_transfer) {
  531. /* Simple approach: read exactly Content-Length bytes */
  532. crMaybeWaitUntilV(bufchain_try_consume(
  533. pn->input, s->content_length));
  534. } else {
  535. /* Chunked transfer: read a sequence of
  536. * <hex length>\r\n<data>\r\n chunks, terminating in one with
  537. * zero length */
  538. do {
  539. /*
  540. * Expect a chunk length
  541. */
  542. s->chunk_length = 0;
  543. while (true) {
  544. char c;
  545. crMaybeWaitUntilV(bufchain_try_fetch_consume(
  546. pn->input, &c, 1));
  547. if (c == '\r') {
  548. continue;
  549. } else if (c == '\n') {
  550. break;
  551. } else if ('0' <= c && c <= '9') {
  552. s->chunk_length = s->chunk_length*16 + (c-'0');
  553. } else if ('A' <= c && c <= 'F') {
  554. s->chunk_length = s->chunk_length*16 + (c-'A'+10);
  555. } else if ('a' <= c && c <= 'f') {
  556. s->chunk_length = s->chunk_length*16 + (c-'a'+10);
  557. } else {
  558. pn->error = dupprintf(
  559. "Received bad character 0x%02X in chunk length "
  560. "during HTTP chunked transfer encoding",
  561. (unsigned)(unsigned char)c);
  562. crStopV;
  563. }
  564. }
  565. /*
  566. * Expect that many bytes of chunked data
  567. */
  568. crMaybeWaitUntilV(bufchain_try_consume(
  569. pn->input, s->chunk_length));
  570. /* Now expect \r\n */
  571. {
  572. char buf[2];
  573. crMaybeWaitUntilV(bufchain_try_fetch_consume(
  574. pn->input, buf, 2));
  575. if (memcmp(buf, "\r\n", 2)) {
  576. pn->error = dupprintf(
  577. "Missing CRLF after chunk "
  578. "during HTTP chunked transfer encoding");
  579. crStopV;
  580. }
  581. }
  582. } while (s->chunk_length);
  583. }
  584. if (200 <= s->http_status && s->http_status < 300) {
  585. /* Any 2xx HTTP response means we're done */
  586. goto authenticated;
  587. } else if (s->http_status == 407) {
  588. /* 407 is Proxy Authentication Required, which we may be
  589. * able to do something about. */
  590. if (s->connection_close) {
  591. /* If we got 407 + connection closed, reconnect before
  592. * sending our next request. */
  593. pn->reconnect = true;
  594. }
  595. /* If the best we can do is report some kind of error from
  596. * a Proxy-Auth header (or an error saying there wasn't
  597. * one at all), and no successful parsing of an auth
  598. * header superseded that, then just throw that error and
  599. * die. */
  600. if (s->next_auth->auth_type == AUTH_ERROR) {
  601. pn->error = dupstr(s->next_auth->error->s);
  602. crStopV;
  603. }
  604. /* If we have auth details from the Conf and haven't tried
  605. * them yet, that's our first step. */
  606. if (s->try_auth_from_conf) {
  607. s->try_auth_from_conf = false;
  608. continue;
  609. }
  610. /* If the server sent us stale="true" in a Digest auth
  611. * header, that means we _don't_ need to request a new
  612. * password yet; just try again with the existing details
  613. * and the fresh nonce it sent us. */
  614. if (s->next_auth->digest_nonce_was_stale)
  615. continue;
  616. /* Either we never had a password in the first place, or
  617. * the one we already presented was rejected. We can only
  618. * proceed from here if we have a way to ask the user
  619. * questions. */
  620. if (!pn->itr) {
  621. pn->error = dupprintf("HTTP proxy requested authentication "
  622. "which we do not have");
  623. crStopV;
  624. }
  625. /*
  626. * Send some prompts to the user. We'll assume the
  627. * password is always required (since it's just been
  628. * rejected, even if we did send one before), and we'll
  629. * prompt for the username only if we don't have one from
  630. * the Conf.
  631. */
  632. s->prompts = proxy_new_prompts(pn->ps);
  633. s->prompts->to_server = true;
  634. s->prompts->from_server = false;
  635. s->prompts->name = dupstr("HTTP proxy authentication");
  636. if (!s->username->len) {
  637. s->username_prompt_index = s->prompts->n_prompts;
  638. add_prompt(s->prompts, dupstr("Proxy username: "), true);
  639. } else {
  640. s->username_prompt_index = -1;
  641. }
  642. s->password_prompt_index = s->prompts->n_prompts;
  643. add_prompt(s->prompts, dupstr("Proxy password: "), false);
  644. while (true) {
  645. SeatPromptResult spr = seat_get_userpass_input(
  646. interactor_announce(pn->itr), s->prompts);
  647. if (spr.kind == SPRK_OK) {
  648. break;
  649. } else if (spr_is_abort(spr)) {
  650. proxy_spr_abort(pn, spr);
  651. crStopV;
  652. }
  653. crReturnV;
  654. }
  655. if (s->username_prompt_index != -1) {
  656. strbuf_clear(s->username);
  657. put_dataz(s->username,
  658. prompt_get_result_ref(
  659. s->prompts->prompts[s->username_prompt_index]));
  660. }
  661. strbuf_clear(s->password);
  662. put_dataz(s->password,
  663. prompt_get_result_ref(
  664. s->prompts->prompts[s->password_prompt_index]));
  665. free_prompts(s->prompts);
  666. s->prompts = NULL;
  667. } else {
  668. /* Any other HTTP response is treated as permanent failure */
  669. pn->error = dupprintf("HTTP response %s",
  670. s->response->s + s->http_status_pos);
  671. crStopV;
  672. }
  673. }
  674. authenticated:
  675. /*
  676. * Success! Hand over to the main connection.
  677. */
  678. pn->done = true;
  679. crFinishV;
  680. }
  681. const struct ProxyNegotiatorVT http_proxy_negotiator_vt = {
  682. .new = proxy_http_new,
  683. .free = proxy_http_free,
  684. .process_queue = proxy_http_process_queue,
  685. .type = "HTTP",
  686. };