diff options
author | David Sommerseth <dazo@users.sourceforge.net> | 2010-05-16 19:35:08 +0200 |
---|---|---|
committer | David Sommerseth <dazo@users.sourceforge.net> | 2010-05-16 19:35:08 +0200 |
commit | 942e05525ac2cbf4a258c7b8f514a058cd00486b (patch) | |
tree | a0c2ffb02472271b5bf84855c050a1b8fe1ae2b2 | |
parent | Avoid repetition of "this config may cache passwords in memory" (v2) (diff) | |
parent | Cleaning up after rebase (diff) | |
download | openvpn-942e05525ac2cbf4a258c7b8f514a058cd00486b.tar.xz |
Merge branch 'master' into bugfix2.1
Conflicts:
openvpn.8
--http-proxy is enhanced with auth-nct for auth (no-clear-text)
Signed-off-by: David Sommerseth <dazo@users.sourceforge.net>
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | config-win32.h | 1 | ||||
-rw-r--r-- | httpdigest.c | 143 | ||||
-rw-r--r-- | httpdigest.h | 60 | ||||
-rw-r--r-- | openvpn.8 | 15 | ||||
-rw-r--r-- | options.c | 12 | ||||
-rw-r--r-- | proxy.c | 508 | ||||
-rw-r--r-- | proxy.h | 17 | ||||
-rw-r--r-- | socket.c | 2 | ||||
-rw-r--r-- | syshead.h | 9 | ||||
-rw-r--r-- | win/build.py | 11 |
11 files changed, 677 insertions, 102 deletions
diff --git a/Makefile.am b/Makefile.am index f453cdb..2980dac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -88,6 +88,7 @@ openvpn_SOURCES = \ fragment.c fragment.h \ gremlin.c gremlin.h \ helper.c helper.h \ + httpdigest.c httpdigest.h \ lladdr.c lladdr.h \ init.c init.h \ integer.h \ diff --git a/config-win32.h b/config-win32.h index c26b0af..be0b320 100644 --- a/config-win32.h +++ b/config-win32.h @@ -291,6 +291,7 @@ typedef unsigned long in_addr_t; #define lseek _lseek
#define chdir _chdir
#define strdup _strdup
+#define strcasecmp _stricmp
#define chsize _chsize
#define S_IRUSR 0
#define S_IWUSR 0
diff --git a/httpdigest.c b/httpdigest.c new file mode 100644 index 0000000..ef77e12 --- /dev/null +++ b/httpdigest.c @@ -0,0 +1,143 @@ +/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#if PROXY_DIGEST_AUTH
+
+#include "crypto.h"
+#include "httpdigest.h"
+
+static void
+CvtHex(
+ IN HASH Bin,
+ OUT HASHHEX Hex
+ )
+{
+ unsigned short i;
+ unsigned char j;
+
+ for (i = 0; i < HASHLEN; i++) {
+ j = (Bin[i] >> 4) & 0xf;
+ if (j <= 9)
+ Hex[i*2] = (j + '0');
+ else
+ Hex[i*2] = (j + 'a' - 10);
+ j = Bin[i] & 0xf;
+ if (j <= 9)
+ Hex[i*2+1] = (j + '0');
+ else
+ Hex[i*2+1] = (j + 'a' - 10);
+ };
+ Hex[HASHHEXLEN] = '\0';
+};
+
+/* calculate H(A1) as per spec */
+void
+DigestCalcHA1(
+ IN char * pszAlg,
+ IN char * pszUserName,
+ IN char * pszRealm,
+ IN char * pszPassword,
+ IN char * pszNonce,
+ IN char * pszCNonce,
+ OUT HASHHEX SessionKey
+ )
+{
+ MD5_CTX Md5Ctx;
+ HASH HA1;
+
+ MD5_Init(&Md5Ctx);
+ MD5_Update(&Md5Ctx, pszUserName, strlen(pszUserName));
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszRealm, strlen(pszRealm));
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszPassword, strlen(pszPassword));
+ MD5_Final(HA1, &Md5Ctx);
+ if (pszAlg && strcasecmp(pszAlg, "md5-sess") == 0)
+ {
+ MD5_Init(&Md5Ctx);
+ MD5_Update(&Md5Ctx, HA1, HASHLEN);
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+ MD5_Final(HA1, &Md5Ctx);
+ };
+ CvtHex(HA1, SessionKey);
+}
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void
+DigestCalcResponse(
+ IN HASHHEX HA1, /* H(A1) */
+ IN char * pszNonce, /* nonce from server */
+ IN char * pszNonceCount, /* 8 hex digits */
+ IN char * pszCNonce, /* client nonce */
+ IN char * pszQop, /* qop-value: "", "auth", "auth-int" */
+ IN char * pszMethod, /* method from the request */
+ IN char * pszDigestUri, /* requested URL */
+ IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
+ OUT HASHHEX Response /* request-digest or response-digest */
+ )
+{
+ MD5_CTX Md5Ctx;
+ HASH HA2;
+ HASH RespHash;
+ HASHHEX HA2Hex;
+
+ // calculate H(A2)
+ MD5_Init(&Md5Ctx);
+ MD5_Update(&Md5Ctx, pszMethod, strlen(pszMethod));
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
+ if (strcasecmp(pszQop, "auth-int") == 0)
+ {
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, HEntity, HASHHEXLEN);
+ };
+ MD5_Final(HA2, &Md5Ctx);
+ CvtHex(HA2, HA2Hex);
+
+ // calculate response
+ MD5_Init(&Md5Ctx);
+ MD5_Update(&Md5Ctx, HA1, HASHHEXLEN);
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+ MD5_Update(&Md5Ctx, ":", 1);
+ if (*pszQop)
+ {
+ MD5_Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount));
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszQop, strlen(pszQop));
+ MD5_Update(&Md5Ctx, ":", 1);
+ };
+ MD5_Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
+ MD5_Final(RespHash, &Md5Ctx);
+ CvtHex(RespHash, Response);
+}
+
+#endif
diff --git a/httpdigest.h b/httpdigest.h new file mode 100644 index 0000000..fb6d114 --- /dev/null +++ b/httpdigest.h @@ -0,0 +1,60 @@ +/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if PROXY_DIGEST_AUTH
+
+#define HASHLEN 16
+typedef unsigned char HASH[HASHLEN];
+#define HASHHEXLEN 32
+typedef unsigned char HASHHEX[HASHHEXLEN+1];
+#undef IN
+#undef OUT
+#define IN const
+#define OUT
+
+/* calculate H(A1) as per HTTP Digest spec */
+void DigestCalcHA1(
+ IN char * pszAlg,
+ IN char * pszUserName,
+ IN char * pszRealm,
+ IN char * pszPassword,
+ IN char * pszNonce,
+ IN char * pszCNonce,
+ OUT HASHHEX SessionKey
+ );
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void DigestCalcResponse(
+ IN HASHHEX HA1, /* H(A1) */
+ IN char * pszNonce, /* nonce from server */
+ IN char * pszNonceCount, /* 8 hex digits */
+ IN char * pszCNonce, /* client nonce */
+ IN char * pszQop, /* qop-value: "", "auth", "auth-int" */
+ IN char * pszMethod, /* method from the request */
+ IN char * pszDigestUri, /* requested URL */
+ IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
+ OUT HASHHEX Response /* request-digest or response-digest */
+ );
+
+#endif
@@ -474,7 +474,7 @@ InternetQueryOption API. This option exists in OpenVPN 2.1 or higher. .\"********************************************************* .TP -.B \-\-http-proxy server port [authfile|'auto'] [auth-method] +.B \-\-http-proxy server port [authfile|'auto'|'auto-nct'] [auth-method] Connect to remote host through an HTTP proxy at address .B server and port @@ -487,6 +487,13 @@ is a file containing a username and password on 2 lines, or .B auth-method should be one of "none", "basic", or "ntlm". +HTTP Digest authentication is supported as well, but only via +the +.B auto +or +.B auto-nct +flags (below). + The .B auto flag causes OpenVPN to automatically determine the @@ -494,6 +501,12 @@ flag causes OpenVPN to automatically determine the and query stdin or the management interface for username/password credentials, if required. This flag exists on OpenVPN 2.1 or higher. + +The +.B auto-nct +flag (no clear-text auth) instructs OpenVPN to automatically +determine the authentication method, but to reject weak +authentication protocols such as HTTP Basic Authentication. .\"********************************************************* .TP .B \-\-http-proxy-retry @@ -108,8 +108,9 @@ static const char usage_message[] = " up is a file containing username/password on 2 lines, or\n" " 'stdin' to prompt from console. Add auth='ntlm' if\n" " the proxy requires NTLM authentication.\n" - "--http-proxy s p 'auto': Like the above directive, but automatically determine\n" - " auth method and query for username/password if needed.\n" + "--http-proxy s p 'auto[-nct]' : Like the above directive, but automatically\n" + " determine auth method and query for username/password\n" + " if needed. auto-nct disables weak proxy auth methods.\n" "--http-proxy-retry : Retry indefinitely on HTTP proxy errors.\n" "--http-proxy-timeout n : Proxy timeout in seconds, default=5.\n" "--http-proxy-option type [parm] : Set extended HTTP proxy options.\n" @@ -4197,8 +4198,13 @@ add_option (struct options *options, if (p[3]) { + /* auto -- try to figure out proxy addr, port, and type automatically */ + /* semiauto -- given proxy addr:port, try to figure out type automatically */ + /* (auto|semiauto)-nct -- disable proxy auth cleartext protocols (i.e. basic auth) */ if (streq (p[3], "auto")) - ho->auth_retry = true; + ho->auth_retry = PAR_ALL; + else if (streq (p[3], "auto-nct")) + ho->auth_retry = PAR_NCT; else { ho->auth_method_string = "basic"; @@ -26,11 +26,13 @@ #include "common.h" #include "misc.h" +#include "crypto.h" #include "win32.h" #include "socket.h" #include "fdmisc.h" #include "proxy.h" #include "base64.h" +#include "httpdigest.h" #include "ntlm.h" #ifdef WIN32 @@ -229,6 +231,189 @@ get_user_pass_http (struct http_proxy_info *p, const bool force) p->up = static_proxy_user_pass; } } +static void +clear_user_pass_http (void) +{ + purge_user_pass (&static_proxy_user_pass, true); +} + +static void +dump_residual (socket_descriptor_t sd, + int timeout, + volatile int *signal_received) +{ + char buf[256]; + while (true) + { + if (!recv_line (sd, buf, sizeof (buf), timeout, true, NULL, signal_received)) + return; + chomp (buf); + msg (D_PROXY, "PROXY HEADER: '%s'", buf); + } +} + +/* + * Extract the Proxy-Authenticate header from the stream. + * Consumes all headers. + */ +static int +get_proxy_authenticate (socket_descriptor_t sd, + int timeout, + char **data, + struct gc_arena *gc, + volatile int *signal_received) +{ + char buf[256]; + int ret = HTTP_AUTH_NONE; + while (true) + { + if (!recv_line (sd, buf, sizeof (buf), timeout, true, NULL, signal_received)) + { + *data = NULL; + return HTTP_AUTH_NONE; + } + chomp (buf); + if (!strlen(buf)) + return ret; + if (ret == HTTP_AUTH_NONE && !strncmp(buf, "Proxy-Authenticate: ", 20)) + { + if (!strncmp(buf+20, "Basic ", 6)) + { + msg (D_PROXY, "PROXY AUTH BASIC: '%s'", buf); + *data = string_alloc(buf+26, gc); + ret = HTTP_AUTH_BASIC; + } +#if PROXY_DIGEST_AUTH + else if (!strncmp(buf+20, "Digest ", 7)) + { + msg (D_PROXY, "PROXY AUTH DIGEST: '%s'", buf); + *data = string_alloc(buf+27, gc); + ret = HTTP_AUTH_DIGEST; + } +#endif +#if NTLM + else if (!strncmp(buf+20, "NTLM", 4)) + { + msg (D_PROXY, "PROXY AUTH HTLM: '%s'", buf); + *data = NULL; + ret = HTTP_AUTH_NTLM; + } +#endif + } + } +} + +static void +store_proxy_authenticate (struct http_proxy_info *p, char *data) +{ + if (p->proxy_authenticate) + free (p->proxy_authenticate); + p->proxy_authenticate = data; +} + +/* + * Parse out key/value pairs from Proxy-Authenticate string. + * Return true on success, or false on parse failure. + */ +static bool +get_key_value(const char *str, /* source string */ + char *key, /* key stored here */ + char *value, /* value stored here */ + int max_key_len, + int max_value_len, + const char **endptr) /* next search position */ +{ + int c; + bool starts_with_quote = false; + bool escape = false; + + for (c = max_key_len-1; (*str && (*str != '=') && c--); ) + *key++ = *str++; + *key = '\0'; + + if('=' != *str++) + /* no key/value found */ + return false; + + if('\"' == *str) + { + /* quoted string */ + str++; + starts_with_quote = true; + } + + for (c = max_value_len-1; *str && c--; str++) + { + switch (*str) + { + case '\\': + if (!escape) + { + /* possibly the start of an escaped quote */ + escape = true; + *value++ = '\\'; /* even though this is an escape character, we still + store it as-is in the target buffer */ + continue; + } + break; + case ',': + if (!starts_with_quote) + { + /* this signals the end of the value if we didn't get a starting quote + and then we do "sloppy" parsing */ + c=0; /* the end */ + continue; + } + break; + case '\r': + case '\n': + /* end of string */ + c=0; + continue; + case '\"': + if (!escape && starts_with_quote) + { + /* end of string */ + c=0; + continue; + } + break; + } + escape = false; + *value++ = *str; + } + *value = '\0'; + + *endptr = str; + + return true; /* success */ +} + +static char * +get_pa_var (const char *key, const char *pa, struct gc_arena *gc) +{ + char k[64]; + char v[256]; + const char *content = pa; + + while (true) + { + const int status = get_key_value(content, k, v, sizeof(k), sizeof(v), &content); + if (status) + { + if (!strcmp(key, k)) + return string_alloc(v, gc); + } + else + return NULL; + + /* advance to start of next key */ + if (*content == ',') + ++content; + while (*content && isspace(*content)) + ++content; + } +} struct http_proxy_info * http_proxy_new (const struct http_proxy_options *o, @@ -265,7 +450,8 @@ http_proxy_new (const struct http_proxy_options *o, opt.server = auto_proxy_info->http.server; opt.port = auto_proxy_info->http.port; - opt.auth_retry = true; + if (!opt.auth_retry) + opt.auth_retry = PAR_ALL; o = &opt; } @@ -287,12 +473,14 @@ http_proxy_new (const struct http_proxy_options *o, p->auth_method = HTTP_AUTH_NONE; else if (!strcmp (o->auth_method_string, "basic")) p->auth_method = HTTP_AUTH_BASIC; +#if NTLM else if (!strcmp (o->auth_method_string, "ntlm")) p->auth_method = HTTP_AUTH_NTLM; else if (!strcmp (o->auth_method_string, "ntlm2")) p->auth_method = HTTP_AUTH_NTLM2; +#endif else - msg (M_FATAL, "ERROR: unknown HTTP authentication method: '%s' -- only the 'none', 'basic', 'ntlm', or 'ntlm2' methods are currently supported", + msg (M_FATAL, "ERROR: unknown HTTP authentication method: '%s'", o->auth_method_string); } @@ -326,101 +514,110 @@ establish_http_proxy_passthru (struct http_proxy_info *p, volatile int *signal_received) { struct gc_arena gc = gc_new (); - char buf[256]; + char buf[512]; char buf2[128]; char get[80]; int status; int nparms; bool ret = false; + bool processed = false; /* get user/pass if not previously given or if --auto-proxy is being used */ if (p->auth_method == HTTP_AUTH_BASIC + || p->auth_method == HTTP_AUTH_DIGEST || p->auth_method == HTTP_AUTH_NTLM) get_user_pass_http (p, false); - /* format HTTP CONNECT message */ - openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s", - host, - port, - p->options.http_version); - - msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); - - /* send HTTP CONNECT message to proxy */ - if (!send_line_crlf (sd, buf)) - goto error; - - /* send User-Agent string if provided */ - if (p->options.user_agent) + /* are we being called again after getting the digest server nonce in the previous transaction? */ + if (p->auth_method == HTTP_AUTH_DIGEST && p->proxy_authenticate) { - openvpn_snprintf (buf, sizeof(buf), "User-Agent: %s", - p->options.user_agent); - if (!send_line_crlf (sd, buf)) - goto error; + nparms = 1; + status = 407; } - - /* auth specified? */ - switch (p->auth_method) + else { - case HTTP_AUTH_NONE: - break; + /* format HTTP CONNECT message */ + openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s", + host, + port, + p->options.http_version); - case HTTP_AUTH_BASIC: - openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: Basic %s", - username_password_as_base64 (p, &gc)); - msg (D_PROXY, "Attempting Basic Proxy-Authorization"); - dmsg (D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf); - openvpn_sleep (1); + msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); + + /* send HTTP CONNECT message to proxy */ if (!send_line_crlf (sd, buf)) goto error; - break; + + /* send User-Agent string if provided */ + if (p->options.user_agent) + { + openvpn_snprintf (buf, sizeof(buf), "User-Agent: %s", + p->options.user_agent); + if (!send_line_crlf (sd, buf)) + goto error; + } + + /* auth specified? */ + switch (p->auth_method) + { + case HTTP_AUTH_NONE: + break; + + case HTTP_AUTH_BASIC: + openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: Basic %s", + username_password_as_base64 (p, &gc)); + msg (D_PROXY, "Attempting Basic Proxy-Authorization"); + dmsg (D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf); + if (!send_line_crlf (sd, buf)) + goto error; + break; #if NTLM - case HTTP_AUTH_NTLM: - case HTTP_AUTH_NTLM2: - /* keep-alive connection */ - openvpn_snprintf (buf, sizeof(buf), "Proxy-Connection: Keep-Alive"); - if (!send_line_crlf (sd, buf)) - goto error; + case HTTP_AUTH_NTLM: + case HTTP_AUTH_NTLM2: + /* keep-alive connection */ + openvpn_snprintf (buf, sizeof(buf), "Proxy-Connection: Keep-Alive"); + if (!send_line_crlf (sd, buf)) + goto error; - openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: NTLM %s", - ntlm_phase_1 (p, &gc)); - msg (D_PROXY, "Attempting NTLM Proxy-Authorization phase 1"); - dmsg (D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf); - openvpn_sleep (1); - if (!send_line_crlf (sd, buf)) - goto error; - break; + openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: NTLM %s", + ntlm_phase_1 (p, &gc)); + msg (D_PROXY, "Attempting NTLM Proxy-Authorization phase 1"); + dmsg (D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf); + if (!send_line_crlf (sd, buf)) + goto error; + break; #endif - default: - ASSERT (0); - } + default: + ASSERT (0); + } - /* send empty CR, LF */ - openvpn_sleep (1); - if (!send_crlf (sd)) - goto error; + /* send empty CR, LF */ + if (!send_crlf (sd)) + goto error; - /* receive reply from proxy */ - if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) - goto error; + /* receive reply from proxy */ + if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) + goto error; + + /* remove trailing CR, LF */ + chomp (buf); - /* remove trailing CR, LF */ - chomp (buf); + msg (D_PROXY, "HTTP proxy returned: '%s'", buf); - msg (D_PROXY, "HTTP proxy returned: '%s'", buf); + /* parse return string */ + nparms = sscanf (buf, "%*s %d", &status); - /* parse return string */ - nparms = sscanf (buf, "%*s %d", &status); + } /* check for a "407 Proxy Authentication Required" response */ - if (nparms >= 1 && status == 407) + while (nparms >= 1 && status == 407) { msg (D_PROXY, "Proxy requires authentication"); /* check for NTLM */ - if (p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2) + if ((p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2) && !processed) { #if NTLM /* look for the phase 2 response */ @@ -448,7 +645,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, msg (D_PROXY, "Received NTLM Proxy-Authorization phase 2 response"); /* receive and discard everything else */ - while (recv_line (sd, NULL, 0, p->options.timeout, true, NULL, signal_received)) + while (recv_line (sd, NULL, 0, 2, true, NULL, signal_received)) ; /* now send the phase 3 reply */ @@ -472,7 +669,6 @@ establish_http_proxy_passthru (struct http_proxy_info *p, /* send HOST etc, */ - openvpn_sleep (1); openvpn_snprintf (buf, sizeof(buf), "Host: %s", host); msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); if (!send_line_crlf (sd, buf)) @@ -490,12 +686,10 @@ establish_http_proxy_passthru (struct http_proxy_info *p, } msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); - openvpn_sleep (1); if (!send_line_crlf (sd, buf)) goto error; /* ok so far... */ /* send empty CR, LF */ - openvpn_sleep (1); if (!send_crlf (sd)) goto error; @@ -510,27 +704,167 @@ establish_http_proxy_passthru (struct http_proxy_info *p, /* parse return string */ nparms = sscanf (buf, "%*s %d", &status); -#else - ASSERT (0); /* No NTLM support */ + processed = true; #endif } - else if (p->auth_method == HTTP_AUTH_NONE && p->options.auth_retry) +#if PROXY_DIGEST_AUTH + else if (p->auth_method == HTTP_AUTH_DIGEST && !processed) + { + char *pa = p->proxy_authenticate; + const int method = p->auth_method; + ASSERT(pa); + + if (method == HTTP_AUTH_DIGEST) + { + const char *http_method = "CONNECT"; + const char *nonce_count = "00000001"; + const char *qop = "auth"; + const char *username = p->up.username; + const char *password = p->up.password; + char *opaque_kv = ""; + char uri[128]; + uint8_t cnonce_raw[8]; + uint8_t *cnonce; + HASHHEX session_key; + HASHHEX response; + + const char *realm = get_pa_var("realm", pa, &gc); + const char *nonce = get_pa_var("nonce", pa, &gc); + const char *algor = get_pa_var("algorithm", pa, &gc); + const char *opaque = get_pa_var("opaque", pa, &gc); + + /* generate a client nonce */ + ASSERT(RAND_bytes(cnonce_raw, sizeof(cnonce_raw))); + cnonce = make_base64_string2(cnonce_raw, sizeof(cnonce_raw), &gc); + + + /* build the digest response */ + openvpn_snprintf (uri, sizeof(uri), "%s:%d", + host, + port); + + if (opaque) + { + const int len = strlen(opaque)+16; + opaque_kv = gc_malloc(len, false, &gc); + openvpn_snprintf (opaque_kv, len, ", opaque=\"%s\"", opaque); + } + + DigestCalcHA1(algor, + username, + realm, + password, + nonce, + cnonce, + session_key); + DigestCalcResponse(session_key, + nonce, + nonce_count, + cnonce, + qop, + http_method, + uri, + NULL, + response); + + /* format HTTP CONNECT message */ + openvpn_snprintf (buf, sizeof(buf), "%s %s HTTP/%s", + http_method, + uri, + p->options.http_version); + + msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); + + /* send HTTP CONNECT message to proxy */ + if (!send_line_crlf (sd, buf)) + goto error; + + /* send HOST etc, */ + openvpn_snprintf (buf, sizeof(buf), "Host: %s", host); + msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); + if (!send_line_crlf (sd, buf)) + goto error; + + /* send digest response */ + 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", + username, + realm, + nonce, + uri, + qop, + nonce_count, + cnonce, + response, + opaque_kv + ); + msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); + if (!send_line_crlf (sd, buf)) + goto error; + if (!send_crlf (sd)) + goto error; + + /* receive reply from proxy */ + if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) + goto error; + + /* remove trailing CR, LF */ + chomp (buf); + + msg (D_PROXY, "HTTP proxy returned: '%s'", buf); + + /* parse return string */ + nparms = sscanf (buf, "%*s %d", &status); + processed = true; + } + else + { + msg (D_PROXY, "HTTP proxy: digest method not supported"); + goto error; + } + } +#endif + else if (p->options.auth_retry) { - /* - * Proxy needs authentication, but we don't have a user/pass. - * Now we will change p->auth_method and return true so that - * our caller knows to call us again on a newly opened socket. - * JYFIXME: This code needs to check proxy error output and set - * JYFIXME: p->auth_method = HTTP_AUTH_NTLM if necessary. - */ - p->auth_method = HTTP_AUTH_BASIC; - ret = true; - goto done; + /* figure out what kind of authentication the proxy needs */ + char *pa = NULL; + const int method = get_proxy_authenticate(sd, + p->options.timeout, + &pa, + NULL, + signal_received); + if (method != HTTP_AUTH_NONE) + { + if (pa) + msg (D_PROXY, "HTTP proxy authenticate '%s'", pa); + if (p->options.auth_retry == PAR_NCT && method == HTTP_AUTH_BASIC) + { + msg (D_PROXY, "HTTP proxy: support for basic auth and other cleartext proxy auth methods is disabled"); + goto error; + } + p->auth_method = method; + store_proxy_authenticate(p, pa); + ret = true; + goto done; + } + else + { + msg (D_PROXY, "HTTP proxy: do not recognize the authentication method required by proxy"); + free (pa); + goto error; + } } else - goto error; - } + { + if (!processed) + msg (D_PROXY, "HTTP proxy: no support for proxy authentication method"); + goto error; + } + /* clear state */ + if (p->options.auth_retry) + clear_user_pass_http(); + store_proxy_authenticate(p, NULL); + } /* check return code, success = 200 */ if (nparms < 1 || status != 200) @@ -538,13 +872,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, msg (D_LINK_ERRORS, "HTTP proxy returned bad status"); #if 0 /* DEBUGGING -- show a multi-line HTTP error response */ - while (true) - { - if (!recv_line (sd, buf, sizeof (buf), p->options.timeout, true, NULL, signal_received)) - goto error; - chomp (buf); - msg (D_PROXY, "HTTP proxy returned: '%s'", buf); - } + dump_residual(sd, p->options.timeout, signal_received); #endif goto error; } @@ -55,18 +55,24 @@ void show_win_proxy_settings (const int msglevel); #ifdef ENABLE_HTTP_PROXY /* HTTP CONNECT authentication methods */ -#define HTTP_AUTH_NONE 0 -#define HTTP_AUTH_BASIC 1 -#define HTTP_AUTH_NTLM 2 -#define HTTP_AUTH_N 3 -#define HTTP_AUTH_NTLM2 4 +#define HTTP_AUTH_NONE 0 +#define HTTP_AUTH_BASIC 1 +#define HTTP_AUTH_DIGEST 2 +#define HTTP_AUTH_NTLM 3 +#define HTTP_AUTH_NTLM2 4 +#define HTTP_AUTH_N 5 /* number of HTTP_AUTH methods */ struct http_proxy_options { const char *server; int port; bool retry; int timeout; + +# define PAR_NO 0 /* don't support any auth retries */ +# define PAR_ALL 1 /* allow all proxy auth protocols */ +# define PAR_NCT 2 /* disable cleartext proxy auth protocols */ bool auth_retry; + const char *auth_method_string; const char *auth_file; const char *http_version; @@ -78,6 +84,7 @@ struct http_proxy_info { int auth_method; struct http_proxy_options options; struct user_pass up; + char *proxy_authenticate; }; struct http_proxy_info *http_proxy_new (const struct http_proxy_options *o, @@ -485,7 +485,7 @@ socket_set_buffers (int fd, const struct socket_buffer_size *sbs) static bool socket_set_tcp_nodelay (int sd, int state) { -#if defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP) && defined(TCP_NODELAY) +#if defined(WIN32) || (defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP) && defined(TCP_NODELAY)) if (setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (void *) &state, sizeof (state)) != 0) { msg (M_WARN, "NOTE: setsockopt TCP_NODELAY=%d failed", state); @@ -576,6 +576,15 @@ socket_defined (const socket_descriptor_t sd) #endif /* + * Should we include proxy digest auth functionality + */ +#if defined(USE_CRYPTO) && defined(ENABLE_HTTP_PROXY) +#define PROXY_DIGEST_AUTH 1 +#else +#define PROXY_DIGEST_AUTH 0 +#endif + +/* * Should we include code common to all proxy methods? */ #if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_SOCKS) diff --git a/win/build.py b/win/build.py index f42715b..3a9fbc7 100644 --- a/win/build.py +++ b/win/build.py @@ -1,4 +1,4 @@ -import os
+import os, sys
from wb import system, config, home_fn, cd_home
os.environ['PATH'] += ";%s\\VC" % (os.path.normpath(config['MSVC']),)
@@ -10,6 +10,13 @@ def main(): cd_home()
build_vc("nmake /f %s" % (home_fn('msvc.mak'),))
+def clean():
+ cd_home()
+ build_vc("nmake /f %s clean" % (home_fn('msvc.mak'),))
+
# if we are run directly, and not loaded as a module
if __name__ == "__main__":
- main()
+ if len(sys.argv) == 2 and sys.argv[1] == 'clean':
+ clean()
+ else:
+ main()
|