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-2005 OpenVPN Solutions LLC <info@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
26 #include "config-win32.h"
31 #ifdef ENABLE_HTTP_PROXY
46 /* cached proxy username/password */
47 static struct user_pass static_proxy_user_pass
;
50 recv_line (socket_descriptor_t sd
,
53 const int timeout_sec
,
55 struct buffer
*lookahead
,
56 volatile int *signal_received
)
73 if (buf_defined (&la
))
75 ASSERT (buf_init (&la
, 0));
80 tv
.tv_sec
= timeout_sec
;
83 status
= select (sd
+ 1, &reads
, NULL
, NULL
, &tv
);
85 get_signal (signal_received
);
93 msg (D_LINK_ERRORS
| M_ERRNO_SOCK
, "recv_line: TCP port read timeout expired");
101 msg (D_LINK_ERRORS
| M_ERRNO_SOCK
, "recv_line: TCP port read failed on select()");
105 /* read single char */
106 size
= recv (sd
, &c
, 1, MSG_NOSIGNAL
);
112 msg (D_LINK_ERRORS
| M_ERRNO_SOCK
, "recv_line: TCP port read failed on recv()");
118 msg (M_INFO
, "PROXY: read '%c' (%d)", c
, (int)c
);
120 msg (M_INFO
, "PROXY: read (%d)", (int)c
);
123 /* store char in buffer */
130 /* also store char in lookahead buffer */
131 if (buf_defined (&la
))
133 buf_write_u8 (&la
, c
);
134 if (!isprint(c
) && !isspace(c
)) /* not ascii? */
137 msg (D_LINK_ERRORS
| M_ERRNO_SOCK
, "recv_line: Non-ASCII character (%d) read on recv()", (int)c
);
144 if (lastc
== '\r' && c
== '\n')
150 /* append trailing null */
161 send_line (socket_descriptor_t sd
,
164 const ssize_t size
= send (sd
, buf
, strlen (buf
), MSG_NOSIGNAL
);
165 if (size
!= (ssize_t
) strlen (buf
))
167 msg (D_LINK_ERRORS
| M_ERRNO_SOCK
, "send_line: TCP port write failed on send()");
174 send_line_crlf (socket_descriptor_t sd
,
179 struct buffer buf
= alloc_buf (strlen (src
) + 3);
180 ASSERT (buf_write (&buf
, src
, strlen (src
)));
181 ASSERT (buf_write (&buf
, "\r\n", 3));
182 ret
= send_line (sd
, BSTR (&buf
));
188 send_crlf (socket_descriptor_t sd
)
190 return send_line_crlf (sd
, "");
194 make_base64_string2 (const uint8_t *str
, int src_len
, struct gc_arena
*gc
)
198 ASSERT (base64_encode ((const void *)str
, src_len
, &b64out
) >= 0);
199 ret
= (uint8_t *) string_alloc (b64out
, gc
);
205 make_base64_string (const uint8_t *str
, struct gc_arena
*gc
)
207 return make_base64_string2 (str
, strlen ((const char *)str
), gc
);
211 username_password_as_base64 (const struct http_proxy_info
*p
,
214 struct buffer out
= alloc_buf_gc (strlen (p
->up
.username
) + strlen (p
->up
.password
) + 2, gc
);
215 ASSERT (strlen (p
->up
.username
) > 0);
216 buf_printf (&out
, "%s:%s", p
->up
.username
, p
->up
.password
);
217 return (const char *)make_base64_string ((const uint8_t*)BSTR (&out
), gc
);
220 struct http_proxy_info
*
221 new_http_proxy (const struct http_proxy_options
*o
,
224 struct http_proxy_info
*p
;
225 ALLOC_OBJ_CLEAR_GC (p
, struct http_proxy_info
, gc
);
228 msg (M_FATAL
, "HTTP_PROXY: server not specified");
230 ASSERT (legal_ipv4_port (o
->port
));
234 /* parse authentication method */
235 p
->auth_method
= HTTP_AUTH_NONE
;
236 if (o
->auth_method_string
)
238 if (!strcmp (o
->auth_method_string
, "none"))
239 p
->auth_method
= HTTP_AUTH_NONE
;
240 else if (!strcmp (o
->auth_method_string
, "basic"))
241 p
->auth_method
= HTTP_AUTH_BASIC
;
242 else if (!strcmp (o
->auth_method_string
, "ntlm"))
243 p
->auth_method
= HTTP_AUTH_NTLM
;
245 msg (M_FATAL
, "ERROR: unknown HTTP authentication method: '%s' -- only the 'none', 'basic', or 'ntlm' methods are currently supported",
246 o
->auth_method_string
);
249 /* only basic and NTLM authentication supported so far */
250 if (p
->auth_method
== HTTP_AUTH_BASIC
|| p
->auth_method
== HTTP_AUTH_NTLM
)
252 get_user_pass (&static_proxy_user_pass
,
256 GET_USER_PASS_MANAGEMENT
);
257 p
->up
= static_proxy_user_pass
;
261 if (p
->auth_method
== HTTP_AUTH_NTLM
)
262 msg (M_FATAL
, "Sorry, this version of " PACKAGE_NAME
" was built without NTLM Proxy support.");
270 establish_http_proxy_passthru (struct http_proxy_info
*p
,
271 socket_descriptor_t sd
, /* already open to proxy */
272 const char *host
, /* openvpn server remote */
273 const int port
, /* openvpn server port */
274 struct buffer
*lookahead
,
275 volatile int *signal_received
)
277 struct gc_arena gc
= gc_new ();
284 /* format HTTP CONNECT message */
285 openvpn_snprintf (buf
, sizeof(buf
), "CONNECT %s:%d HTTP/%s",
288 p
->options
.http_version
);
290 msg (D_PROXY
, "Send to HTTP proxy: '%s'", buf
);
292 /* send HTTP CONNECT message to proxy */
293 if (!send_line_crlf (sd
, buf
))
296 /* send User-Agent string if provided */
297 if (p
->options
.user_agent
)
299 openvpn_snprintf (buf
, sizeof(buf
), "User-Agent: %s",
300 p
->options
.user_agent
);
301 if (!send_line_crlf (sd
, buf
))
305 /* auth specified? */
306 switch (p
->auth_method
)
311 case HTTP_AUTH_BASIC
:
312 openvpn_snprintf (buf
, sizeof(buf
), "Proxy-Authorization: Basic %s",
313 username_password_as_base64 (p
, &gc
));
314 msg (D_PROXY
, "Attempting Basic Proxy-Authorization");
315 dmsg (D_SHOW_KEYS
, "Send to HTTP proxy: '%s'", buf
);
317 if (!send_line_crlf (sd
, buf
))
323 openvpn_snprintf (buf
, sizeof(buf
), "Proxy-Authorization: NTLM %s",
324 ntlm_phase_1 (p
, &gc
));
325 msg (D_PROXY
, "Attempting NTLM Proxy-Authorization phase 1");
326 dmsg (D_SHOW_KEYS
, "Send to HTTP proxy: '%s'", buf
);
328 if (!send_line_crlf (sd
, buf
))
337 /* send empty CR, LF */
342 /* receive reply from proxy */
343 if (!recv_line (sd
, buf
, sizeof(buf
), p
->options
.timeout
, true, NULL
, signal_received
))
346 /* remove trailing CR, LF */
349 msg (D_PROXY
, "HTTP proxy returned: '%s'", buf
);
351 /* parse return string */
352 nparms
= sscanf (buf
, "%*s %d", &status
);
354 /* check for a "407 Proxy Authentication Required" response */
355 if (nparms
>= 1 && status
== 407)
357 msg (D_PROXY
, "Proxy requires authentication");
360 if (p
->auth_method
== HTTP_AUTH_NTLM
)
363 /* look for the phase 2 response */
367 if (!recv_line (sd
, buf
, sizeof(buf
), p
->options
.timeout
, true, NULL
, signal_received
))
370 msg (D_PROXY
, "HTTP proxy returned: '%s'", buf
);
372 openvpn_snprintf (get
, sizeof get
, "%%*s NTLM %%%ds", (int) sizeof (buf2
) - 1);
373 nparms
= sscanf (buf
, get
, buf2
);
374 buf2
[127] = 0; /* we only need the beginning - ensure it's null terminated. */
376 /* check for "Proxy-Authenticate: NTLM TlRM..." */
380 msg (D_PROXY
, "auth string: '%s'", buf2
);
384 /* if we are here then auth string was got */
385 msg (D_PROXY
, "Received NTLM Proxy-Authorization phase 2 response");
387 /* receive and discard everything else */
388 while (recv_line (sd
, NULL
, 0, p
->options
.timeout
, true, NULL
, signal_received
))
391 /* now send the phase 3 reply */
393 /* format HTTP CONNECT message */
394 openvpn_snprintf (buf
, sizeof(buf
), "CONNECT %s:%d HTTP/%s",
397 p
->options
.http_version
);
399 msg (D_PROXY
, "Send to HTTP proxy: '%s'", buf
);
401 /* send HTTP CONNECT message to proxy */
402 if (!send_line_crlf (sd
, buf
))
407 openvpn_snprintf (buf
, sizeof(buf
), "Host: %s", host
);
408 msg (D_PROXY
, "Send to HTTP proxy: '%s'", buf
);
409 if (!send_line_crlf (sd
, buf
))
412 openvpn_snprintf (buf
, sizeof(buf
), "Proxy-Authorization: NTLM %s",
413 ntlm_phase_3 (p
, buf2
, &gc
));
414 msg (D_PROXY
, "Attempting NTLM Proxy-Authorization phase 3");
415 msg (D_PROXY
, "Send to HTTP proxy: '%s'", buf
);
417 if (!send_line_crlf (sd
, buf
))
420 /* send empty CR, LF */
425 /* receive reply from proxy */
426 if (!recv_line (sd
, buf
, sizeof(buf
), p
->options
.timeout
, true, NULL
, signal_received
))
429 /* remove trailing CR, LF */
432 msg (D_PROXY
, "HTTP proxy returned: '%s'", buf
);
434 /* parse return string */
435 nparms
= sscanf (buf
, "%*s %d", &status
);
437 ASSERT (0); /* No NTLM support */
444 /* check return code, success = 200 */
445 if (nparms
< 1 || status
!= 200)
447 msg (D_LINK_ERRORS
, "HTTP proxy returned bad status");
449 /* DEBUGGING -- show a multi-line HTTP error response */
452 if (!recv_line (sd
, buf
, sizeof (buf
), p
->options
.timeout
, true, NULL
, signal_received
))
455 msg (D_PROXY
, "HTTP proxy returned: '%s'", buf
);
461 /* receive line from proxy and discard */
462 if (!recv_line (sd
, NULL
, 0, p
->options
.timeout
, true, NULL
, signal_received
))
466 * Toss out any extraneous chars, but don't throw away the
467 * start of the OpenVPN data stream (put it in lookahead).
469 while (recv_line (sd
, NULL
, 0, 2, false, lookahead
, signal_received
))
473 if (lookahead
&& BLEN (lookahead
))
474 msg (M_INFO
, "HTTP PROXY: lookahead: %s", format_hex (BPTR (lookahead
), BLEN (lookahead
), 0));
481 /* on error, should we exit or restart? */
482 if (!*signal_received
)
483 *signal_received
= (p
->options
.retry
? SIGUSR1
: SIGTERM
); /* SOFT-SIGUSR1 -- HTTP proxy error */
489 static void dummy(void) {}
490 #endif /* ENABLE_HTTP_PROXY */