http_p.c 25 KB

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