15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/http/http_auth_handler_basic.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/base64.h"
107d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/strings/string_util.h"
11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_errors.h"
135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "net/base/net_string_util.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/http/http_auth.h"
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "net/http/http_auth_challenge_tokenizer.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net {
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Parses a realm from an auth challenge, and converts to UTF8-encoding.
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns whether the realm is invalid or the parameters are invalid.
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Note that if a realm was not specified, we will default it to "";
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// so specifying 'Basic realm=""' is equivalent to 'Basic'.
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This is more generous than RFC 2617, which is pretty clear in the
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// production of challenge that realm is required.
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// We allow it to be compatibility with certain embedded webservers that don't
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// include a realm (see http://crbug.com/20984.)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The over-the-wire realm is encoded as ISO-8859-1 (aka Latin-1).
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(cbentzel): Realm may need to be decoded using RFC 2047 rules as
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// well, see http://crbug.com/25790.
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool ParseRealm(const HttpAuthChallengeTokenizer& tokenizer,
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                std::string* realm) {
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(realm);
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  realm->clear();
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HttpUtil::NameValuePairsIterator parameters = tokenizer.param_pairs();
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (parameters.GetNext()) {
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!LowerCaseEqualsASCII(parameters.name(), "realm"))
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (!net::ConvertToUtf8AndNormalize(parameters.value(), kCharsetLatin1,
47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                        realm)) {
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return parameters.valid();
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool HttpAuthHandlerBasic::Init(HttpAuthChallengeTokenizer* challenge) {
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  auth_scheme_ = HttpAuth::AUTH_SCHEME_BASIC;
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  score_ = 1;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  properties_ = 0;
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ParseChallenge(challenge);
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool HttpAuthHandlerBasic::ParseChallenge(
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    HttpAuthChallengeTokenizer* challenge) {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Verify the challenge's auth-scheme.
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!LowerCaseEqualsASCII(challenge->scheme(), "basic"))
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string realm;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ParseRealm(*challenge, &realm))
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  realm_ = realm;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HttpAuth::AuthorizationResult HttpAuthHandlerBasic::HandleAnotherChallenge(
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    HttpAuthChallengeTokenizer* challenge) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Basic authentication is always a single round, so any responses
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // should be treated as a rejection.  However, if the new challenge
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // is for a different realm, then indicate the realm change.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string realm;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ParseRealm(*challenge, &realm))
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return HttpAuth::AUTHORIZATION_RESULT_INVALID;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (realm_ != realm)?
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM:
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HttpAuth::AUTHORIZATION_RESULT_REJECT;
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int HttpAuthHandlerBasic::GenerateAuthTokenImpl(
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const AuthCredentials* credentials, const HttpRequestInfo*,
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const CompletionCallback&, std::string* auth_token) {
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(credentials);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(eroman): is this the right encoding of username/password?
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string base64_username_password;
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::Base64Encode(base::UTF16ToUTF8(credentials->username()) + ":" +
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         base::UTF16ToUTF8(credentials->password()),
98a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                     &base64_username_password);
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *auth_token = "Basic " + base64_username_password;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HttpAuthHandlerBasic::Factory::Factory() {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HttpAuthHandlerBasic::Factory::~Factory() {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int HttpAuthHandlerBasic::Factory::CreateAuthHandler(
110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    HttpAuthChallengeTokenizer* challenge,
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HttpAuth::Target target,
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const GURL& origin,
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CreateReason reason,
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int digest_nonce_count,
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const BoundNetLog& net_log,
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_ptr<HttpAuthHandler>* handler) {
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(cbentzel): Move towards model of parsing in the factory
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //                 method and only constructing when valid.
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<HttpAuthHandler> tmp_handler(new HttpAuthHandlerBasic());
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log))
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ERR_INVALID_RESPONSE;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  handler->swap(tmp_handler);
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace net
127