Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | function handleRequest(request, response) |
michael@0 | 2 | { |
michael@0 | 3 | try { |
michael@0 | 4 | reallyHandleRequest(request, response); |
michael@0 | 5 | } catch (e) { |
michael@0 | 6 | response.setStatusLine("1.0", 200, "AlmostOK"); |
michael@0 | 7 | response.write("Error handling request: " + e); |
michael@0 | 8 | } |
michael@0 | 9 | } |
michael@0 | 10 | |
michael@0 | 11 | |
michael@0 | 12 | function reallyHandleRequest(request, response) { |
michael@0 | 13 | var match; |
michael@0 | 14 | var requestAuth = true, requestProxyAuth = true; |
michael@0 | 15 | |
michael@0 | 16 | // Allow the caller to drive how authentication is processed via the query. |
michael@0 | 17 | // Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar |
michael@0 | 18 | // The extra ? allows the user/pass/realm checks to succeed if the name is |
michael@0 | 19 | // at the beginning of the query string. |
michael@0 | 20 | var query = "?" + request.queryString; |
michael@0 | 21 | |
michael@0 | 22 | var expected_user = "", expected_pass = "", realm = "mochitest"; |
michael@0 | 23 | var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy"; |
michael@0 | 24 | var huge = false, plugin = false, anonymous = false; |
michael@0 | 25 | var authHeaderCount = 1; |
michael@0 | 26 | // user=xxx |
michael@0 | 27 | match = /[^_]user=([^&]*)/.exec(query); |
michael@0 | 28 | if (match) |
michael@0 | 29 | expected_user = match[1]; |
michael@0 | 30 | |
michael@0 | 31 | // pass=xxx |
michael@0 | 32 | match = /[^_]pass=([^&]*)/.exec(query); |
michael@0 | 33 | if (match) |
michael@0 | 34 | expected_pass = match[1]; |
michael@0 | 35 | |
michael@0 | 36 | // realm=xxx |
michael@0 | 37 | match = /[^_]realm=([^&]*)/.exec(query); |
michael@0 | 38 | if (match) |
michael@0 | 39 | realm = match[1]; |
michael@0 | 40 | |
michael@0 | 41 | // proxy_user=xxx |
michael@0 | 42 | match = /proxy_user=([^&]*)/.exec(query); |
michael@0 | 43 | if (match) |
michael@0 | 44 | proxy_expected_user = match[1]; |
michael@0 | 45 | |
michael@0 | 46 | // proxy_pass=xxx |
michael@0 | 47 | match = /proxy_pass=([^&]*)/.exec(query); |
michael@0 | 48 | if (match) |
michael@0 | 49 | proxy_expected_pass = match[1]; |
michael@0 | 50 | |
michael@0 | 51 | // proxy_realm=xxx |
michael@0 | 52 | match = /proxy_realm=([^&]*)/.exec(query); |
michael@0 | 53 | if (match) |
michael@0 | 54 | proxy_realm = match[1]; |
michael@0 | 55 | |
michael@0 | 56 | // huge=1 |
michael@0 | 57 | match = /huge=1/.exec(query); |
michael@0 | 58 | if (match) |
michael@0 | 59 | huge = true; |
michael@0 | 60 | |
michael@0 | 61 | // plugin=1 |
michael@0 | 62 | match = /plugin=1/.exec(query); |
michael@0 | 63 | if (match) |
michael@0 | 64 | plugin = true; |
michael@0 | 65 | |
michael@0 | 66 | // multiple=1 |
michael@0 | 67 | match = /multiple=([^&]*)/.exec(query); |
michael@0 | 68 | if (match) |
michael@0 | 69 | authHeaderCount = match[1]+0; |
michael@0 | 70 | |
michael@0 | 71 | // anonymous=1 |
michael@0 | 72 | match = /anonymous=1/.exec(query); |
michael@0 | 73 | if (match) |
michael@0 | 74 | anonymous = true; |
michael@0 | 75 | |
michael@0 | 76 | // Look for an authentication header, if any, in the request. |
michael@0 | 77 | // |
michael@0 | 78 | // EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
michael@0 | 79 | // |
michael@0 | 80 | // This test only supports Basic auth. The value sent by the client is |
michael@0 | 81 | // "username:password", obscured with base64 encoding. |
michael@0 | 82 | |
michael@0 | 83 | var actual_user = "", actual_pass = "", authHeader, authPresent = false; |
michael@0 | 84 | if (request.hasHeader("Authorization")) { |
michael@0 | 85 | authPresent = true; |
michael@0 | 86 | authHeader = request.getHeader("Authorization"); |
michael@0 | 87 | match = /Basic (.+)/.exec(authHeader); |
michael@0 | 88 | if (match.length != 2) |
michael@0 | 89 | throw "Couldn't parse auth header: " + authHeader; |
michael@0 | 90 | |
michael@0 | 91 | var userpass = base64ToString(match[1]); // no atob() :-( |
michael@0 | 92 | match = /(.*):(.*)/.exec(userpass); |
michael@0 | 93 | if (match.length != 3) |
michael@0 | 94 | throw "Couldn't decode auth header: " + userpass; |
michael@0 | 95 | actual_user = match[1]; |
michael@0 | 96 | actual_pass = match[2]; |
michael@0 | 97 | } |
michael@0 | 98 | |
michael@0 | 99 | var proxy_actual_user = "", proxy_actual_pass = ""; |
michael@0 | 100 | if (request.hasHeader("Proxy-Authorization")) { |
michael@0 | 101 | authHeader = request.getHeader("Proxy-Authorization"); |
michael@0 | 102 | match = /Basic (.+)/.exec(authHeader); |
michael@0 | 103 | if (match.length != 2) |
michael@0 | 104 | throw "Couldn't parse auth header: " + authHeader; |
michael@0 | 105 | |
michael@0 | 106 | var userpass = base64ToString(match[1]); // no atob() :-( |
michael@0 | 107 | match = /(.*):(.*)/.exec(userpass); |
michael@0 | 108 | if (match.length != 3) |
michael@0 | 109 | throw "Couldn't decode auth header: " + userpass; |
michael@0 | 110 | proxy_actual_user = match[1]; |
michael@0 | 111 | proxy_actual_pass = match[2]; |
michael@0 | 112 | } |
michael@0 | 113 | |
michael@0 | 114 | // Don't request authentication if the credentials we got were what we |
michael@0 | 115 | // expected. |
michael@0 | 116 | if (expected_user == actual_user && |
michael@0 | 117 | expected_pass == actual_pass) { |
michael@0 | 118 | requestAuth = false; |
michael@0 | 119 | } |
michael@0 | 120 | if (proxy_expected_user == proxy_actual_user && |
michael@0 | 121 | proxy_expected_pass == proxy_actual_pass) { |
michael@0 | 122 | requestProxyAuth = false; |
michael@0 | 123 | } |
michael@0 | 124 | |
michael@0 | 125 | if (anonymous) { |
michael@0 | 126 | if (authPresent) { |
michael@0 | 127 | response.setStatusLine("1.0", 400, "Unexpected authorization header found"); |
michael@0 | 128 | } else { |
michael@0 | 129 | response.setStatusLine("1.0", 200, "Authorization header not found"); |
michael@0 | 130 | } |
michael@0 | 131 | } else { |
michael@0 | 132 | if (requestProxyAuth) { |
michael@0 | 133 | response.setStatusLine("1.0", 407, "Proxy authentication required"); |
michael@0 | 134 | for (i = 0; i < authHeaderCount; ++i) |
michael@0 | 135 | response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true); |
michael@0 | 136 | } else if (requestAuth) { |
michael@0 | 137 | response.setStatusLine("1.0", 401, "Authentication required"); |
michael@0 | 138 | for (i = 0; i < authHeaderCount; ++i) |
michael@0 | 139 | response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true); |
michael@0 | 140 | } else { |
michael@0 | 141 | response.setStatusLine("1.0", 200, "OK"); |
michael@0 | 142 | } |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | response.setHeader("Content-Type", "application/xhtml+xml", false); |
michael@0 | 146 | response.write("<html xmlns='http://www.w3.org/1999/xhtml'>"); |
michael@0 | 147 | response.write("<p>Login: <span id='ok'>" + (requestAuth ? "FAIL" : "PASS") + "</span></p>\n"); |
michael@0 | 148 | response.write("<p>Proxy: <span id='proxy'>" + (requestProxyAuth ? "FAIL" : "PASS") + "</span></p>\n"); |
michael@0 | 149 | response.write("<p>Auth: <span id='auth'>" + authHeader + "</span></p>\n"); |
michael@0 | 150 | response.write("<p>User: <span id='user'>" + actual_user + "</span></p>\n"); |
michael@0 | 151 | response.write("<p>Pass: <span id='pass'>" + actual_pass + "</span></p>\n"); |
michael@0 | 152 | |
michael@0 | 153 | if (huge) { |
michael@0 | 154 | response.write("<div style='display: none'>"); |
michael@0 | 155 | for (i = 0; i < 100000; i++) { |
michael@0 | 156 | response.write("123456789\n"); |
michael@0 | 157 | } |
michael@0 | 158 | response.write("</div>"); |
michael@0 | 159 | response.write("<span id='footnote'>This is a footnote after the huge content fill</span>"); |
michael@0 | 160 | } |
michael@0 | 161 | |
michael@0 | 162 | if (plugin) { |
michael@0 | 163 | response.write("<embed id='embedtest' style='width: 400px; height: 100px;' " + |
michael@0 | 164 | "type='application/x-test'></embed>\n"); |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | response.write("</html>"); |
michael@0 | 168 | } |
michael@0 | 169 | |
michael@0 | 170 | |
michael@0 | 171 | // base64 decoder |
michael@0 | 172 | // |
michael@0 | 173 | // Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa() |
michael@0 | 174 | // doesn't seem to exist. :-( |
michael@0 | 175 | /* Convert Base64 data to a string */ |
michael@0 | 176 | const toBinaryTable = [ |
michael@0 | 177 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, |
michael@0 | 178 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, |
michael@0 | 179 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, |
michael@0 | 180 | 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, |
michael@0 | 181 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, |
michael@0 | 182 | 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, |
michael@0 | 183 | -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, |
michael@0 | 184 | 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 |
michael@0 | 185 | ]; |
michael@0 | 186 | const base64Pad = '='; |
michael@0 | 187 | |
michael@0 | 188 | function base64ToString(data) { |
michael@0 | 189 | |
michael@0 | 190 | var result = ''; |
michael@0 | 191 | var leftbits = 0; // number of bits decoded, but yet to be appended |
michael@0 | 192 | var leftdata = 0; // bits decoded, but yet to be appended |
michael@0 | 193 | |
michael@0 | 194 | // Convert one by one. |
michael@0 | 195 | for (var i = 0; i < data.length; i++) { |
michael@0 | 196 | var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; |
michael@0 | 197 | var padding = (data[i] == base64Pad); |
michael@0 | 198 | // Skip illegal characters and whitespace |
michael@0 | 199 | if (c == -1) continue; |
michael@0 | 200 | |
michael@0 | 201 | // Collect data into leftdata, update bitcount |
michael@0 | 202 | leftdata = (leftdata << 6) | c; |
michael@0 | 203 | leftbits += 6; |
michael@0 | 204 | |
michael@0 | 205 | // If we have 8 or more bits, append 8 bits to the result |
michael@0 | 206 | if (leftbits >= 8) { |
michael@0 | 207 | leftbits -= 8; |
michael@0 | 208 | // Append if not padding. |
michael@0 | 209 | if (!padding) |
michael@0 | 210 | result += String.fromCharCode((leftdata >> leftbits) & 0xff); |
michael@0 | 211 | leftdata &= (1 << leftbits) - 1; |
michael@0 | 212 | } |
michael@0 | 213 | } |
michael@0 | 214 | |
michael@0 | 215 | // If there are any bits left, the base64 string was corrupted |
michael@0 | 216 | if (leftbits) |
michael@0 | 217 | throw Components.Exception('Corrupted base64 string'); |
michael@0 | 218 | |
michael@0 | 219 | return result; |
michael@0 | 220 | } |