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-2009 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
42 #ifdef ENABLE_HTTP_PROXY
44 /* cached proxy username/password */
45 static struct user_pass static_proxy_user_pass
;
48 recv_line (socket_descriptor_t sd
,
51 const int timeout_sec
,
53 struct buffer
*lookahead
,
54 volatile int *signal_received
)
71 if (buf_defined (&la
))
73 ASSERT (buf_init (&la
, 0));
78 tv
.tv_sec
= timeout_sec
;
81 status
= select (sd
+ 1, &reads
, NULL
, NULL
, &tv
);
83 get_signal (signal_received
);
91 msg (D_LINK_ERRORS
| M_ERRNO_SOCK
, "recv_line: TCP port read timeout expired");
99 msg (D_LINK_ERRORS
| M_ERRNO_SOCK
, "recv_line: TCP port read failed on select()");
103 /* read single char */
104 size
= recv (sd
, &c
, 1, MSG_NOSIGNAL
);
110 msg (D_LINK_ERRORS
| M_ERRNO_SOCK
, "recv_line: TCP port read failed on recv()");
116 msg (M_INFO
, "PROXY: read '%c' (%d)", c
, (int)c
);
118 msg (M_INFO
, "PROXY: read (%d)", (int)c
);
121 /* store char in buffer */
128 /* also store char in lookahead buffer */
129 if (buf_defined (&la
))
131 buf_write_u8 (&la
, c
);
132 if (!isprint(c
) && !isspace(c
)) /* not ascii? */
135 msg (D_LINK_ERRORS
| M_ERRNO_SOCK
, "recv_line: Non-ASCII character (%d) read on recv()", (int)c
);
142 if (lastc
== '\r' && c
== '\n')
148 /* append trailing null */
159 send_line (socket_descriptor_t sd
,
162 const ssize_t size
= send (sd
, buf
, strlen (buf
), MSG_NOSIGNAL
);
163 if (size
!= (ssize_t
) strlen (buf
))
165 msg (D_LINK_ERRORS
| M_ERRNO_SOCK
, "send_line: TCP port write failed on send()");
172 send_line_crlf (socket_descriptor_t sd
,
177 struct buffer buf
= alloc_buf (strlen (src
) + 3);
178 ASSERT (buf_write (&buf
, src
, strlen (src
)));
179 ASSERT (buf_write (&buf
, "\r\n", 3));
180 ret
= send_line (sd
, BSTR (&buf
));
186 send_crlf (socket_descriptor_t sd
)
188 return send_line_crlf (sd
, "");
192 make_base64_string2 (const uint8_t *str
, int src_len
, struct gc_arena
*gc
)
196 ASSERT (base64_encode ((const void *)str
, src_len
, &b64out
) >= 0);
197 ret
= (uint8_t *) string_alloc (b64out
, gc
);
203 make_base64_string (const uint8_t *str
, struct gc_arena
*gc
)
205 return make_base64_string2 (str
, strlen ((const char *)str
), gc
);
209 username_password_as_base64 (const struct http_proxy_info
*p
,
212 struct buffer out
= alloc_buf_gc (strlen (p
->up
.username
) + strlen (p
->up
.password
) + 2, gc
);
213 ASSERT (strlen (p
->up
.username
) > 0);
214 buf_printf (&out
, "%s:%s", p
->up
.username
, p
->up
.password
);
215 return (const char *)make_base64_string ((const uint8_t*)BSTR (&out
), gc
);
219 get_user_pass_http (struct http_proxy_info
*p
, const bool force
)
221 if (!static_proxy_user_pass
.defined
|| force
)
223 get_user_pass (&static_proxy_user_pass
,
224 p
->options
.auth_file
,
226 GET_USER_PASS_MANAGEMENT
);
227 p
->up
= static_proxy_user_pass
;
231 struct http_proxy_info
*
232 http_proxy_new (const struct http_proxy_options
*o
,
233 struct auto_proxy_info
*auto_proxy_info
)
235 struct http_proxy_info
*p
;
236 struct http_proxy_options opt
;
242 /* if --http-proxy explicitly given, disable auto-proxy */
243 auto_proxy_info
= NULL
;
247 /* if no --http-proxy explicitly given and no auto settings, fail */
248 if (!auto_proxy_info
->http
.server
)
259 /* These settings are only used for --auto-proxy */
261 opt
.http_version
= "1.0";
264 opt
.server
= auto_proxy_info
->http
.server
;
265 opt
.port
= auto_proxy_info
->http
.port
;
266 opt
.auth_retry
= true;
272 if (!o
|| !o
->server
)
273 msg (M_FATAL
, "HTTP_PROXY: server not specified");
275 ASSERT (legal_ipv4_port (o
->port
));
277 ALLOC_OBJ_CLEAR (p
, struct http_proxy_info
);
280 /* parse authentication method */
281 p
->auth_method
= HTTP_AUTH_NONE
;
282 if (o
->auth_method_string
)
284 if (!strcmp (o
->auth_method_string
, "none"))
285 p
->auth_method
= HTTP_AUTH_NONE
;
286 else if (!strcmp (o
->auth_method_string
, "basic"))
287 p
->auth_method
= HTTP_AUTH_BASIC
;
288 else if (!strcmp (o
->auth_method_string
, "ntlm"))
289 p
->auth_method
= HTTP_AUTH_NTLM
;
290 else if (!strcmp (o
->auth_method_string
, "ntlm2"))
291 p
->auth_method
= HTTP_AUTH_NTLM2
;
293 msg (M_FATAL
, "ERROR: unknown HTTP authentication method: '%s' -- only the 'none', 'basic', 'ntlm', or 'ntlm2' methods are currently supported",
294 o
->auth_method_string
);
297 /* only basic and NTLM/NTLMv2 authentication supported so far */
298 if (p
->auth_method
== HTTP_AUTH_BASIC
|| p
->auth_method
== HTTP_AUTH_NTLM
|| p
->auth_method
== HTTP_AUTH_NTLM2
)
300 get_user_pass_http (p
, true);
304 if (p
->auth_method
== HTTP_AUTH_NTLM
|| p
->auth_method
== HTTP_AUTH_NTLM2
)
305 msg (M_FATAL
, "Sorry, this version of " PACKAGE_NAME
" was built without NTLM Proxy support.");
313 http_proxy_close (struct http_proxy_info
*hp
)
319 establish_http_proxy_passthru (struct http_proxy_info
*p
,
320 socket_descriptor_t sd
, /* already open to proxy */
321 const char *host
, /* openvpn server remote */
322 const int port
, /* openvpn server port */
323 struct buffer
*lookahead
,
324 volatile int *signal_received
)
326 struct gc_arena gc
= gc_new ();
334 /* get user/pass if not previously given or if --auto-proxy is being used */
335 if (p
->auth_method
== HTTP_AUTH_BASIC
336 || p
->auth_method
== HTTP_AUTH_NTLM
)
337 get_user_pass_http (p
, false);
339 /* format HTTP CONNECT message */
340 openvpn_snprintf (buf
, sizeof(buf
), "CONNECT %s:%d HTTP/%s",
343 p
->options
.http_version
);
345 msg (D_PROXY
, "Send to HTTP proxy: '%s'", buf
);
347 /* send HTTP CONNECT message to proxy */
348 if (!send_line_crlf (sd
, buf
))
351 /* send User-Agent string if provided */
352 if (p
->options
.user_agent
)
354 openvpn_snprintf (buf
, sizeof(buf
), "User-Agent: %s",
355 p
->options
.user_agent
);
356 if (!send_line_crlf (sd
, buf
))
360 /* auth specified? */
361 switch (p
->auth_method
)
366 case HTTP_AUTH_BASIC
:
367 openvpn_snprintf (buf
, sizeof(buf
), "Proxy-Authorization: Basic %s",
368 username_password_as_base64 (p
, &gc
));
369 msg (D_PROXY
, "Attempting Basic Proxy-Authorization");
370 dmsg (D_SHOW_KEYS
, "Send to HTTP proxy: '%s'", buf
);
372 if (!send_line_crlf (sd
, buf
))
378 case HTTP_AUTH_NTLM2
:
379 /* keep-alive connection */
380 openvpn_snprintf (buf
, sizeof(buf
), "Proxy-Connection: Keep-Alive");
381 if (!send_line_crlf (sd
, buf
))
384 openvpn_snprintf (buf
, sizeof(buf
), "Proxy-Authorization: NTLM %s",
385 ntlm_phase_1 (p
, &gc
));
386 msg (D_PROXY
, "Attempting NTLM Proxy-Authorization phase 1");
387 dmsg (D_SHOW_KEYS
, "Send to HTTP proxy: '%s'", buf
);
389 if (!send_line_crlf (sd
, buf
))
398 /* send empty CR, LF */
403 /* receive reply from proxy */
404 if (!recv_line (sd
, buf
, sizeof(buf
), p
->options
.timeout
, true, NULL
, signal_received
))
407 /* remove trailing CR, LF */
410 msg (D_PROXY
, "HTTP proxy returned: '%s'", buf
);
412 /* parse return string */
413 nparms
= sscanf (buf
, "%*s %d", &status
);
415 /* check for a "407 Proxy Authentication Required" response */
416 if (nparms
>= 1 && status
== 407)
418 msg (D_PROXY
, "Proxy requires authentication");
421 if (p
->auth_method
== HTTP_AUTH_NTLM
|| p
->auth_method
== HTTP_AUTH_NTLM2
)
424 /* look for the phase 2 response */
428 if (!recv_line (sd
, buf
, sizeof(buf
), p
->options
.timeout
, true, NULL
, signal_received
))
431 msg (D_PROXY
, "HTTP proxy returned: '%s'", buf
);
433 openvpn_snprintf (get
, sizeof get
, "%%*s NTLM %%%ds", (int) sizeof (buf2
) - 1);
434 nparms
= sscanf (buf
, get
, buf2
);
435 buf2
[127] = 0; /* we only need the beginning - ensure it's null terminated. */
437 /* check for "Proxy-Authenticate: NTLM TlRM..." */
441 msg (D_PROXY
, "auth string: '%s'", buf2
);
445 /* if we are here then auth string was got */
446 msg (D_PROXY
, "Received NTLM Proxy-Authorization phase 2 response");
448 /* receive and discard everything else */
449 while (recv_line (sd
, NULL
, 0, p
->options
.timeout
, true, NULL
, signal_received
))
452 /* now send the phase 3 reply */
454 /* format HTTP CONNECT message */
455 openvpn_snprintf (buf
, sizeof(buf
), "CONNECT %s:%d HTTP/%s",
458 p
->options
.http_version
);
460 msg (D_PROXY
, "Send to HTTP proxy: '%s'", buf
);
462 /* send HTTP CONNECT message to proxy */
463 if (!send_line_crlf (sd
, buf
))
466 /* keep-alive connection */
467 openvpn_snprintf (buf
, sizeof(buf
), "Proxy-Connection: Keep-Alive");
468 if (!send_line_crlf (sd
, buf
))
474 openvpn_snprintf (buf
, sizeof(buf
), "Host: %s", host
);
475 msg (D_PROXY
, "Send to HTTP proxy: '%s'", buf
);
476 if (!send_line_crlf (sd
, buf
))
479 msg (D_PROXY
, "Attempting NTLM Proxy-Authorization phase 3");
481 const char *np3
= ntlm_phase_3 (p
, buf2
, &gc
);
484 msg (D_PROXY
, "NTLM Proxy-Authorization phase 3 failed: received corrupted data from proxy server");
487 openvpn_snprintf (buf
, sizeof(buf
), "Proxy-Authorization: NTLM %s", np3
);
490 msg (D_PROXY
, "Send to HTTP proxy: '%s'", buf
);
492 if (!send_line_crlf (sd
, buf
))
495 /* send empty CR, LF */
500 /* receive reply from proxy */
501 if (!recv_line (sd
, buf
, sizeof(buf
), p
->options
.timeout
, true, NULL
, signal_received
))
504 /* remove trailing CR, LF */
507 msg (D_PROXY
, "HTTP proxy returned: '%s'", buf
);
509 /* parse return string */
510 nparms
= sscanf (buf
, "%*s %d", &status
);
512 ASSERT (0); /* No NTLM support */
515 else if (p
->auth_method
== HTTP_AUTH_NONE
&& p
->options
.auth_retry
)
518 * Proxy needs authentication, but we don't have a user/pass.
519 * Now we will change p->auth_method and return true so that
520 * our caller knows to call us again on a newly opened socket.
521 * JYFIXME: This code needs to check proxy error output and set
522 * JYFIXME: p->auth_method = HTTP_AUTH_NTLM if necessary.
524 p
->auth_method
= HTTP_AUTH_BASIC
;
533 /* check return code, success = 200 */
534 if (nparms
< 1 || status
!= 200)
536 msg (D_LINK_ERRORS
, "HTTP proxy returned bad status");
538 /* DEBUGGING -- show a multi-line HTTP error response */
541 if (!recv_line (sd
, buf
, sizeof (buf
), p
->options
.timeout
, true, NULL
, signal_received
))
544 msg (D_PROXY
, "HTTP proxy returned: '%s'", buf
);
550 /* receive line from proxy and discard */
551 if (!recv_line (sd
, NULL
, 0, p
->options
.timeout
, true, NULL
, signal_received
))
555 * Toss out any extraneous chars, but don't throw away the
556 * start of the OpenVPN data stream (put it in lookahead).
558 while (recv_line (sd
, NULL
, 0, 2, false, lookahead
, signal_received
))
562 if (lookahead
&& BLEN (lookahead
))
563 msg (M_INFO
, "HTTP PROXY: lookahead: %s", format_hex (BPTR (lookahead
), BLEN (lookahead
), 0));
571 /* on error, should we exit or restart? */
572 if (!*signal_received
)
573 *signal_received
= (p
->options
.retry
? SIGUSR1
: SIGTERM
); /* SOFT-SIGUSR1 -- HTTP proxy error */
579 static void dummy(void) {}
580 #endif /* ENABLE_HTTP_PROXY */
582 #ifdef GENERAL_PROXY_SUPPORT
588 get_windows_internet_string (const DWORD dwOption
, struct gc_arena
*gc
)
593 /* Initially, get size of return buffer */
594 InternetQueryOption (NULL
, dwOption
, NULL
, &size
);
597 /* Now get actual info */
598 ret
= (INTERNET_PROXY_INFO
*) gc_malloc (size
, false, gc
);
599 if (!InternetQueryOption (NULL
, dwOption
, (LPVOID
) ret
, &size
))
606 static INTERNET_PROXY_INFO
*
607 get_windows_proxy_settings (struct gc_arena
*gc
)
610 INTERNET_PROXY_INFO
*ret
= NULL
;
612 /* Initially, get size of return buffer */
613 InternetQueryOption (NULL
, INTERNET_OPTION_PROXY
, NULL
, &size
);
616 /* Now get actual info */
617 ret
= (INTERNET_PROXY_INFO
*) gc_malloc (size
, false, gc
);
618 if (!InternetQueryOption (NULL
, INTERNET_OPTION_PROXY
, (LPVOID
) ret
, &size
))
625 parse_windows_proxy_setting (const char *str
, struct auto_proxy_info_entry
*e
, struct gc_arena
*gc
)
628 const char *ret
= NULL
;
633 buf_set_read (&in
, (const uint8_t *)str
, strlen (str
));
635 if (strchr (str
, '=') != NULL
)
637 if (buf_parse (&in
, '=', buf
, sizeof (buf
)))
638 ret
= string_alloc (buf
, gc
);
641 if (buf_parse (&in
, ':', buf
, sizeof (buf
)))
642 e
->server
= string_alloc (buf
, gc
);
644 if (e
->server
&& buf_parse (&in
, '\0', buf
, sizeof (buf
)))
645 e
->port
= atoi (buf
);
651 parse_windows_proxy_setting_list (const char *str
, const char *type
, struct auto_proxy_info_entry
*e
, struct gc_arena
*gc
)
653 struct gc_arena gc_local
= gc_new ();
654 struct auto_proxy_info_entry el
;
662 buf_set_read (&in
, (const uint8_t *)str
, strlen (str
));
663 if (strchr (str
, '=') != NULL
)
665 while (buf_parse (&in
, ' ', buf
, sizeof (buf
)))
667 const char *t
= parse_windows_proxy_setting (buf
, &el
, &gc_local
);
668 if (t
&& !strcmp (t
, type
))
675 if (!parse_windows_proxy_setting (str
, &el
, &gc_local
))
681 if (el
.server
&& el
.port
> 0)
683 e
->server
= string_alloc (el
.server
, gc
);
692 win_proxy_access_type (const DWORD dwAccessType
)
694 switch (dwAccessType
)
696 case INTERNET_OPEN_TYPE_DIRECT
:
697 return "INTERNET_OPEN_TYPE_DIRECT";
698 case INTERNET_OPEN_TYPE_PROXY
:
699 return "INTERNET_OPEN_TYPE_PROXY";
706 show_win_proxy_settings (const int msglevel
)
708 INTERNET_PROXY_INFO
*info
;
709 struct gc_arena gc
= gc_new ();
711 info
= get_windows_proxy_settings (&gc
);
712 msg (msglevel
, "PROXY INFO: %s %s",
713 win_proxy_access_type (info
->dwAccessType
),
714 info
->lpszProxy
? info
->lpszProxy
: "[NULL]");
719 struct auto_proxy_info
*
720 get_proxy_settings (char **err
, struct gc_arena
*gc
)
722 struct gc_arena gc_local
= gc_new ();
723 INTERNET_PROXY_INFO
*info
;
724 struct auto_proxy_info
*pi
;
726 ALLOC_OBJ_CLEAR_GC (pi
, struct auto_proxy_info
, gc
);
731 info
= get_windows_proxy_settings (&gc_local
);
736 *err
= "PROXY: failed to obtain windows proxy info";
740 switch (info
->dwAccessType
)
742 case INTERNET_OPEN_TYPE_DIRECT
:
744 case INTERNET_OPEN_TYPE_PROXY
:
745 if (!info
->lpszProxy
)
747 parse_windows_proxy_setting_list (info
->lpszProxy
, NULL
, &pi
->http
, gc
);
748 if (!pi
->http
.server
)
749 parse_windows_proxy_setting_list (info
->lpszProxy
, "http", &pi
->http
, gc
);
750 parse_windows_proxy_setting_list (info
->lpszProxy
, "socks", &pi
->socks
, gc
);
754 *err
= "PROXY: unknown proxy type";
765 struct auto_proxy_info
*
766 get_proxy_settings (char **err
, struct gc_arena
*gc
)
770 *err
= string_alloc ("PROXY: automatic detection not supported on this OS", gc
);
772 #else /* test --auto-proxy feature */
773 struct auto_proxy_info
*pi
;
774 ALLOC_OBJ_CLEAR_GC (pi
, struct auto_proxy_info
, gc
);
775 pi
->http
.server
= "10.10.0.2";
776 pi
->http
.port
= 4000;
783 #endif /* GENERAL_PROXY_SUPPORT */