diff --git a/netwerk/protocol/http/src/nsHttpBasicAuth.cpp b/netwerk/protocol/http/src/nsHttpBasicAuth.cpp --- a/netwerk/protocol/http/src/nsHttpBasicAuth.cpp +++ b/netwerk/protocol/http/src/nsHttpBasicAuth.cpp @@ -98,22 +98,22 @@ nsHttpBasicAuth::GenerateCredentials(nsI LOG(("nsHttpBasicAuth::GenerateCredentials [challenge=%s]\n", challenge)); NS_ENSURE_ARG_POINTER(creds); // we only know how to deal with Basic auth for http. PRBool isBasicAuth = !PL_strncasecmp(challenge, "basic", 5); NS_ENSURE_TRUE(isBasicAuth, NS_ERROR_UNEXPECTED); - // we work with ASCII around here + // convert user and pass in UTF8 (see bug 41489) nsCAutoString userpass; - LossyCopyUTF16toASCII(user, userpass); + CopyUTF16toUTF8(user, userpass); userpass.Append(':'); // always send a ':' (see bug 129565) if (password) - LossyAppendUTF16toASCII(password, userpass); + AppendUTF16toUTF8(password, userpass); // plbase64.h provides this worst-case output buffer size calculation. // use calloc, since PL_Base64Encode does not null terminate. *creds = (char *) calloc(6 + ((userpass.Length() + 2)/3)*4 + 1, 1); if (!*creds) return NS_ERROR_OUT_OF_MEMORY; memcpy(*creds, "Basic ", 6); diff --git a/netwerk/test/unit/test_authentication.js b/netwerk/test/unit/test_authentication.js --- a/netwerk/test/unit/test_authentication.js +++ b/netwerk/test/unit/test_authentication.js @@ -4,23 +4,23 @@ do_load_httpd_js(); const FLAG_RETURN_FALSE = 1 << 0; const FLAG_WRONG_PASSWORD = 1 << 1; const nsIAuthPrompt2 = Components.interfaces.nsIAuthPrompt2; const nsIAuthInformation = Components.interfaces.nsIAuthInformation; -function AuthPrompt1(flags) { +function AuthPrompt1(flags, user, pass) { this.flags = flags; + this.user = user || "guest"; + this.pass = pass || "guest"; } AuthPrompt1.prototype = { - user: "guest", - pass: "guest", expectedRealm: "secret", QueryInterface: function authprompt_qi(iid) { if (iid.equals(Components.interfaces.nsISupports) || iid.equals(Components.interfaces.nsIAuthPrompt)) return this; throw Components.results.NS_ERROR_NO_INTERFACE; @@ -59,23 +59,23 @@ AuthPrompt1.prototype = { }, promptPassword: function ap1_promptPW(title, text, realm, save, pwd) { do_throw("unexpected promptPassword call"); } }; -function AuthPrompt2(flags) { +function AuthPrompt2(flags, user, pass) { this.flags = flags; + this.user = user || "guest"; + this.pass = pass || "guest"; } AuthPrompt2.prototype = { - user: "guest", - pass: "guest", expectedRealm: "secret", QueryInterface: function authprompt2_qi(iid) { if (iid.equals(Components.interfaces.nsISupports) || iid.equals(Components.interfaces.nsIAuthPrompt2)) return this; throw Components.results.NS_ERROR_NO_INTERFACE; @@ -126,45 +126,44 @@ AuthPrompt2.prototype = { return true; }, asyncPromptAuth: function ap2_async(chan, cb, ctx, lvl, info) { throw 0x80004001; } }; -function Requestor(flags, versions, username) { +function Requestor(flags, versions, user, pass) { this.flags = flags; this.versions = versions; - this.username = username; + this.user = user; + this.pass = pass; } Requestor.prototype = { QueryInterface: function requestor_qi(iid) { if (iid.equals(Components.interfaces.nsISupports) || iid.equals(Components.interfaces.nsIInterfaceRequestor)) return this; throw Components.results.NS_ERROR_NO_INTERFACE; }, getInterface: function requestor_gi(iid) { if (this.versions & 1 && iid.equals(Components.interfaces.nsIAuthPrompt)) { // Allow the prompt to store state by caching it here if (!this.prompt1) - this.prompt1 = new AuthPrompt1(this.flags); + this.prompt1 = new AuthPrompt1(this.flags, this.user, this.pass); return this.prompt1; } if (this.versions & 2 && iid.equals(Components.interfaces.nsIAuthPrompt2)) { // Allow the prompt to store state by caching it here if (!this.prompt2) - this.prompt2 = new AuthPrompt2(this.flags); - if (this.username) - this.prompt2.user = this.username; + this.prompt2 = new AuthPrompt2(this.flags, this.user, this.pass); return this.prompt2; } throw Components.results.NS_ERROR_NO_INTERFACE; }, prompt1: null, prompt2: null @@ -252,26 +251,27 @@ function makeChan(url) { .QueryInterface(Components.interfaces.nsIHttpChannel); return chan; } var tests = [test_noauth, test_returnfalse1, test_wrongpw1, test_prompt1, test_returnfalse2, test_wrongpw2, test_prompt2, test_ntlm, test_auth, test_digest_noauth, test_digest, - test_digest_bogus_user]; + test_digest_bogus_user, test_basic_utf8]; var current_test = 0; var httpserv = null; function run_test() { httpserv = new nsHttpServer(); httpserv.registerPathHandler("/auth", authHandler); + httpserv.registerPathHandler("/auth/utf8", basicUTF8Handler); httpserv.registerPathHandler("/auth/ntlm/simple", authNtlmSimple); httpserv.registerPathHandler("/auth/realm", authRealm); httpserv.registerPathHandler("/auth/digest", authDigest); httpserv.start(4444); tests[0](); } @@ -360,16 +360,28 @@ function test_auth() { chan.notificationCallbacks = new RealmTestRequestor(); listener.expectedCode = 401; // Unauthorized chan.asyncOpen(listener, null); do_test_pending(); } +function test_basic_utf8() { + var chan = makeChan("http://localhost:4444/auth/utf8"); + + // big:airplane in Japanese, according to google translate + chan.notificationCallbacks = new Requestor(0, 1, "\u5927\u304d\u3044", "\u98db\u884c\u6a5f"); + listener.expectedCode = 200; // Unauthorized + chan.asyncOpen(listener, null); + + do_test_pending(); +} + + function test_digest_noauth() { var chan = makeChan("http://localhost:4444/auth/digest"); //chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 2); listener.expectedCode = 401; // Unauthorized chan.asyncOpen(listener, null); do_test_pending(); @@ -391,21 +403,17 @@ function test_digest_bogus_user() { listener.expectedCode = 401; // unauthorized chan.asyncOpen(listener, null); do_test_pending(); } // PATH HANDLERS -// /auth -function authHandler(metadata, response) { - // btoa("guest:guest"), but that function is not available here - var expectedHeader = "Basic Z3Vlc3Q6Z3Vlc3Q="; - +function sendBasicResponse(metadata, response, expectedHeader) { var body; if (metadata.hasHeader("Authorization") && metadata.getHeader("Authorization") == expectedHeader) { response.setStatusLine(metadata.httpVersion, 200, "OK, authorized"); response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false); body = "success"; @@ -417,16 +425,30 @@ function authHandler(metadata, response) response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false); body = "failed"; } response.bodyOutputStream.write(body, body.length); } +// /auth +function authHandler(metadata, response) { + // btoa("guest:guest"), but that function is not available here + var expectedHeader = "Basic Z3Vlc3Q6Z3Vlc3Q="; + sendBasicResponse(metadata, response, expectedHeader); +} + +// /auth/utf8 +function basicUTF8Handler(metadata, response) { + // btoa("\u5927\u304d\u3044:\u98db\u884c\u6a5f"), but that function is not available here + var expectedHeader = "Basic 5aSn44GN44GEOumjm+ihjOapnw=="; + sendBasicResponse(metadata, response, expectedHeader); +} + // /auth/ntlm/simple function authNtlmSimple(metadata, response) { response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); response.setHeader("WWW-Authenticate", "NTLM" /* + ' realm="secret"' */, false); var body = "NOTE: This just sends an NTLM challenge, it never\n" + "accepts the authentication. It also closes\n" + "the connection after sending the challenge\n";