diff --git a/lib/verifier/certassertion.js b/lib/verifier/certassertion.js index 4e54eb943f5300be3e5c9dabaa74d1914f651b07..ac94d2ee659cfdf8879e125e9f70e7714563f242 100644 --- a/lib/verifier/certassertion.js +++ b/lib/verifier/certassertion.js @@ -145,36 +145,52 @@ function retrieveHostPublicKey(host, successCB, errorCB) { // *got* is what was provided by the RP, so depending on their implementation // it might be strangely formed. function compareAudiences(want, got) { + function normalizeParsedURL(u) { + if (!u.port) u.port = u.protocol === 'https:' ? 443 : 80; + return u; + } + try { - var checkHostOnly = false; - - // issue #82 - for a limited time, let's allow got to be sloppy and omit scheme - // in which case we guess a scheme based on port - if (!/^https?:\/\//.test(got)) { - var x = got.split(':'); - var scheme = "http"; - if (x.length === 2 && x[1] === '443') scheme = "https"; - got = scheme + "://" + got; - checkHostOnly = true; + var got_scheme, got_domain, got_port; + + // We allow the RP to provide audience in multiple forms (see issue #82). + // The RP SHOULD provide full origin, but we allow these alternate forms for + // some dude named Postel doesn't go postal. + // 1. full origin 'http://rp.tld' + // 1a. full origin with port 'http://rp.tld:8080' + // 2. domain and port 'rp.tld:8080' + // 3. domain only 'rp.tld' + + // case 1 & 1a + if (/^https?:\/\//.test(got)) { + var gu = normalizeParsedURL(url.parse(got)); + got_scheme = gu.protocol; + got_domain = gu.hostname; + got_port = gu.port; } - - // now parse and compare - function normalizeParsedURL(u) { - if (!u.port) u.port = u.protocol === 'https:' ? 443 : 80; - return u; + // case 2 + else if (got.indexOf(':') != -1) { + var p = got.split(':'); + if (p.length !== 2) throw "malformed domain"; + got_domain = p[0]; + got_port = p[1]; + } + // case 3 + else { + got_domain = got; } + // now parse "want" url want = normalizeParsedURL(url.parse(want)); - got = normalizeParsedURL(url.parse(got)); - - if (checkHostOnly) return want.hostname === got.hostname; + // compare the parts explicitly provided by the client + if (got_scheme && got_scheme != want.protocol) throw "scheme mismatch" + if (got_port && got_port != want.port) throw "port mismatch" + if (got_domain && got_domain != want.hostname) throw "domain mismatch" - return (want.protocol === got.protocol && - want.hostname === got.hostname && - want.port == got.port); + return undefined; } catch(e) { - return false; + return e.toString(); } } @@ -211,10 +227,11 @@ function verify(assertion, audience, successCB, errorCB, pkRetriever) { tok.parse(bundle.assertion); // audience must match! - if (!compareAudiences(tok.audience, audience)) { + var err = compareAudiences(tok.audience, audience) + if (err) { logger.debug("verification failure, audience mismatch: '" - + tok.audience + "' != '" + audience + "'"); - return errorCB("audience mismatch"); + + tok.audience + "' != '" + audience + "': " + err); + return errorCB("audience mismatch: " + err); } if (tok.verify(pk)) { diff --git a/tests/verifier-test.js b/tests/verifier-test.js index 4854fc11851df6be08570b7badee3cca765c5dd2..5943e3a3471ab21531da4729ab66bda854d5fb2d 100755 --- a/tests/verifier-test.js +++ b/tests/verifier-test.js @@ -198,7 +198,7 @@ suite.addBatch({ "fails with a nice error": function(r, err) { var resp = JSON.parse(r.body); assert.strictEqual(resp.status, 'failure'); - assert.strictEqual(resp.reason, 'audience mismatch'); + assert.strictEqual(resp.reason, 'audience mismatch: domain mismatch'); } }, "but specifying the wrong port": { @@ -211,7 +211,7 @@ suite.addBatch({ "fails with a nice error": function(r, err) { var resp = JSON.parse(r.body); assert.strictEqual(resp.status, 'failure'); - assert.strictEqual(resp.reason, 'audience mismatch'); + assert.strictEqual(resp.reason, 'audience mismatch: port mismatch'); } }, "but specifying the wrong scheme": { @@ -224,7 +224,7 @@ suite.addBatch({ "fails with a nice error": function(r, err) { var resp = JSON.parse(r.body); assert.strictEqual(resp.status, 'failure'); - assert.strictEqual(resp.reason, 'audience mismatch'); + assert.strictEqual(resp.reason, 'audience mismatch: scheme mismatch'); } }, "and providing just a domain and port": { @@ -256,7 +256,7 @@ suite.addBatch({ "fails as you would expect": function(r, err) { var resp = JSON.parse(r.body); assert.strictEqual(resp.status, 'failure'); - assert.strictEqual(resp.reason, 'audience mismatch'); + assert.strictEqual(resp.reason, 'audience mismatch: port mismatch'); } }, "leaving off the audience": {