1/*
2 *  OpenVPN -- An application to securely tunnel IP networks
3 *             over a single TCP/UDP port, with support for SSL/TLS-based
4 *             session authentication and key exchange,
5 *             packet encryption, packet authentication, and
6 *             packet compression.
7 *
8 *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
9 *
10 *  This program is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 2
12 *  as published by the Free Software Foundation.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this program (see the file COPYING included with this
21 *  distribution); if not, write to the Free Software Foundation, Inc.,
22 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#elif defined(_MSC_VER)
28#include "config-msvc.h"
29#endif
30
31#include "syshead.h"
32
33#include "common.h"
34#include "misc.h"
35#include "crypto.h"
36#include "win32.h"
37#include "socket.h"
38#include "fdmisc.h"
39#include "proxy.h"
40#include "base64.h"
41#include "httpdigest.h"
42#include "ntlm.h"
43#include "memdbg.h"
44
45#ifdef ENABLE_HTTP_PROXY
46
47#define UP_TYPE_PROXY        "HTTP Proxy"
48
49struct http_proxy_options *
50init_http_proxy_options_once (struct http_proxy_options **hpo,
51                              struct gc_arena *gc)
52{
53  if (!*hpo)
54    {
55      ALLOC_OBJ_CLEAR_GC (*hpo, struct http_proxy_options, gc);
56      /* http proxy defaults */
57      (*hpo)->timeout = 5;
58      (*hpo)->http_version = "1.0";
59    }
60  return *hpo;
61}
62
63
64/* cached proxy username/password */
65static struct user_pass static_proxy_user_pass;
66
67static bool
68recv_line (socket_descriptor_t sd,
69	   char *buf,
70	   int len,
71	   const int timeout_sec,
72	   const bool verbose,
73	   struct buffer *lookahead,
74	   volatile int *signal_received)
75{
76  struct buffer la;
77  int lastc = 0;
78
79  CLEAR (la);
80  if (lookahead)
81    la = *lookahead;
82
83  while (true)
84    {
85      int status;
86      ssize_t size;
87      fd_set reads;
88      struct timeval tv;
89      uint8_t c;
90
91      if (buf_defined (&la))
92	{
93	  ASSERT (buf_init (&la, 0));
94	}
95
96      FD_ZERO (&reads);
97      FD_SET (sd, &reads);
98      tv.tv_sec = timeout_sec;
99      tv.tv_usec = 0;
100
101      status = select (sd + 1, &reads, NULL, NULL, &tv);
102
103      get_signal (signal_received);
104      if (*signal_received)
105	goto error;
106
107      /* timeout? */
108      if (status == 0)
109	{
110	  if (verbose)
111	    msg (D_LINK_ERRORS | M_ERRNO, "recv_line: TCP port read timeout expired");
112	  goto error;
113	}
114
115      /* error */
116      if (status < 0)
117	{
118	  if (verbose)
119	    msg (D_LINK_ERRORS | M_ERRNO, "recv_line: TCP port read failed on select()");
120	  goto error;
121	}
122
123      /* read single char */
124      size = recv (sd, &c, 1, MSG_NOSIGNAL);
125
126      /* error? */
127      if (size != 1)
128	{
129	  if (verbose)
130	    msg (D_LINK_ERRORS | M_ERRNO, "recv_line: TCP port read failed on recv()");
131	  goto error;
132	}
133
134#if 0
135      if (isprint(c))
136	msg (M_INFO, "PROXY: read '%c' (%d)", c, (int)c);
137      else
138	msg (M_INFO, "PROXY: read (%d)", (int)c);
139#endif
140
141      /* store char in buffer */
142      if (len > 1)
143	{
144	  *buf++ = c;
145	  --len;
146	}
147
148      /* also store char in lookahead buffer */
149      if (buf_defined (&la))
150	{
151	  buf_write_u8 (&la, c);
152	  if (!isprint(c) && !isspace(c)) /* not ascii? */
153	    {
154	      if (verbose)
155		msg (D_LINK_ERRORS | M_ERRNO, "recv_line: Non-ASCII character (%d) read on recv()", (int)c);
156	      *lookahead = la;
157	      return false;
158	    }
159	}
160
161      /* end of line? */
162      if (lastc == '\r' && c == '\n')
163	break;
164
165      lastc = c;
166    }
167
168  /* append trailing null */
169  if (len > 0)
170    *buf++ = '\0';
171
172  return true;
173
174 error:
175  return false;
176}
177
178static bool
179send_line (socket_descriptor_t sd,
180	   const char *buf)
181{
182  const ssize_t size = send (sd, buf, strlen (buf), MSG_NOSIGNAL);
183  if (size != (ssize_t) strlen (buf))
184    {
185      msg (D_LINK_ERRORS | M_ERRNO, "send_line: TCP port write failed on send()");
186      return false;
187    }
188  return true;
189}
190
191static bool
192send_line_crlf (socket_descriptor_t sd,
193		const char *src)
194{
195  bool ret;
196
197  struct buffer buf = alloc_buf (strlen (src) + 3);
198  ASSERT (buf_write (&buf, src, strlen (src)));
199  ASSERT (buf_write (&buf, "\r\n", 3));
200  ret = send_line (sd, BSTR (&buf));
201  free_buf (&buf);
202  return ret;
203}
204
205static bool
206send_crlf (socket_descriptor_t sd)
207{
208  return send_line_crlf (sd, "");
209}
210
211uint8_t *
212make_base64_string2 (const uint8_t *str, int src_len, struct gc_arena *gc)
213{
214  uint8_t *ret = NULL;
215  char *b64out = NULL;
216  ASSERT (openvpn_base64_encode ((const void *)str, src_len, &b64out) >= 0);
217  ret = (uint8_t *) string_alloc (b64out, gc);
218  free (b64out);
219  return ret;
220}
221
222uint8_t *
223make_base64_string (const uint8_t *str, struct gc_arena *gc)
224{
225  return make_base64_string2 (str, strlen ((const char *)str), gc);
226}
227
228static const char *
229username_password_as_base64 (const struct http_proxy_info *p,
230			     struct gc_arena *gc)
231{
232  struct buffer out = alloc_buf_gc (strlen (p->up.username) + strlen (p->up.password) + 2, gc);
233  ASSERT (strlen (p->up.username) > 0);
234  buf_printf (&out, "%s:%s", p->up.username, p->up.password);
235  return (const char *)make_base64_string ((const uint8_t*)BSTR (&out), gc);
236}
237
238static void
239get_user_pass_http (struct http_proxy_info *p, const bool force)
240{
241  if (!static_proxy_user_pass.defined || force)
242    {
243      unsigned int flags = GET_USER_PASS_MANAGEMENT;
244      if (p->queried_creds)
245	flags |= GET_USER_PASS_PREVIOUS_CREDS_FAILED;
246      get_user_pass (&static_proxy_user_pass,
247		     p->options.auth_file,
248		     UP_TYPE_PROXY,
249		     flags);
250      p->queried_creds = true;
251      p->up = static_proxy_user_pass;
252    }
253}
254static void
255clear_user_pass_http (void)
256{
257  purge_user_pass (&static_proxy_user_pass, true);
258}
259
260static void
261dump_residual (socket_descriptor_t sd,
262	       int timeout,
263	       volatile int *signal_received)
264{
265  char buf[256];
266  while (true)
267    {
268      if (!recv_line (sd, buf, sizeof (buf), timeout, true, NULL, signal_received))
269	return;
270      chomp (buf);
271      msg (D_PROXY, "PROXY HEADER: '%s'", buf);
272    }
273}
274
275/*
276 * Extract the Proxy-Authenticate header from the stream.
277 * Consumes all headers.
278 */
279static int
280get_proxy_authenticate (socket_descriptor_t sd,
281		        int timeout,
282			char **data,
283			struct gc_arena *gc,
284		        volatile int *signal_received)
285{
286  char buf[256];
287  int ret = HTTP_AUTH_NONE;
288  while (true)
289    {
290      if (!recv_line (sd, buf, sizeof (buf), timeout, true, NULL, signal_received))
291	{
292	  *data = NULL;
293	  return HTTP_AUTH_NONE;
294	}
295      chomp (buf);
296      if (!strlen(buf))
297	return ret;
298      if (ret == HTTP_AUTH_NONE && !strncmp(buf, "Proxy-Authenticate: ", 20))
299	{
300	  if (!strncmp(buf+20, "Basic ", 6))
301	    {
302	      msg (D_PROXY, "PROXY AUTH BASIC: '%s'", buf);
303	      *data = string_alloc(buf+26, gc);
304	      ret = HTTP_AUTH_BASIC;
305	    }
306#if PROXY_DIGEST_AUTH
307	  else if (!strncmp(buf+20, "Digest ", 7))
308	    {
309	      msg (D_PROXY, "PROXY AUTH DIGEST: '%s'", buf);
310	      *data = string_alloc(buf+27, gc);
311	      ret = HTTP_AUTH_DIGEST;
312	    }
313#endif
314#if NTLM
315	  else if (!strncmp(buf+20, "NTLM", 4))
316	    {
317	      msg (D_PROXY, "PROXY AUTH HTLM: '%s'", buf);
318	      *data = NULL;
319	      ret = HTTP_AUTH_NTLM;
320	    }
321#endif
322	}
323    }
324}
325
326static void
327store_proxy_authenticate (struct http_proxy_info *p, char *data)
328{
329  if (p->proxy_authenticate)
330    free (p->proxy_authenticate);
331  p->proxy_authenticate = data;
332}
333
334/*
335 * Parse out key/value pairs from Proxy-Authenticate string.
336 * Return true on success, or false on parse failure.
337 */
338static bool
339get_key_value(const char *str,       /* source string */
340	      char *key,             /* key stored here */
341	      char *value,           /* value stored here */
342	      int max_key_len,
343	      int max_value_len,
344	      const char **endptr)   /* next search position */
345{
346  int c;
347  bool starts_with_quote = false;
348  bool escape = false;
349
350  for (c = max_key_len-1; (*str && (*str != '=') && c--); )
351    *key++ = *str++;
352  *key = '\0';
353
354  if('=' != *str++)
355    /* no key/value found */
356    return false;
357
358  if('\"' == *str)
359    {
360      /* quoted string */
361      str++;
362      starts_with_quote = true;
363    }
364
365  for (c = max_value_len-1; *str && c--; str++)
366    {
367      switch (*str)
368	{
369	case '\\':
370	  if (!escape)
371	    {
372	      /* possibly the start of an escaped quote */
373	      escape = true;
374	      *value++ = '\\'; /* even though this is an escape character, we still
375				  store it as-is in the target buffer */
376	      continue;
377	    }
378	  break;
379	case ',':
380	  if (!starts_with_quote)
381	    {
382	      /* this signals the end of the value if we didn't get a starting quote
383		 and then we do "sloppy" parsing */
384	      c=0; /* the end */
385	      continue;
386	    }
387	  break;
388	case '\r':
389	case '\n':
390	  /* end of string */
391	  c=0;
392	continue;
393	case '\"':
394	  if (!escape && starts_with_quote)
395	    {
396	      /* end of string */
397	      c=0;
398	      continue;
399	    }
400	  break;
401	}
402      escape = false;
403      *value++ = *str;
404    }
405  *value = '\0';
406
407  *endptr = str;
408
409  return true; /* success */
410}
411
412static char *
413get_pa_var (const char *key, const char *pa, struct gc_arena *gc)
414{
415  char k[64];
416  char v[256];
417  const char *content = pa;
418
419  while (true)
420    {
421      const int status = get_key_value(content, k, v, sizeof(k), sizeof(v), &content);
422      if (status)
423	{
424	  if (!strcmp(key, k))
425	    return string_alloc(v, gc);
426	}
427      else
428	return NULL;
429
430      /* advance to start of next key */
431      if (*content == ',')
432	++content;
433      while (*content && isspace(*content))
434	++content;
435    }
436}
437
438struct http_proxy_info *
439http_proxy_new (const struct http_proxy_options *o)
440{
441  struct http_proxy_info *p;
442  struct http_proxy_options opt;
443
444  if (!o || !o->server)
445    msg (M_FATAL, "HTTP_PROXY: server not specified");
446
447  ASSERT (legal_ipv4_port (o->port));
448
449  ALLOC_OBJ_CLEAR (p, struct http_proxy_info);
450  p->options = *o;
451
452  /* parse authentication method */
453  p->auth_method = HTTP_AUTH_NONE;
454  if (o->auth_method_string)
455    {
456      if (!strcmp (o->auth_method_string, "none"))
457	p->auth_method = HTTP_AUTH_NONE;
458      else if (!strcmp (o->auth_method_string, "basic"))
459	p->auth_method = HTTP_AUTH_BASIC;
460#if NTLM
461      else if (!strcmp (o->auth_method_string, "ntlm"))
462	p->auth_method = HTTP_AUTH_NTLM;
463      else if (!strcmp (o->auth_method_string, "ntlm2"))
464	p->auth_method = HTTP_AUTH_NTLM2;
465#endif
466      else
467	msg (M_FATAL, "ERROR: unknown HTTP authentication method: '%s'",
468	     o->auth_method_string);
469    }
470
471  /* only basic and NTLM/NTLMv2 authentication supported so far */
472  if (p->auth_method == HTTP_AUTH_BASIC || p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2)
473    {
474      get_user_pass_http (p, true);
475    }
476
477#if !NTLM
478  if (p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2)
479    msg (M_FATAL, "Sorry, this version of " PACKAGE_NAME " was built without NTLM Proxy support.");
480#endif
481
482  p->defined = true;
483  return p;
484}
485
486void
487http_proxy_close (struct http_proxy_info *hp)
488{
489  free (hp);
490}
491
492bool
493establish_http_proxy_passthru (struct http_proxy_info *p,
494			       socket_descriptor_t sd, /* already open to proxy */
495			       const char *host,       /* openvpn server remote */
496			       const int port,         /* openvpn server port */
497			       struct buffer *lookahead,
498			       volatile int *signal_received)
499{
500  struct gc_arena gc = gc_new ();
501  char buf[512];
502  char buf2[129];
503  char get[80];
504  int status;
505  int nparms;
506  bool ret = false;
507  bool processed = false;
508
509  /* get user/pass if not previously given */
510  if (p->auth_method == HTTP_AUTH_BASIC
511      || p->auth_method == HTTP_AUTH_DIGEST
512      || p->auth_method == HTTP_AUTH_NTLM)
513    get_user_pass_http (p, false);
514
515  /* are we being called again after getting the digest server nonce in the previous transaction? */
516  if (p->auth_method == HTTP_AUTH_DIGEST && p->proxy_authenticate)
517    {
518      nparms = 1;
519      status = 407;
520    }
521  else
522    {
523      /* format HTTP CONNECT message */
524      openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s",
525			host,
526			port,
527			p->options.http_version);
528
529      msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
530
531      /* send HTTP CONNECT message to proxy */
532      if (!send_line_crlf (sd, buf))
533	goto error;
534
535      openvpn_snprintf(buf, sizeof(buf), "Host: %s", host);
536      if (!send_line_crlf(sd, buf))
537        goto error;
538
539      /* send User-Agent string if provided */
540      if (p->options.user_agent)
541	{
542	  openvpn_snprintf (buf, sizeof(buf), "User-Agent: %s",
543			    p->options.user_agent);
544	  if (!send_line_crlf (sd, buf))
545	    goto error;
546	}
547
548      /* auth specified? */
549      switch (p->auth_method)
550	{
551	case HTTP_AUTH_NONE:
552	  break;
553
554	case HTTP_AUTH_BASIC:
555	  openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: Basic %s",
556			    username_password_as_base64 (p, &gc));
557	  msg (D_PROXY, "Attempting Basic Proxy-Authorization");
558	  dmsg (D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf);
559	  if (!send_line_crlf (sd, buf))
560	    goto error;
561	  break;
562
563#if NTLM
564	case HTTP_AUTH_NTLM:
565	case HTTP_AUTH_NTLM2:
566	  /* keep-alive connection */
567	  openvpn_snprintf (buf, sizeof(buf), "Proxy-Connection: Keep-Alive");
568	  if (!send_line_crlf (sd, buf))
569	    goto error;
570
571	  openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: NTLM %s",
572			    ntlm_phase_1 (p, &gc));
573	  msg (D_PROXY, "Attempting NTLM Proxy-Authorization phase 1");
574	  dmsg (D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf);
575	  if (!send_line_crlf (sd, buf))
576	    goto error;
577	  break;
578#endif
579
580	default:
581	  ASSERT (0);
582	}
583
584      /* send empty CR, LF */
585      if (!send_crlf (sd))
586	goto error;
587
588      /* receive reply from proxy */
589      if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received))
590	goto error;
591
592      /* remove trailing CR, LF */
593      chomp (buf);
594
595      msg (D_PROXY, "HTTP proxy returned: '%s'", buf);
596
597      /* parse return string */
598      nparms = sscanf (buf, "%*s %d", &status);
599
600    }
601
602  /* check for a "407 Proxy Authentication Required" response */
603  while (nparms >= 1 && status == 407)
604    {
605      msg (D_PROXY, "Proxy requires authentication");
606
607      if (p->auth_method == HTTP_AUTH_BASIC && !processed)
608	{
609	  processed = true;
610	}
611      else if ((p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2) && !processed) /* check for NTLM */
612        {
613#if NTLM
614          /* look for the phase 2 response */
615
616          while (true)
617            {
618              if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received))
619                goto error;
620              chomp (buf);
621              msg (D_PROXY, "HTTP proxy returned: '%s'", buf);
622
623              openvpn_snprintf (get, sizeof get, "%%*s NTLM %%%ds", (int) sizeof (buf2) - 1);
624              nparms = sscanf (buf, get, buf2);
625              buf2[128] = 0; /* we only need the beginning - ensure it's null terminated. */
626
627              /* check for "Proxy-Authenticate: NTLM TlRM..." */
628              if (nparms == 1)
629                {
630                  /* parse buf2 */
631                  msg (D_PROXY, "auth string: '%s'", buf2);
632                  break;
633                }
634            }
635          /* if we are here then auth string was got */
636          msg (D_PROXY, "Received NTLM Proxy-Authorization phase 2 response");
637
638          /* receive and discard everything else */
639          while (recv_line (sd, NULL, 0, 2, true, NULL, signal_received))
640            ;
641
642          /* now send the phase 3 reply */
643
644          /* format HTTP CONNECT message */
645          openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s",
646			    host,
647			    port,
648			    p->options.http_version);
649
650          msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
651
652          /* send HTTP CONNECT message to proxy */
653          if (!send_line_crlf (sd, buf))
654            goto error;
655
656          /* keep-alive connection */
657          openvpn_snprintf (buf, sizeof(buf), "Proxy-Connection: Keep-Alive");
658          if (!send_line_crlf (sd, buf))
659            goto error;
660
661
662          /* send HOST etc, */
663          openvpn_snprintf (buf, sizeof(buf), "Host: %s", host);
664          msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
665          if (!send_line_crlf (sd, buf))
666            goto error;
667
668          msg (D_PROXY, "Attempting NTLM Proxy-Authorization phase 3");
669	  {
670	    const char *np3 = ntlm_phase_3 (p, buf2, &gc);
671	    if (!np3)
672	      {
673		msg (D_PROXY, "NTLM Proxy-Authorization phase 3 failed: received corrupted data from proxy server");
674		goto error;
675	      }
676	    openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: NTLM %s", np3);
677	  }
678
679          msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
680          if (!send_line_crlf (sd, buf))
681	    goto error;
682          /* ok so far... */
683          /* send empty CR, LF */
684          if (!send_crlf (sd))
685            goto error;
686
687          /* receive reply from proxy */
688          if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received))
689            goto error;
690
691          /* remove trailing CR, LF */
692          chomp (buf);
693
694          msg (D_PROXY, "HTTP proxy returned: '%s'", buf);
695
696          /* parse return string */
697          nparms = sscanf (buf, "%*s %d", &status);
698	  processed = true;
699#endif
700	}
701#if PROXY_DIGEST_AUTH
702      else if (p->auth_method == HTTP_AUTH_DIGEST && !processed)
703	{
704	  char *pa = p->proxy_authenticate;
705	  const int method = p->auth_method;
706	  ASSERT(pa);
707
708	  if (method == HTTP_AUTH_DIGEST)
709	    {
710	      const char *http_method = "CONNECT";
711	      const char *nonce_count = "00000001";
712	      const char *qop = "auth";
713	      const char *username = p->up.username;
714	      const char *password = p->up.password;
715	      char *opaque_kv = "";
716	      char uri[128];
717	      uint8_t cnonce_raw[8];
718	      uint8_t *cnonce;
719	      HASHHEX session_key;
720	      HASHHEX response;
721
722	      const char *realm = get_pa_var("realm", pa, &gc);
723	      const char *nonce = get_pa_var("nonce", pa, &gc);
724	      const char *algor = get_pa_var("algorithm", pa, &gc);
725	      const char *opaque = get_pa_var("opaque", pa, &gc);
726
727	      /* generate a client nonce */
728	      ASSERT(rand_bytes(cnonce_raw, sizeof(cnonce_raw)));
729	      cnonce = make_base64_string2(cnonce_raw, sizeof(cnonce_raw), &gc);
730
731
732	      /* build the digest response */
733	      openvpn_snprintf (uri, sizeof(uri), "%s:%d",
734				host,
735				port);
736
737	      if (opaque)
738		{
739		  const int len = strlen(opaque)+16;
740		  opaque_kv = gc_malloc(len, false, &gc);
741		  openvpn_snprintf (opaque_kv, len, ", opaque=\"%s\"", opaque);
742		}
743
744	      DigestCalcHA1(algor,
745			    username,
746			    realm,
747			    password,
748			    nonce,
749			    (char *)cnonce,
750			    session_key);
751	      DigestCalcResponse(session_key,
752				 nonce,
753				 nonce_count,
754				 (char *)cnonce,
755				 qop,
756				 http_method,
757				 uri,
758				 NULL,
759				 response);
760
761	      /* format HTTP CONNECT message */
762	      openvpn_snprintf (buf, sizeof(buf), "%s %s HTTP/%s",
763				http_method,
764				uri,
765				p->options.http_version);
766
767	      msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
768
769	      /* send HTTP CONNECT message to proxy */
770	      if (!send_line_crlf (sd, buf))
771		goto error;
772
773	      /* send HOST etc, */
774	      openvpn_snprintf (buf, sizeof(buf), "Host: %s", host);
775	      msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
776	      if (!send_line_crlf (sd, buf))
777		goto error;
778
779	      /* send digest response */
780	      openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", qop=%s, nc=%s, cnonce=\"%s\", response=\"%s\"%s",
781				username,
782				realm,
783				nonce,
784				uri,
785				qop,
786				nonce_count,
787				cnonce,
788				response,
789				opaque_kv
790				);
791	      msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
792	      if (!send_line_crlf (sd, buf))
793		goto error;
794	      if (!send_crlf (sd))
795		goto error;
796
797	      /* receive reply from proxy */
798	      if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received))
799		goto error;
800
801	      /* remove trailing CR, LF */
802	      chomp (buf);
803
804	      msg (D_PROXY, "HTTP proxy returned: '%s'", buf);
805
806	      /* parse return string */
807	      nparms = sscanf (buf, "%*s %d", &status);
808	      processed = true;
809	    }
810	  else
811	    {
812	      msg (D_PROXY, "HTTP proxy: digest method not supported");
813	      goto error;
814	    }
815	}
816#endif
817      else if (p->options.auth_retry)
818	{
819	  /* figure out what kind of authentication the proxy needs */
820	  char *pa = NULL;
821	  const int method = get_proxy_authenticate(sd,
822						    p->options.timeout,
823						    &pa,
824						    NULL,
825						    signal_received);
826	  if (method != HTTP_AUTH_NONE)
827	    {
828	      if (pa)
829		msg (D_PROXY, "HTTP proxy authenticate '%s'", pa);
830	      if (p->options.auth_retry == PAR_NCT && method == HTTP_AUTH_BASIC)
831		{
832		  msg (D_PROXY, "HTTP proxy: support for basic auth and other cleartext proxy auth methods is disabled");
833		  goto error;
834		}
835	      p->auth_method = method;
836	      store_proxy_authenticate(p, pa);
837	      ret = true;
838	      goto done;
839	    }
840	  else
841	    {
842	      msg (D_PROXY, "HTTP proxy: do not recognize the authentication method required by proxy");
843	      free (pa);
844	      goto error;
845	    }
846	}
847      else
848	{
849	  if (!processed)
850	    msg (D_PROXY, "HTTP proxy: no support for proxy authentication method");
851	  goto error;
852	}
853
854      /* clear state */
855      if (p->options.auth_retry)
856	clear_user_pass_http();
857      store_proxy_authenticate(p, NULL);
858    }
859
860  /* check return code, success = 200 */
861  if (nparms < 1 || status != 200)
862    {
863      msg (D_LINK_ERRORS, "HTTP proxy returned bad status");
864#if 0
865      /* DEBUGGING -- show a multi-line HTTP error response */
866      dump_residual(sd, p->options.timeout, signal_received);
867#endif
868      goto error;
869    }
870
871  /* SUCCESS */
872
873  /* receive line from proxy and discard */
874  if (!recv_line (sd, NULL, 0, p->options.timeout, true, NULL, signal_received))
875    goto error;
876
877  /*
878   * Toss out any extraneous chars, but don't throw away the
879   * start of the OpenVPN data stream (put it in lookahead).
880   */
881  while (recv_line (sd, NULL, 0, 2, false, lookahead, signal_received))
882    ;
883
884  /* reset queried_creds so that we don't think that the next creds request is due to an auth error */
885  p->queried_creds = false;
886
887#if 0
888  if (lookahead && BLEN (lookahead))
889    msg (M_INFO, "HTTP PROXY: lookahead: %s", format_hex (BPTR (lookahead), BLEN (lookahead), 0));
890#endif
891
892 done:
893  gc_free (&gc);
894  return ret;
895
896 error:
897  /* on error, should we exit or restart? */
898  if (!*signal_received)
899    *signal_received = (p->options.retry ? SIGUSR1 : SIGTERM); /* SOFT-SIGUSR1 -- HTTP proxy error */
900  gc_free (&gc);
901  return ret;
902}
903
904#else
905static void dummy(void) {}
906#endif /* ENABLE_HTTP_PROXY */
907
908