bug 599295 r=jduell

--HG--
extra : rebase_source : b376fd42a37c8b92389b43fac2e253a8466c3349
This commit is contained in:
Patrick McManus 2011-11-02 17:43:27 -04:00
parent e3203701e9
commit f495a11892
6 changed files with 192 additions and 20 deletions

View File

@ -584,6 +584,7 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t
if loc.scheme == "https" and "nocert" not in loc.options:
customCertRE = re.compile("^cert=(?P<nickname>[0-9a-zA-Z_ ]+)")
clientAuthRE = re.compile("^clientauth=(?P<clientauth>[a-z]+)")
redirRE = re.compile("^redir=(?P<redirhost>[0-9a-zA-Z_ .]+)")
for option in loc.options:
match = customCertRE.match(option)
if match:
@ -597,6 +598,12 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t
sslTunnelConfig.write("clientauth:%s:%s:%s:%s\n" %
(loc.host, loc.port, self.sslPort, clientauth))
match = redirRE.match(option)
if match:
redirhost = match.group("redirhost")
sslTunnelConfig.write("redirhost:%s:%s:%s:%s\n" %
(loc.host, loc.port, self.sslPort, redirhost))
sslTunnelConfig.close()
# Pre-create the certification database for the profile

View File

@ -52,7 +52,7 @@
# number is the default for the protocol.
#
# Unrecognized options are ignored. Recognized options are "primary" and
# "privileged", "nocert", "cert=some_cert_nickname".
# "privileged", "nocert", "cert=some_cert_nickname", "redir=hostname".
#
# "primary" denotes a location which is the canonical location of
# the server; this location is the one assumed for requests which don't
@ -71,6 +71,12 @@
# directory. When new certificate is added to this dir pgo/ssltunnel
# must be builded then.
#
# "redir=hostname" tells the pgo server is only used for https://
# hosts while processing the CONNECT tunnel request. It responds
# to the CONNECT with a 302 and redirection to the hostname instead
# of connecting to the real back end and replying with a 200. This
# mode exists primarily to ensure we don't allow a proxy to do that.
#
#
# This is the primary location from which tests run.
@ -175,3 +181,10 @@ http://malware.example.com:80
# Bug 483437, 484111
https://www.bank1.com:443 privileged,cert=escapeattack1
https://www.bank2.com:443 privileged,cert=escapeattack2
#
# CONNECT for redirproxy results in a 302 redirect to
# test1.example.com
#
https://redirproxy.example.com:443 privileged,redir=test1.example.com

View File

@ -70,6 +70,7 @@ _CHROME_FILES = \
test_bug571390.xul \
test_bug574596.html \
test_bug683852.xul \
test_bug599295.html \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -0,0 +1,81 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=599295
-->
<head>
<title>Test for Bug 599295</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=599295">Mozilla Bug 599295</a>
<style type="text/css">
#link1 a { -moz-user-select:none; }
</style>
<div id="link1"><a href="http://www.mozilla.org/">link1</a></div>
<div id="link2"><a href="http://www.mozilla.org/">link2</a></div>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 599295 **/
/* Do not allow a response to a CONNECT method, used to establish an
SSL tunnel over an HTTP proxy, to contain a redirect */
const BinaryInputStream =
Components.Constructor("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream",
"setInputStream");
var listener = {
_httpstatus : 0,
onStartRequest: function(request, context) {
request.QueryInterface(Components.interfaces.nsIHttpChannel);
_httpstatus = request.responseStatus;
},
onDataAvailable: function(request, context, stream, offset, count) {
new BinaryInputStream(stream).readByteArray(count);
},
onStopRequest: function(request, context, status) {
/* testing here that the redirect was not followed. If it was followed
we would see a http status of 200 and status of NS_OK */
is(_httpstatus, 302, "http status 302");
is(status, Components.results.NS_ERROR_CONNECTION_REFUSED, "raised refused");
SimpleTest.finish();
}
};
function runTest() {
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var uri = ios.newURI("https://redirproxy.example.com/test", "", null);
var channel = ios.newChannelFromURI(uri);
/* Previously, necko would allow a 302 as part of a CONNECT response
if the LOAD_DOCUMENT_URI flag was set and the original document
URI had not yet been changed. */
channel.loadFlags |= Components.interfaces.nsIChannel.LOAD_DOCUMENT_URI;
channel.QueryInterface(Components.interfaces.nsIHttpChannelInternal);
channel.documentURI = uri;
channel.asyncOpen(listener, null);
}
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(runTest);
</script>
</pre>
</body>
</html>

