1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/http/http_auth_handler_basic.h"
6
7#include <string>
8
9#include "base/base64.h"
10#include "base/strings/string_util.h"
11#include "base/strings/utf_string_conversions.h"
12#include "net/base/net_errors.h"
13#include "net/base/net_string_util.h"
14#include "net/http/http_auth.h"
15#include "net/http/http_auth_challenge_tokenizer.h"
16
17namespace net {
18
19namespace {
20
21// Parses a realm from an auth challenge, and converts to UTF8-encoding.
22// Returns whether the realm is invalid or the parameters are invalid.
23//
24// Note that if a realm was not specified, we will default it to "";
25// so specifying 'Basic realm=""' is equivalent to 'Basic'.
26//
27// This is more generous than RFC 2617, which is pretty clear in the
28// production of challenge that realm is required.
29//
30// We allow it to be compatibility with certain embedded webservers that don't
31// include a realm (see http://crbug.com/20984.)
32//
33// The over-the-wire realm is encoded as ISO-8859-1 (aka Latin-1).
34//
35// TODO(cbentzel): Realm may need to be decoded using RFC 2047 rules as
36// well, see http://crbug.com/25790.
37bool ParseRealm(const HttpAuthChallengeTokenizer& tokenizer,
38                std::string* realm) {
39  CHECK(realm);
40  realm->clear();
41  HttpUtil::NameValuePairsIterator parameters = tokenizer.param_pairs();
42  while (parameters.GetNext()) {
43    if (!LowerCaseEqualsASCII(parameters.name(), "realm"))
44      continue;
45
46    if (!net::ConvertToUtf8AndNormalize(parameters.value(), kCharsetLatin1,
47                                        realm)) {
48      return false;
49    }
50  }
51  return parameters.valid();
52}
53
54}  // namespace
55
56bool HttpAuthHandlerBasic::Init(HttpAuthChallengeTokenizer* challenge) {
57  auth_scheme_ = HttpAuth::AUTH_SCHEME_BASIC;
58  score_ = 1;
59  properties_ = 0;
60  return ParseChallenge(challenge);
61}
62
63bool HttpAuthHandlerBasic::ParseChallenge(
64    HttpAuthChallengeTokenizer* challenge) {
65  // Verify the challenge's auth-scheme.
66  if (!LowerCaseEqualsASCII(challenge->scheme(), "basic"))
67    return false;
68
69  std::string realm;
70  if (!ParseRealm(*challenge, &realm))
71    return false;
72
73  realm_ = realm;
74  return true;
75}
76
77HttpAuth::AuthorizationResult HttpAuthHandlerBasic::HandleAnotherChallenge(
78    HttpAuthChallengeTokenizer* challenge) {
79  // Basic authentication is always a single round, so any responses
80  // should be treated as a rejection.  However, if the new challenge
81  // is for a different realm, then indicate the realm change.
82  std::string realm;
83  if (!ParseRealm(*challenge, &realm))
84    return HttpAuth::AUTHORIZATION_RESULT_INVALID;
85  return (realm_ != realm)?
86      HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM:
87      HttpAuth::AUTHORIZATION_RESULT_REJECT;
88}
89
90int HttpAuthHandlerBasic::GenerateAuthTokenImpl(
91    const AuthCredentials* credentials, const HttpRequestInfo*,
92    const CompletionCallback&, std::string* auth_token) {
93  DCHECK(credentials);
94  // TODO(eroman): is this the right encoding of username/password?
95  std::string base64_username_password;
96  base::Base64Encode(base::UTF16ToUTF8(credentials->username()) + ":" +
97                         base::UTF16ToUTF8(credentials->password()),
98                     &base64_username_password);
99  *auth_token = "Basic " + base64_username_password;
100  return OK;
101}
102
103HttpAuthHandlerBasic::Factory::Factory() {
104}
105
106HttpAuthHandlerBasic::Factory::~Factory() {
107}
108
109int HttpAuthHandlerBasic::Factory::CreateAuthHandler(
110    HttpAuthChallengeTokenizer* challenge,
111    HttpAuth::Target target,
112    const GURL& origin,
113    CreateReason reason,
114    int digest_nonce_count,
115    const BoundNetLog& net_log,
116    scoped_ptr<HttpAuthHandler>* handler) {
117  // TODO(cbentzel): Move towards model of parsing in the factory
118  //                 method and only constructing when valid.
119  scoped_ptr<HttpAuthHandler> tmp_handler(new HttpAuthHandlerBasic());
120  if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log))
121    return ERR_INVALID_RESPONSE;
122  handler->swap(tmp_handler);
123  return OK;
124}
125
126}  // namespace net
127