svn cleanup
[anytun.git] / openvpn / proxy.c
bloba32e8e15fd1f99b6268812acf4439b5390d829b2
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.
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
25 #ifdef WIN32
26 #include "config-win32.h"
27 #else
28 #include "config.h"
29 #endif
31 #ifdef ENABLE_HTTP_PROXY
33 #include "syshead.h"
35 #include "common.h"
36 #include "misc.h"
37 #include "win32.h"
38 #include "socket.h"
39 #include "fdmisc.h"
40 #include "proxy.h"
41 #include "base64.h"
42 #include "ntlm.h"
44 #include "memdbg.h"
46 /* cached proxy username/password */
47 static struct user_pass static_proxy_user_pass;
49 static bool
50 recv_line (socket_descriptor_t sd,
51 char *buf,
52 int len,
53 const int timeout_sec,
54 const bool verbose,
55 struct buffer *lookahead,
56 volatile int *signal_received)
58 struct buffer la;
59 int lastc = 0;
61 CLEAR (la);
62 if (lookahead)
63 la = *lookahead;
65 while (true)
67 int status;
68 ssize_t size;
69 fd_set reads;
70 struct timeval tv;
71 uint8_t c;
73 if (buf_defined (&la))
75 ASSERT (buf_init (&la, 0));
78 FD_ZERO (&reads);
79 FD_SET (sd, &reads);
80 tv.tv_sec = timeout_sec;
81 tv.tv_usec = 0;
83 status = select (sd + 1, &reads, NULL, NULL, &tv);
85 get_signal (signal_received);
86 if (*signal_received)
87 goto error;
89 /* timeout? */
90 if (status == 0)
92 if (verbose)
93 msg (D_LINK_ERRORS | M_ERRNO_SOCK, "recv_line: TCP port read timeout expired");
94 goto error;
97 /* error */
98 if (status < 0)
100 if (verbose)
101 msg (D_LINK_ERRORS | M_ERRNO_SOCK, "recv_line: TCP port read failed on select()");
102 goto error;
105 /* read single char */
106 size = recv (sd, &c, 1, MSG_NOSIGNAL);
108 /* error? */
109 if (size != 1)
111 if (verbose)
112 msg (D_LINK_ERRORS | M_ERRNO_SOCK, "recv_line: TCP port read failed on recv()");
113 goto error;
116 #if 0
117 if (isprint(c))
118 msg (M_INFO, "PROXY: read '%c' (%d)", c, (int)c);
119 else
120 msg (M_INFO, "PROXY: read (%d)", (int)c);
121 #endif
123 /* store char in buffer */
124 if (len > 1)
126 *buf++ = c;
127 --len;
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? */
136 if (verbose)
137 msg (D_LINK_ERRORS | M_ERRNO_SOCK, "recv_line: Non-ASCII character (%d) read on recv()", (int)c);
138 *lookahead = la;
139 return false;
143 /* end of line? */
144 if (lastc == '\r' && c == '\n')
145 break;
147 lastc = c;
150 /* append trailing null */
151 if (len > 0)
152 *buf++ = '\0';
154 return true;
156 error:
157 return false;
160 static bool
161 send_line (socket_descriptor_t sd,
162 const char *buf)
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()");
168 return false;
170 return true;
173 static bool
174 send_line_crlf (socket_descriptor_t sd,
175 const char *src)
177 bool ret;
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));
183 free_buf (&buf);
184 return ret;
187 static bool
188 send_crlf (socket_descriptor_t sd)
190 return send_line_crlf (sd, "");
193 uint8_t *
194 make_base64_string2 (const uint8_t *str, int src_len, struct gc_arena *gc)
196 uint8_t *ret = NULL;
197 char *b64out = NULL;
198 ASSERT (base64_encode ((const void *)str, src_len, &b64out) >= 0);
199 ret = (uint8_t *) string_alloc (b64out, gc);
200 free (b64out);
201 return ret;
204 uint8_t *
205 make_base64_string (const uint8_t *str, struct gc_arena *gc)
207 return make_base64_string2 (str, strlen ((const char *)str), gc);
210 static const char *
211 username_password_as_base64 (const struct http_proxy_info *p,
212 struct gc_arena *gc)
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,
222 struct gc_arena *gc)
224 struct http_proxy_info *p;
225 ALLOC_OBJ_CLEAR_GC (p, struct http_proxy_info, gc);
227 if (!o->server)
228 msg (M_FATAL, "HTTP_PROXY: server not specified");
230 ASSERT (legal_ipv4_port (o->port));
232 p->options = *o;
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;
244 else
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,
253 o->auth_file,
254 false,
255 "HTTP Proxy",
256 GET_USER_PASS_MANAGEMENT);
257 p->up = static_proxy_user_pass;
260 #if !NTLM
261 if (p->auth_method == HTTP_AUTH_NTLM)
262 msg (M_FATAL, "Sorry, this version of " PACKAGE_NAME " was built without NTLM Proxy support.");
263 #endif
265 p->defined = true;
266 return p;
269 void
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 ();
278 char buf[256];
279 char buf2[128];
280 char get[80];
281 int status;
282 int nparms;
284 /* format HTTP CONNECT message */
285 openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s",
286 host,
287 port,
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))
294 goto error;
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))
302 goto error;
305 /* auth specified? */
306 switch (p->auth_method)
308 case HTTP_AUTH_NONE:
309 break;
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);
316 openvpn_sleep (1);
317 if (!send_line_crlf (sd, buf))
318 goto error;
319 break;
321 #if NTLM
322 case HTTP_AUTH_NTLM:
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);
327 openvpn_sleep (1);
328 if (!send_line_crlf (sd, buf))
329 goto error;
330 break;
331 #endif
333 default:
334 ASSERT (0);
337 /* send empty CR, LF */
338 openvpn_sleep (1);
339 if (!send_crlf (sd))
340 goto error;
342 /* receive reply from proxy */
343 if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received))
344 goto error;
346 /* remove trailing CR, LF */
347 chomp (buf);
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");
359 /* check for NTLM */
360 if (p->auth_method == HTTP_AUTH_NTLM)
362 #if NTLM
363 /* look for the phase 2 response */
365 while (true)
367 if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received))
368 goto error;
369 chomp (buf);
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..." */
377 if (nparms == 1)
379 /* parse buf2 */
380 msg (D_PROXY, "auth string: '%s'", buf2);
381 break;
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",
395 host,
396 port,
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))
403 goto error;
405 /* send HOST etc, */
406 openvpn_sleep (1);
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))
410 goto error;
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);
416 openvpn_sleep (1);
417 if (!send_line_crlf (sd, buf))
418 goto error;
419 /* ok so far... */
420 /* send empty CR, LF */
421 openvpn_sleep (1);
422 if (!send_crlf (sd))
423 goto error;
425 /* receive reply from proxy */
426 if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received))
427 goto error;
429 /* remove trailing CR, LF */
430 chomp (buf);
432 msg (D_PROXY, "HTTP proxy returned: '%s'", buf);
434 /* parse return string */
435 nparms = sscanf (buf, "%*s %d", &status);
436 #else
437 ASSERT (0); /* No NTLM support */
438 #endif
440 else goto error;
444 /* check return code, success = 200 */
445 if (nparms < 1 || status != 200)
447 msg (D_LINK_ERRORS, "HTTP proxy returned bad status");
448 #if 0
449 /* DEBUGGING -- show a multi-line HTTP error response */
450 while (true)
452 if (!recv_line (sd, buf, sizeof (buf), p->options.timeout, true, NULL, signal_received))
453 goto error;
454 chomp (buf);
455 msg (D_PROXY, "HTTP proxy returned: '%s'", buf);
457 #endif
458 goto error;
461 /* receive line from proxy and discard */
462 if (!recv_line (sd, NULL, 0, p->options.timeout, true, NULL, signal_received))
463 goto error;
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))
472 #if 0
473 if (lookahead && BLEN (lookahead))
474 msg (M_INFO, "HTTP PROXY: lookahead: %s", format_hex (BPTR (lookahead), BLEN (lookahead), 0));
475 #endif
477 gc_free (&gc);
478 return;
480 error:
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 */
484 gc_free (&gc);
485 return;
488 #else
489 static void dummy(void) {}
490 #endif /* ENABLE_HTTP_PROXY */