View File

@ -863,18 +863,8 @@ bool
nsHttpChannel::ShouldSSLProxyResponseContinue(PRUint32 httpStatus)
{
// When SSL connect has failed, allow proxy reply to continue only if it's
// an auth request, or a redirect of a non-POST top-level document load.
switch (httpStatus) {
case 407:
return true;
case 300: case 301: case 302: case 303: case 307:
{
return ( (mLoadFlags & nsIChannel::LOAD_DOCUMENT_URI) &&
mURI == mDocumentURI &&
mRequestHead.Method() != nsHttp::Post);
}
}
return false;
// a 407 (proxy authentication required) response
return (httpStatus == 407);
}
/**

View File

@ -186,6 +186,7 @@ typedef struct {
string cert_nickname;
PLHashTable* host_cert_table;
PLHashTable* host_clientauth_table;
PLHashTable* host_redir_table;
} server_info_t;
typedef struct {
@ -330,7 +331,7 @@ void SignalShutdown()
bool ReadConnectRequest(server_info_t* server_info,
relayBuffer& buffer, PRInt32* result, string& certificate,
client_auth_option* clientauth, string& host)
client_auth_option* clientauth, string& host, string& location)
{
if (buffer.present() < 4) {
LOG_DEBUG((" !! only %d bytes present in the buffer", (int)buffer.present()));
@ -375,13 +376,17 @@ bool ReadConnectRequest(server_info_t* server_info,
else
*clientauth = caNone;
void *redir = PL_HashTableLookup(server_info->host_redir_table, token);
if (redir)
location = static_cast<char*>(redir);
token = strtok2(_caret, "/", &_caret);
if (strcmp(token, "HTTP")) {
LOG_ERRORD((" not tailed with HTTP but with %s", token));
return true;
}
*result = 200;
*result = (redir) ? 302 : 200;
return true;
}
@ -613,6 +618,7 @@ void HandleConnection(void* data)
bool ssl_updated = !do_http_proxy;
bool expect_request_start = do_http_proxy;
string certificateToUse;
string locationHeader;
client_auth_option clientAuth;
string fullHost;
@ -747,7 +753,7 @@ void HandleConnection(void* data)
// We have to accept and handle the initial CONNECT request here
PRInt32 response;
if (!connect_accepted && ReadConnectRequest(ci->server_info, buffers[s],
&response, certificateToUse, &clientAuth, fullHost))
&response, certificateToUse, &clientAuth, fullHost, locationHeader))
{
// Mark this as a proxy-only connection (no SSL) if the CONNECT
// request didn't come for port 443 or from any of the server's
@ -776,19 +782,33 @@ void HandleConnection(void* data)
connect_accepted = true;
// Store response to the oposite buffer
if (response != 200)
if (response == 200)
{
LOG_DEBUG((" accepted CONNECT request, connected to the server, sending OK to the client\n"));
strcpy(buffers[s2].buffer, "HTTP/1.1 200 Connected\r\nConnection: keep-alive\r\n\r\n");
}
else if (response == 302)
{
LOG_DEBUG((" accepted CONNECT request with redirection, "
"sending location and 302 to the client\n"));
client_done = true;
sprintf(buffers[s2].buffer,
"HTTP/1.1 302 Moved\r\n"
"Location: https://%s/\r\n"
"Connection: close\r\n\r\n",
locationHeader.c_str());
}
else
{
LOG_ERRORD((" could not read the connect request, closing connection with %d", response));
client_done = true;
sprintf(buffers[s2].buffer, "HTTP/1.1 %d ERROR\r\nConnection: close\r\n\r\n", response);
buffers[s2].buffertail = buffers[s2].buffer + strlen(buffers[s2].buffer);
break;
}
strcpy(buffers[s2].buffer, "HTTP/1.1 200 Connected\r\nConnection: keep-alive\r\n\r\n");
buffers[s2].buffertail = buffers[s2].buffer + strlen(buffers[s2].buffer);
LOG_DEBUG((" accepted CONNECT request, connected to the server, sending OK to the client\n"));
// Send the response to the client socket
break;
} // end of CONNECT handling
@ -1106,6 +1126,12 @@ int processConfigLine(char* configLine)
LOG_ERROR(("Internal, could not create hash table\n"));
return 1;
}
server.host_redir_table = PL_NewHashTable(0, PL_HashString, PL_CompareStrings, PL_CompareStrings, NULL, NULL);
if (!server.host_redir_table)
{
LOG_ERROR(("Internal, could not create hash table\n"));
return 1;
}
servers.push_back(server);
}
@ -1172,6 +1198,51 @@ int processConfigLine(char* configLine)
return 0;
}
if (!strcmp(keyword, "redirhost"))
{
char* hostname = strtok2(_caret, ":", &_caret);
char* hostportstring = strtok2(_caret, ":", &_caret);
char* serverportstring = strtok2(_caret, ":", &_caret);
int port = atoi(serverportstring);
if (port <= 0) {
LOG_ERROR(("Invalid port specified: %s\n", serverportstring));
return 1;
}
if (server_info_t* existingServer = findServerInfo(port))
{
char* redirhoststring = strtok2(_caret, ":", &_caret);
any_host_spec_config = true;
char *hostname_copy = new char[strlen(hostname)+strlen(hostportstring)+2];
if (!hostname_copy) {
LOG_ERROR(("Out of memory"));
return 1;
}
strcpy(hostname_copy, hostname);
strcat(hostname_copy, ":");
strcat(hostname_copy, hostportstring);
char *redir_copy = new char[strlen(redirhoststring)+1];
strcpy(redir_copy, redirhoststring);
PLHashEntry* entry = PL_HashTableAdd(existingServer->host_redir_table, hostname_copy, redir_copy);
if (!entry) {
LOG_ERROR(("Out of memory"));
return 1;
}
}
else
{
LOG_ERROR(("Server on port %d for redirhost option is not defined, use 'listen' option first", port));
return 1;
}
return 0;
}
// Configure the NSS certificate database directory
if (!strcmp(keyword, "certdbdir"))
{
@ -1232,6 +1303,13 @@ PRIntn freeHostCertHashItems(PLHashEntry *he, PRIntn i, void *arg)
return HT_ENUMERATE_REMOVE;
}
PRIntn freeHostRedirHashItems(PLHashEntry *he, PRIntn i, void *arg)
{
delete [] (char*)he->key;
delete [] (char*)he->value;
return HT_ENUMERATE_REMOVE;
}
PRIntn freeClientAuthHashItems(PLHashEntry *he, PRIntn i, void *arg)
{
delete [] (char*)he->key;
@ -1370,8 +1448,10 @@ int main(int argc, char** argv)
{
PL_HashTableEnumerateEntries(it->host_cert_table, freeHostCertHashItems, NULL);
PL_HashTableEnumerateEntries(it->host_clientauth_table, freeClientAuthHashItems, NULL);
PL_HashTableEnumerateEntries(it->host_redir_table, freeHostRedirHashItems, NULL);
PL_HashTableDestroy(it->host_cert_table);
PL_HashTableDestroy(it->host_clientauth_table);
PL_HashTableDestroy(it->host_redir_table);
}
PR_Cleanup();