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
8 * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
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.
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.
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
27 #elif defined(_MSC_VER)
28 #include "config-msvc.h"
41 #include "httpdigest.h"
45 #ifdef ENABLE_HTTP_PROXY
47 #define UP_TYPE_PROXY "HTTP Proxy"
49 struct http_proxy_options
*
50 init_http_proxy_options_once (struct http_proxy_options
**hpo
,
55 ALLOC_OBJ_CLEAR_GC (*hpo
, struct http_proxy_options
, gc
);
56 /* http proxy defaults */
58 (*hpo
)->http_version
= "1.0";
64 /* cached proxy username/password */
65 static struct user_pass static_proxy_user_pass
;
68 recv_line (socket_descriptor_t sd
,
71 const int timeout_sec
,
73 struct buffer
*lookahead
,
74 volatile int *signal_received
)
91 if (buf_defined (&la
))
93 ASSERT (buf_init (&la
, 0));
98 tv
.tv_sec
= timeout_sec
;
101 status
= select (sd
+ 1, &reads
, NULL
, NULL
, &tv
);
103 get_signal (signal_received
);
104 if (*signal_received
)
111 msg (D_LINK_ERRORS
| M_ERRNO
, "recv_line: TCP port read timeout expired");
119 msg (D_LINK_ERRORS
| M_ERRNO
, "recv_line: TCP port read failed on select()");
123 /* read single char */
124 size
= recv (sd
, &c
, 1, MSG_NOSIGNAL
);
130 msg (D_LINK_ERRORS
| M_ERRNO
, "recv_line: TCP port read failed on recv()");
136 msg (M_INFO
, "PROXY: read '%c' (%d)", c
, (int)c
);
138 msg (M_INFO
, "PROXY: read (%d)", (int)c
);
141 /* store char in buffer */
148 /* also store char in lookahead buffer */
149 if (buf_defined (&la
))
151 buf_write_u8 (&la
, c
);
152 if (!isprint(c
) && !isspace(c
)) /* not ascii? */
155 msg (D_LINK_ERRORS
| M_ERRNO
, "recv_line: Non-ASCII character (%d) read on recv()", (int)c
);
162 if (lastc
== '\r' && c
== '\n')
168 /* append trailing null */
179 send_line (socket_descriptor_t sd
,
182 const ssize_t size
= send (sd
, buf
, strlen (buf
), MSG_NOSIGNAL
);
183 if (size
!= (ssize_t
) strlen (buf
))
185 msg (D_LINK_ERRORS
| M_ERRNO
, "send_line: TCP port write failed on send()");
192 send_line_crlf (socket_descriptor_t sd
,
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
));
206 send_crlf (socket_descriptor_t sd
)
208 return send_line_crlf (sd
, "");
212 make_base64_string2 (const uint8_t *str
, int src_len
, struct gc_arena
*gc
)
216 ASSERT (openvpn_base64_encode ((const void *)str
, src_len
, &b64out
) >= 0);
217 ret
= (uint8_t *) string_alloc (b64out
, gc
);
223 make_base64_string (const uint8_t *str
, struct gc_arena
*gc
)
225 return make_base64_string2 (str
, strlen ((const char *)str
), gc
);
229 username_password_as_base64 (const struct http_proxy_info
*p
,
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
);
239 get_user_pass_http (struct http_proxy_info
*p
, const bool force
)
241 if (!static_proxy_user_pass
.defined
|| force
)
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
,
250 p
->queried_creds
= true;
251 p
->up
= static_proxy_user_pass
;
255 clear_user_pass_http (void)
257 purge_user_pass (&static_proxy_user_pass
, true);
261 dump_residual (socket_descriptor_t sd
,
263 volatile int *signal_received
)
268 if (!recv_line (sd
, buf
, sizeof (buf
), timeout
, true, NULL
, signal_received
))
271 msg (D_PROXY
, "PROXY HEADER: '%s'", buf
);
276 * Extract the Proxy-Authenticate header from the stream.
277 * Consumes all headers.
280 get_proxy_authenticate (socket_descriptor_t sd
,
284 volatile int *signal_received
)
287 int ret
= HTTP_AUTH_NONE
;
290 if (!recv_line (sd
, buf
, sizeof (buf
), timeout
, true, NULL
, signal_received
))
293 return HTTP_AUTH_NONE
;
298 if (ret
== HTTP_AUTH_NONE
&& !strncmp(buf
, "Proxy-Authenticate: ", 20))
300 if (!strncmp(buf
+20, "Basic ", 6))
302 msg (D_PROXY
, "PROXY AUTH BASIC: '%s'", buf
);
303 *data
= string_alloc(buf
+26, gc
);
304 ret
= HTTP_AUTH_BASIC
;
306 #if PROXY_DIGEST_AUTH
307 else if (!strncmp(buf
+20, "Digest ", 7))
309 msg (D_PROXY
, "PROXY AUTH DIGEST: '%s'", buf
);
310 *data
= string_alloc(buf
+27, gc
);
311 ret
= HTTP_AUTH_DIGEST
;
315 else if (!strncmp(buf
+20, "NTLM", 4))
317 msg (D_PROXY
, "PROXY AUTH HTLM: '%s'", buf
);
319 ret
= HTTP_AUTH_NTLM
;
327 store_proxy_authenticate (struct http_proxy_info
*p
, char *data
)
329 if (p
->proxy_authenticate
)
330 free (p
->proxy_authenticate
);
331 p
->proxy_authenticate
= data
;
335 * Parse out key/value pairs from Proxy-Authenticate string.
336 * Return true on success, or false on parse failure.
339 get_key_value(const char *str
, /* source string */
340 char *key
, /* key stored here */
341 char *value
, /* value stored here */
344 const char **endptr
) /* next search position */
347 bool starts_with_quote
= false;
350 for (c
= max_key_len
-1; (*str
&& (*str
!= '=') && c
--); )
355 /* no key/value found */
362 starts_with_quote
= true;
365 for (c
= max_value_len
-1; *str
&& c
--; str
++)
372 /* possibly the start of an escaped quote */
374 *value
++ = '\\'; /* even though this is an escape character, we still
375 store it as-is in the target buffer */
380 if (!starts_with_quote
)
382 /* this signals the end of the value if we didn't get a starting quote
383 and then we do "sloppy" parsing */
394 if (!escape
&& starts_with_quote
)
409 return true; /* success */
413 get_pa_var (const char *key
, const char *pa
, struct gc_arena
*gc
)
417 const char *content
= pa
;
421 const int status
= get_key_value(content
, k
, v
, sizeof(k
), sizeof(v
), &content
);
425 return string_alloc(v
, gc
);
430 /* advance to start of next key */
433 while (*content
&& isspace(*content
))
438 struct http_proxy_info
*
439 http_proxy_new (const struct http_proxy_options
*o
)
441 struct http_proxy_info
*p
;
442 struct http_proxy_options opt
;
444 if (!o
|| !o
->server
)
445 msg (M_FATAL
, "HTTP_PROXY: server not specified");
447 ASSERT (legal_ipv4_port (o
->port
));
449 ALLOC_OBJ_CLEAR (p
, struct http_proxy_info
);
452 /* parse authentication method */
453 p
->auth_method
= HTTP_AUTH_NONE
;
454 if (o
->auth_method_string
)
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
;
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
;
467 msg (M_FATAL
, "ERROR: unknown HTTP authentication method: '%s'",
468 o
->auth_method_string
);
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
)
474 get_user_pass_http (p
, true);
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.");
487 http_proxy_close (struct http_proxy_info
*hp
)
493 establish_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
)
500 struct gc_arena gc
= gc_new ();
507 bool processed
= false;
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);
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
)
523 /* format HTTP CONNECT message */
524 openvpn_snprintf (buf
, sizeof(buf
), "CONNECT %s:%d HTTP/%s",
527 p
->options
.http_version
);
529 msg (D_PROXY
, "Send to HTTP proxy: '%s'", buf
);
531 /* send HTTP CONNECT message to proxy */
532 if (!send_line_crlf (sd
, buf
))
535 openvpn_snprintf(buf
, sizeof(buf
), "Host: %s", host
);
536 if (!send_line_crlf(sd
, buf
))
539 /* send User-Agent string if provided */
540 if (p
->options
.user_agent
)
542 openvpn_snprintf (buf
, sizeof(buf
), "User-Agent: %s",
543 p
->options
.user_agent
);
544 if (!send_line_crlf (sd
, buf
))
548 /* auth specified? */
549 switch (p
->auth_method
)
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
))
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
))
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
))
584 /* send empty CR, LF */
588 /* receive reply from proxy */
589 if (!recv_line (sd
, buf
, sizeof(buf
), p
->options
.timeout
, true, NULL
, signal_received
))
592 /* remove trailing CR, LF */
595 msg (D_PROXY
, "HTTP proxy returned: '%s'", buf
);
597 /* parse return string */
598 nparms
= sscanf (buf
, "%*s %d", &status
);
602 /* check for a "407 Proxy Authentication Required" response */
603 while (nparms
>= 1 && status
== 407)
605 msg (D_PROXY
, "Proxy requires authentication");
607 if (p
->auth_method
== HTTP_AUTH_BASIC
&& !processed
)
611 else if ((p
->auth_method
== HTTP_AUTH_NTLM
|| p
->auth_method
== HTTP_AUTH_NTLM2
) && !processed
) /* check for NTLM */
614 /* look for the phase 2 response */
618 if (!recv_line (sd
, buf
, sizeof(buf
), p
->options
.timeout
, true, NULL
, signal_received
))
621 msg (D_PROXY
, "HTTP proxy returned: '%s'", buf
);
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. */
627 /* check for "Proxy-Authenticate: NTLM TlRM..." */
631 msg (D_PROXY
, "auth string: '%s'", buf2
);
635 /* if we are here then auth string was got */
636 msg (D_PROXY
, "Received NTLM Proxy-Authorization phase 2 response");
638 /* receive and discard everything else */
639 while (recv_line (sd
, NULL
, 0, 2, true, NULL
, signal_received
))
642 /* now send the phase 3 reply */
644 /* format HTTP CONNECT message */
645 openvpn_snprintf (buf
, sizeof(buf
), "CONNECT %s:%d HTTP/%s",
648 p
->options
.http_version
);
650 msg (D_PROXY
, "Send to HTTP proxy: '%s'", buf
);
652 /* send HTTP CONNECT message to proxy */
653 if (!send_line_crlf (sd
, buf
))
656 /* keep-alive connection */
657 openvpn_snprintf (buf
, sizeof(buf
), "Proxy-Connection: Keep-Alive");
658 if (!send_line_crlf (sd
, buf
))
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
))
668 msg (D_PROXY
, "Attempting NTLM Proxy-Authorization phase 3");
670 const char *np3
= ntlm_phase_3 (p
, buf2
, &gc
);
673 msg (D_PROXY
, "NTLM Proxy-Authorization phase 3 failed: received corrupted data from proxy server");
676 openvpn_snprintf (buf
, sizeof(buf
), "Proxy-Authorization: NTLM %s", np3
);
679 msg (D_PROXY
, "Send to HTTP proxy: '%s'", buf
);
680 if (!send_line_crlf (sd
, buf
))
683 /* send empty CR, LF */
687 /* receive reply from proxy */
688 if (!recv_line (sd
, buf
, sizeof(buf
), p
->options
.timeout
, true, NULL
, signal_received
))
691 /* remove trailing CR, LF */
694 msg (D_PROXY
, "HTTP proxy returned: '%s'", buf
);
696 /* parse return string */
697 nparms
= sscanf (buf
, "%*s %d", &status
);
701 #if PROXY_DIGEST_AUTH
702 else if (p
->auth_method
== HTTP_AUTH_DIGEST
&& !processed
)
704 char *pa
= p
->proxy_authenticate
;
705 const int method
= p
->auth_method
;
708 if (method
== HTTP_AUTH_DIGEST
)
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
= "";
717 uint8_t cnonce_raw
[8];
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
);
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
);
732 /* build the digest response */
733 openvpn_snprintf (uri
, sizeof(uri
), "%s:%d",
739 const int len
= strlen(opaque
)+16;
740 opaque_kv
= gc_malloc(len
, false, &gc
);
741 openvpn_snprintf (opaque_kv
, len
, ", opaque=\"%s\"", opaque
);
751 DigestCalcResponse(session_key
,
761 /* format HTTP CONNECT message */
762 openvpn_snprintf (buf
, sizeof(buf
), "%s %s HTTP/%s",
765 p
->options
.http_version
);
767 msg (D_PROXY
, "Send to HTTP proxy: '%s'", buf
);
769 /* send HTTP CONNECT message to proxy */
770 if (!send_line_crlf (sd
, buf
))
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
))
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",
791 msg (D_PROXY
, "Send to HTTP proxy: '%s'", buf
);
792 if (!send_line_crlf (sd
, buf
))
797 /* receive reply from proxy */
798 if (!recv_line (sd
, buf
, sizeof(buf
), p
->options
.timeout
, true, NULL
, signal_received
))
801 /* remove trailing CR, LF */
804 msg (D_PROXY
, "HTTP proxy returned: '%s'", buf
);
806 /* parse return string */
807 nparms
= sscanf (buf
, "%*s %d", &status
);
812 msg (D_PROXY
, "HTTP proxy: digest method not supported");
817 else if (p
->options
.auth_retry
)
819 /* figure out what kind of authentication the proxy needs */
821 const int method
= get_proxy_authenticate(sd
,
826 if (method
!= HTTP_AUTH_NONE
)
829 msg (D_PROXY
, "HTTP proxy authenticate '%s'", pa
);
830 if (p
->options
.auth_retry
== PAR_NCT
&& method
== HTTP_AUTH_BASIC
)
832 msg (D_PROXY
, "HTTP proxy: support for basic auth and other cleartext proxy auth methods is disabled");
835 p
->auth_method
= method
;
836 store_proxy_authenticate(p
, pa
);
842 msg (D_PROXY
, "HTTP proxy: do not recognize the authentication method required by proxy");
850 msg (D_PROXY
, "HTTP proxy: no support for proxy authentication method");
855 if (p
->options
.auth_retry
)
856 clear_user_pass_http();
857 store_proxy_authenticate(p
, NULL
);
860 /* check return code, success = 200 */
861 if (nparms
< 1 || status
!= 200)
863 msg (D_LINK_ERRORS
, "HTTP proxy returned bad status");
865 /* DEBUGGING -- show a multi-line HTTP error response */
866 dump_residual(sd
, p
->options
.timeout
, signal_received
);
873 /* receive line from proxy and discard */
874 if (!recv_line (sd
, NULL
, 0, p
->options
.timeout
, true, NULL
, signal_received
))
878 * Toss out any extraneous chars, but don't throw away the
879 * start of the OpenVPN data stream (put it in lookahead).
881 while (recv_line (sd
, NULL
, 0, 2, false, lookahead
, signal_received
))
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;
888 if (lookahead
&& BLEN (lookahead
))
889 msg (M_INFO
, "HTTP PROXY: lookahead: %s", format_hex (BPTR (lookahead
), BLEN (lookahead
), 0));
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 */
905 static void dummy(void) {}
906 #endif /* ENABLE_HTTP_PROXY */