Attachment #420886: Convert basic credentials to UTF-8 prior to base64 encoding them for bug #41489

View | Details | Raw Unified | Return to bug 41489
Collapse All | Expand All

(-)a/netwerk/protocol/http/src/nsHttpBasicAuth.cpp (-3 / +3 lines)
Line     Link Here 
 Lines 98-119   nsHttpBasicAuth::GenerateCredentials(nsI Link Here 
98
    LOG(("nsHttpBasicAuth::GenerateCredentials [challenge=%s]\n", challenge));
98
    LOG(("nsHttpBasicAuth::GenerateCredentials [challenge=%s]\n", challenge));
99
99
100
    NS_ENSURE_ARG_POINTER(creds);
100
    NS_ENSURE_ARG_POINTER(creds);
101
101
102
    // we only know how to deal with Basic auth for http.
102
    // we only know how to deal with Basic auth for http.
103
    PRBool isBasicAuth = !PL_strncasecmp(challenge, "basic", 5);
103
    PRBool isBasicAuth = !PL_strncasecmp(challenge, "basic", 5);
104
    NS_ENSURE_TRUE(isBasicAuth, NS_ERROR_UNEXPECTED);
104
    NS_ENSURE_TRUE(isBasicAuth, NS_ERROR_UNEXPECTED);
105
105
106
    // we work with ASCII around here
106
    // convert user and pass in UTF8 (see bug 41489)
107
    nsCAutoString userpass;
107
    nsCAutoString userpass;
108
    LossyCopyUTF16toASCII(user, userpass);
108
    CopyUTF16toUTF8(user, userpass);
109
    userpass.Append(':'); // always send a ':' (see bug 129565)
109
    userpass.Append(':'); // always send a ':' (see bug 129565)
110
    if (password)
110
    if (password)
111
        LossyAppendUTF16toASCII(password, userpass);
111
        AppendUTF16toUTF8(password, userpass);
112
112
113
    // plbase64.h provides this worst-case output buffer size calculation.
113
    // plbase64.h provides this worst-case output buffer size calculation.
114
    // use calloc, since PL_Base64Encode does not null terminate.
114
    // use calloc, since PL_Base64Encode does not null terminate.
115
    *creds = (char *) calloc(6 + ((userpass.Length() + 2)/3)*4 + 1, 1);
115
    *creds = (char *) calloc(6 + ((userpass.Length() + 2)/3)*4 + 1, 1);
116
    if (!*creds)
116
    if (!*creds)
117
        return NS_ERROR_OUT_OF_MEMORY;
117
        return NS_ERROR_OUT_OF_MEMORY;
118
118
119
    memcpy(*creds, "Basic ", 6);
119
    memcpy(*creds, "Basic ", 6);
(-)a/netwerk/test/unit/test_authentication.js (-18 / +40 lines)
Line     Link Here 
 Lines 4-26   do_load_httpd_js(); Link Here 
4
4
5
const FLAG_RETURN_FALSE   = 1 << 0;
5
const FLAG_RETURN_FALSE   = 1 << 0;
6
const FLAG_WRONG_PASSWORD = 1 << 1;
6
const FLAG_WRONG_PASSWORD = 1 << 1;
7
7
8
const nsIAuthPrompt2 = Components.interfaces.nsIAuthPrompt2;
8
const nsIAuthPrompt2 = Components.interfaces.nsIAuthPrompt2;
9
const nsIAuthInformation = Components.interfaces.nsIAuthInformation;
9
const nsIAuthInformation = Components.interfaces.nsIAuthInformation;
10
10
11
11
12
function AuthPrompt1(flags) {
12
function AuthPrompt1(flags, user, pass) {
13
  this.flags = flags;
13
  this.flags = flags;
14
  this.user = user || "guest";
15
  this.pass = pass || "guest";
14
}
16
}
15
17
16
AuthPrompt1.prototype = {
18
AuthPrompt1.prototype = {
17
  user: "guest",
18
  pass: "guest",
19
19
20
  expectedRealm: "secret",
20
  expectedRealm: "secret",
21
21
22
  QueryInterface: function authprompt_qi(iid) {
22
  QueryInterface: function authprompt_qi(iid) {
23
    if (iid.equals(Components.interfaces.nsISupports) ||
23
    if (iid.equals(Components.interfaces.nsISupports) ||
24
        iid.equals(Components.interfaces.nsIAuthPrompt))
24
        iid.equals(Components.interfaces.nsIAuthPrompt))
25
      return this;
25
      return this;
26
    throw Components.results.NS_ERROR_NO_INTERFACE;
26
    throw Components.results.NS_ERROR_NO_INTERFACE;
 Lines 59-81   AuthPrompt1.prototype = { Link Here 
59
  },
59
  },
60
60
61
  promptPassword: function ap1_promptPW(title, text, realm, save, pwd) {
61
  promptPassword: function ap1_promptPW(title, text, realm, save, pwd) {
62
    do_throw("unexpected promptPassword call");
62
    do_throw("unexpected promptPassword call");
63
  }
63
  }
64
64
65
};
65
};
66
66
67
function AuthPrompt2(flags) {
67
function AuthPrompt2(flags, user, pass) {
68
  this.flags = flags;
68
  this.flags = flags;
69
  this.user = user || "guest";
70
  this.pass = pass || "guest";
69
}
71
}
70
72
71
AuthPrompt2.prototype = {
73
AuthPrompt2.prototype = {
72
  user: "guest",
73
  pass: "guest",
74
74
75
  expectedRealm: "secret",
75
  expectedRealm: "secret",
76
76
77
  QueryInterface: function authprompt2_qi(iid) {
77
  QueryInterface: function authprompt2_qi(iid) {
78
    if (iid.equals(Components.interfaces.nsISupports) ||
78
    if (iid.equals(Components.interfaces.nsISupports) ||
79
        iid.equals(Components.interfaces.nsIAuthPrompt2))
79
        iid.equals(Components.interfaces.nsIAuthPrompt2))
80
      return this;
80
      return this;
81
    throw Components.results.NS_ERROR_NO_INTERFACE;
81
    throw Components.results.NS_ERROR_NO_INTERFACE;
 Lines 126-170   AuthPrompt2.prototype = { Link Here 
126
    return true;
126
    return true;
127
  },
127
  },
128
128
129
  asyncPromptAuth: function ap2_async(chan, cb, ctx, lvl, info) {
129
  asyncPromptAuth: function ap2_async(chan, cb, ctx, lvl, info) {
130
    throw 0x80004001;
130
    throw 0x80004001;
131
  }
131
  }
132
};
132
};
133
133
134
function Requestor(flags, versions, username) {
134
function Requestor(flags, versions, user, pass) {
135
  this.flags = flags;
135
  this.flags = flags;
136
  this.versions = versions;
136
  this.versions = versions;
137
  this.username = username;
137
  this.user = user;
138
  this.pass = pass;
138
}
139
}
139
140
140
Requestor.prototype = {
141
Requestor.prototype = {
141
  QueryInterface: function requestor_qi(iid) {
142
  QueryInterface: function requestor_qi(iid) {
142
    if (iid.equals(Components.interfaces.nsISupports) ||
143
    if (iid.equals(Components.interfaces.nsISupports) ||
143
        iid.equals(Components.interfaces.nsIInterfaceRequestor))
144
        iid.equals(Components.interfaces.nsIInterfaceRequestor))
144
      return this;
145
      return this;
145
    throw Components.results.NS_ERROR_NO_INTERFACE;
146
    throw Components.results.NS_ERROR_NO_INTERFACE;
146
  },
147
  },
147
148
148
  getInterface: function requestor_gi(iid) {
149
  getInterface: function requestor_gi(iid) {
149
    if (this.versions & 1 &&
150
    if (this.versions & 1 &&
150
        iid.equals(Components.interfaces.nsIAuthPrompt)) {
151
        iid.equals(Components.interfaces.nsIAuthPrompt)) {
151
      // Allow the prompt to store state by caching it here
152
      // Allow the prompt to store state by caching it here
152
      if (!this.prompt1)
153
      if (!this.prompt1)
153
        this.prompt1 = new AuthPrompt1(this.flags); 
154
        this.prompt1 = new AuthPrompt1(this.flags, this.user, this.pass); 
154
      return this.prompt1;
155
      return this.prompt1;
155
    }
156
    }
156
    if (this.versions & 2 &&
157
    if (this.versions & 2 &&
157
        iid.equals(Components.interfaces.nsIAuthPrompt2)) {
158
        iid.equals(Components.interfaces.nsIAuthPrompt2)) {
158
      // Allow the prompt to store state by caching it here
159
      // Allow the prompt to store state by caching it here
159
      if (!this.prompt2)
160
      if (!this.prompt2)
160
        this.prompt2 = new AuthPrompt2(this.flags);
161
        this.prompt2 = new AuthPrompt2(this.flags, this.user, this.pass);
161
      if (this.username)
162
        this.prompt2.user = this.username;
163
      return this.prompt2;
162
      return this.prompt2;
164
    }
163
    }
165
164
166
    throw Components.results.NS_ERROR_NO_INTERFACE;
165
    throw Components.results.NS_ERROR_NO_INTERFACE;
167
  },
166
  },
168
167
169
  prompt1: null,
168
  prompt1: null,
170
  prompt2: null
169
  prompt2: null
 Lines 252-277   function makeChan(url) { Link Here 
252
                .QueryInterface(Components.interfaces.nsIHttpChannel);
251
                .QueryInterface(Components.interfaces.nsIHttpChannel);
253
252
254
  return chan;
253
  return chan;
255
}
254
}
256
255
257
var tests = [test_noauth, test_returnfalse1, test_wrongpw1, test_prompt1,
256
var tests = [test_noauth, test_returnfalse1, test_wrongpw1, test_prompt1,
258
             test_returnfalse2, test_wrongpw2, test_prompt2, test_ntlm,
257
             test_returnfalse2, test_wrongpw2, test_prompt2, test_ntlm,
259
             test_auth, test_digest_noauth, test_digest,
258
             test_auth, test_digest_noauth, test_digest,
260
             test_digest_bogus_user];
259
             test_digest_bogus_user, test_basic_utf8];
261
260
262
var current_test = 0;
261
var current_test = 0;
263
262
264
var httpserv = null;
263
var httpserv = null;
265
264
266
function run_test() {
265
function run_test() {
267
  httpserv = new nsHttpServer();
266
  httpserv = new nsHttpServer();
268
267
269
  httpserv.registerPathHandler("/auth", authHandler);
268
  httpserv.registerPathHandler("/auth", authHandler);
269
  httpserv.registerPathHandler("/auth/utf8", basicUTF8Handler);
270
  httpserv.registerPathHandler("/auth/ntlm/simple", authNtlmSimple);
270
  httpserv.registerPathHandler("/auth/ntlm/simple", authNtlmSimple);
271
  httpserv.registerPathHandler("/auth/realm", authRealm);
271
  httpserv.registerPathHandler("/auth/realm", authRealm);
272
  httpserv.registerPathHandler("/auth/digest", authDigest);
272
  httpserv.registerPathHandler("/auth/digest", authDigest);
273
273
274
  httpserv.start(4444);
274
  httpserv.start(4444);
275
275
276
  tests[0]();
276
  tests[0]();
277
}
277
}
 Lines 360-375   function test_auth() { Link Here 
360
360
361
  chan.notificationCallbacks = new RealmTestRequestor();
361
  chan.notificationCallbacks = new RealmTestRequestor();
362
  listener.expectedCode = 401; // Unauthorized
362
  listener.expectedCode = 401; // Unauthorized
363
  chan.asyncOpen(listener, null);
363
  chan.asyncOpen(listener, null);
364
364
365
  do_test_pending();
365
  do_test_pending();
366
}
366
}
367
367
368
function test_basic_utf8() {
369
  var chan = makeChan("http://localhost:4444/auth/utf8");
370
371
  // big:airplane in Japanese, according to google translate
372
  chan.notificationCallbacks = new Requestor(0, 1, "\u5927\u304d\u3044", "\u98db\u884c\u6a5f");
373
  listener.expectedCode = 200; // Unauthorized
374
  chan.asyncOpen(listener, null);
375
376
  do_test_pending();
377
}
378
379
368
function test_digest_noauth() {
380
function test_digest_noauth() {
369
  var chan = makeChan("http://localhost:4444/auth/digest");
381
  var chan = makeChan("http://localhost:4444/auth/digest");
370
382
371
  //chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 2);
383
  //chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 2);
372
  listener.expectedCode = 401; // Unauthorized
384
  listener.expectedCode = 401; // Unauthorized
373
  chan.asyncOpen(listener, null);
385
  chan.asyncOpen(listener, null);
374
386
375
  do_test_pending();
387
  do_test_pending();
 Lines 391-411   function test_digest_bogus_user() { Link Here 
391
  listener.expectedCode = 401; // unauthorized
403
  listener.expectedCode = 401; // unauthorized
392
  chan.asyncOpen(listener, null);
404
  chan.asyncOpen(listener, null);
393
405
394
  do_test_pending();
406
  do_test_pending();
395
}
407
}
396
408
397
// PATH HANDLERS
409
// PATH HANDLERS
398
410
399
// /auth
411
function sendBasicResponse(metadata, response, expectedHeader) {
400
function authHandler(metadata, response) {
401
  // btoa("guest:guest"), but that function is not available here
402
  var expectedHeader = "Basic Z3Vlc3Q6Z3Vlc3Q=";
403
404
  var body;
412
  var body;
405
  if (metadata.hasHeader("Authorization") &&
413
  if (metadata.hasHeader("Authorization") &&
406
      metadata.getHeader("Authorization") == expectedHeader)
414
      metadata.getHeader("Authorization") == expectedHeader)
407
  {
415
  {
408
    response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
416
    response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
409
    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
417
    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
410
418
411
    body = "success";
419
    body = "success";
 Lines 417-432   function authHandler(metadata, response) Link Here 
417
    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
425
    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
418
426
419
    body = "failed";
427
    body = "failed";
420
  }
428
  }
421
429
422
  response.bodyOutputStream.write(body, body.length);
430
  response.bodyOutputStream.write(body, body.length);
423
}
431
}
424
432
433
// /auth
434
function authHandler(metadata, response) {
435
  // btoa("guest:guest"), but that function is not available here
436
  var expectedHeader = "Basic Z3Vlc3Q6Z3Vlc3Q=";
437
  sendBasicResponse(metadata, response, expectedHeader);
438
}
439
440
// /auth/utf8
441
function basicUTF8Handler(metadata, response) {
442
  // btoa("\u5927\u304d\u3044:\u98db\u884c\u6a5f"), but that function is not available here
443
  var expectedHeader = "Basic 5aSn44GN44GEOumjm+ihjOapnw==";
444
  sendBasicResponse(metadata, response, expectedHeader);
445
}
446
425
// /auth/ntlm/simple
447
// /auth/ntlm/simple
426
function authNtlmSimple(metadata, response) {
448
function authNtlmSimple(metadata, response) {
427
  response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
449
  response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
428
  response.setHeader("WWW-Authenticate", "NTLM" /* + ' realm="secret"' */, false);
450
  response.setHeader("WWW-Authenticate", "NTLM" /* + ' realm="secret"' */, false);
429
451
430
  var body = "NOTE: This just sends an NTLM challenge, it never\n" +
452
  var body = "NOTE: This just sends an NTLM challenge, it never\n" +
431
             "accepts the authentication. It also closes\n" +
453
             "accepts the authentication. It also closes\n" +
432
             "the connection after sending the challenge\n";
454
             "the connection after sending the challenge\n";

Return to bug 41489