Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(869)

Side by Side Diff: components/password_manager/core/browser/affiliation_utils.cc

Issue 2953443002: Revert of Reland: Move the files related to Android <-> Web credentials to a separate folder. (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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 "components/password_manager/core/browser/affiliation_utils.h"
6
7 #include <algorithm>
8 #include <ostream>
9
10 #include "base/base64.h"
11 #include "base/strings/string_piece.h"
12 #include "base/strings/string_util.h"
13 #include "components/autofill/core/common/password_form.h"
14 #include "components/url_formatter/elide_url.h"
15 #include "components/variations/variations_associated_data.h"
16 #include "net/base/escape.h"
17 #include "url/third_party/mozilla/url_parse.h"
18 #include "url/url_canon_stdstring.h"
19
20 namespace password_manager {
21
22 namespace {
23
24 // The scheme used for identifying Android applications.
25 const char kAndroidAppScheme[] = "android";
26
27 // Returns a StringPiece corresponding to |component| in |uri|, or the empty
28 // string in case there is no such component.
29 base::StringPiece ComponentString(const std::string& uri,
30 const url::Component& component) {
31 if (!component.is_valid())
32 return base::StringPiece();
33 return base::StringPiece(uri.c_str() + component.begin, component.len);
34 }
35
36 // Returns true if the passed ASCII |input| string contains nothing else than
37 // alphanumeric characters and those in |other_characters|.
38 bool ContainsOnlyAlphanumericAnd(const base::StringPiece& input,
39 const base::StringPiece& other_characters) {
40 for (char c : input) {
41 if (!base::IsAsciiAlpha(c) && !base::IsAsciiDigit(c) &&
42 other_characters.find(c) == base::StringPiece::npos)
43 return false;
44 }
45 return true;
46 }
47
48 // Canonicalizes a Web facet URI, and returns true if canonicalization was
49 // successful and produced a valid URI.
50 bool CanonicalizeWebFacetURI(const std::string& input_uri,
51 const url::Parsed& input_parsed,
52 std::string* canonical_uri) {
53 url::Parsed canonical_parsed;
54 url::StdStringCanonOutput canonical_output(canonical_uri);
55
56 bool canonicalization_succeeded = url::CanonicalizeStandardURL(
57 input_uri.c_str(), input_uri.size(), input_parsed, nullptr,
58 &canonical_output, &canonical_parsed);
59 canonical_output.Complete();
60
61 if (canonicalization_succeeded && canonical_parsed.host.is_nonempty() &&
62 !canonical_parsed.username.is_valid() &&
63 !canonical_parsed.password.is_valid() &&
64 ComponentString(*canonical_uri, canonical_parsed.path) == "/" &&
65 !canonical_parsed.query.is_valid() && !canonical_parsed.ref.is_valid()) {
66 // Get rid of the trailing slash added by url::CanonicalizeStandardURL().
67 DCHECK_EQ((size_t)canonical_parsed.path.begin, canonical_uri->size() - 1);
68 canonical_uri->erase(canonical_parsed.path.begin,
69 canonical_parsed.path.len);
70 return true;
71 }
72 return false;
73 }
74
75 // Adds padding until the length of the base64-encoded |data| becomes a multiple
76 // of 4, and returns true if the thusly obtained |data| is now correctly padded,
77 // i.e., there are at most 2 padding characters ('=') at the very end.
78 bool CanonicalizeBase64Padding(std::string* data) {
79 while (data->size() % 4u != 0u)
80 data->push_back('=');
81
82 size_t first_padding = data->find_first_of('=');
83 return first_padding == std::string::npos ||
84 (data->size() - first_padding <= 2u &&
85 data->find_first_not_of('=', first_padding) == std::string::npos);
86 }
87
88 // Canonicalizes the username component in an Android facet URI (containing the
89 // certificate hash), and returns true if canonicalization was successful and
90 // produced a valid non-empty component.
91 bool CanonicalizeHashComponent(const base::StringPiece& input_hash,
92 url::CanonOutput* canonical_output) {
93 // Characters other than alphanumeric that are used in the "URL and filename
94 // safe" base64 alphabet; plus the padding ('=').
95 const char kBase64NonAlphanumericChars[] = "-_=";
96
97 // We need net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS to
98 // unescape the padding ('=').
99 std::string base64_encoded_hash = net::UnescapeURLComponent(
100 input_hash.as_string(),
101 net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
102
103 if (!base64_encoded_hash.empty() &&
104 CanonicalizeBase64Padding(&base64_encoded_hash) &&
105 ContainsOnlyAlphanumericAnd(base64_encoded_hash,
106 kBase64NonAlphanumericChars)) {
107 canonical_output->Append(base64_encoded_hash.data(),
108 base64_encoded_hash.size());
109 canonical_output->push_back('@');
110 return true;
111 }
112 return false;
113 }
114
115 // Canonicalizes the host component in an Android facet URI (containing the
116 // package name), and returns true if canonicalization was successful and
117 // produced a valid non-empty component.
118 bool CanonicalizePackageNameComponent(
119 const base::StringPiece& input_package_name,
120 url::CanonOutput* canonical_output) {
121 // Characters other than alphanumeric that are permitted in the package names.
122 const char kPackageNameNonAlphanumericChars[] = "._";
123
124 std::string package_name = net::UnescapeURLComponent(
125 input_package_name.as_string(), net::UnescapeRule::NORMAL);
126
127 // TODO(engedy): We might want to use a regex to check this more throughly.
128 if (!package_name.empty() &&
129 ContainsOnlyAlphanumericAnd(package_name,
130 kPackageNameNonAlphanumericChars)) {
131 canonical_output->Append(package_name.data(), package_name.size());
132 return true;
133 }
134 return false;
135 }
136
137 // Canonicalizes an Android facet URI, and returns true if canonicalization was
138 // successful and produced a valid URI.
139 bool CanonicalizeAndroidFacetURI(const std::string& input_uri,
140 const url::Parsed& input_parsed,
141 std::string* canonical_uri) {
142 url::StdStringCanonOutput canonical_output(canonical_uri);
143
144 url::Component unused;
145 bool success = url::CanonicalizeScheme(
146 input_uri.c_str(), input_parsed.scheme, &canonical_output, &unused);
147
148 canonical_output.push_back('/');
149 canonical_output.push_back('/');
150
151 // We cannot use url::CanonicalizeUserInfo as that would percent encode the
152 // base64 padding characters ('=').
153 success &= CanonicalizeHashComponent(
154 ComponentString(input_uri, input_parsed.username), &canonical_output);
155
156 // We cannot use url::CanonicalizeHost as that would convert the package name
157 // to lower case, but the package name is case sensitive.
158 success &= CanonicalizePackageNameComponent(
159 ComponentString(input_uri.data(), input_parsed.host), &canonical_output);
160
161 canonical_output.Complete();
162
163 return success && !input_parsed.password.is_nonempty() &&
164 (!input_parsed.path.is_nonempty() ||
165 ComponentString(input_uri, input_parsed.path) == "/") &&
166 !input_parsed.port.is_nonempty() && !input_parsed.query.is_valid() &&
167 !input_parsed.ref.is_valid();
168 }
169
170 // Computes the canonicalized form of |uri| into |canonical_uri|, and returns
171 // true if canonicalization was successful and produced a valid URI.
172 bool ParseAndCanonicalizeFacetURI(const std::string& input_uri,
173 std::string* canonical_uri) {
174 DCHECK(canonical_uri);
175 canonical_uri->clear();
176 canonical_uri->reserve(input_uri.size() + 32);
177
178 url::Parsed input_parsed;
179 url::ParseStandardURL(input_uri.c_str(), input_uri.size(), &input_parsed);
180
181 base::StringPiece scheme = ComponentString(input_uri, input_parsed.scheme);
182 if (base::LowerCaseEqualsASCII(scheme, url::kHttpsScheme)) {
183 return CanonicalizeWebFacetURI(input_uri, input_parsed, canonical_uri);
184 } else if (base::LowerCaseEqualsASCII(scheme, kAndroidAppScheme)) {
185 return CanonicalizeAndroidFacetURI(input_uri, input_parsed, canonical_uri);
186 }
187 return false;
188 }
189
190 } // namespace
191
192
193 // FacetURI -------------------------------------------------------------------
194
195 FacetURI::FacetURI() : is_valid_(false) {
196 }
197
198 // static
199 FacetURI FacetURI::FromPotentiallyInvalidSpec(const std::string& spec) {
200 std::string canonical_spec;
201 bool is_valid = ParseAndCanonicalizeFacetURI(spec, &canonical_spec);
202 return FacetURI(canonical_spec, is_valid);
203 }
204
205 // static
206 FacetURI FacetURI::FromCanonicalSpec(const std::string& canonical_spec) {
207 return FacetURI(canonical_spec, true);
208 }
209
210 bool FacetURI::operator==(const FacetURI& other) const {
211 DCHECK(is_empty() || is_valid());
212 DCHECK(other.is_empty() || other.is_valid());
213 return canonical_spec_ == other.canonical_spec_;
214 }
215
216 bool FacetURI::operator!=(const FacetURI& other) const {
217 DCHECK(is_empty() || is_valid());
218 DCHECK(other.is_empty() || other.is_valid());
219 return canonical_spec_ != other.canonical_spec_;
220 }
221
222 bool FacetURI::operator<(const FacetURI& other) const {
223 DCHECK(is_empty() || is_valid());
224 DCHECK(other.is_empty() || other.is_valid());
225 return canonical_spec_ < other.canonical_spec_;
226 }
227
228 bool FacetURI::operator>(const FacetURI& other) const {
229 DCHECK(is_empty() || is_valid());
230 DCHECK(other.is_empty() || other.is_valid());
231 return canonical_spec_ > other.canonical_spec_;
232 }
233
234 bool FacetURI::IsValidWebFacetURI() const {
235 return scheme() == url::kHttpsScheme;
236 }
237
238 bool FacetURI::IsValidAndroidFacetURI() const {
239 return scheme() == kAndroidAppScheme;
240 }
241
242 std::string FacetURI::scheme() const {
243 return is_valid()
244 ? ComponentString(canonical_spec_, parsed_.scheme).as_string()
245 : "";
246 }
247
248 std::string FacetURI::android_package_name() const {
249 if (!IsValidAndroidFacetURI())
250 return "";
251 return ComponentString(canonical_spec_, parsed_.host).as_string();
252 }
253
254 FacetURI::FacetURI(const std::string& canonical_spec, bool is_valid)
255 : is_valid_(is_valid), canonical_spec_(canonical_spec) {
256 // TODO(engedy): Refactor code in order to avoid to avoid parsing the URL
257 // twice.
258 url::ParseStandardURL(canonical_spec_.c_str(), canonical_spec_.size(),
259 &parsed_);
260 }
261
262
263 // AffiliatedFacetsWithUpdateTime ---------------------------------------------
264
265 AffiliatedFacetsWithUpdateTime::AffiliatedFacetsWithUpdateTime() {
266 }
267
268 AffiliatedFacetsWithUpdateTime::AffiliatedFacetsWithUpdateTime(
269 const AffiliatedFacetsWithUpdateTime& other) = default;
270
271 AffiliatedFacetsWithUpdateTime::~AffiliatedFacetsWithUpdateTime() {
272 }
273
274
275 // Helpers --------------------------------------------------------------------
276
277 std::ostream& operator<<(std::ostream& os, const FacetURI& facet_uri) {
278 return os << facet_uri.potentially_invalid_spec();
279 }
280
281 bool AreEquivalenceClassesEqual(const AffiliatedFacets& a,
282 const AffiliatedFacets& b) {
283 if (a.size() != b.size())
284 return false;
285
286 std::vector<FacetURI> a_sorted(a.begin(), a.end());
287 std::vector<FacetURI> b_sorted(b.begin(), b.end());
288 std::sort(a_sorted.begin(), a_sorted.end());
289 std::sort(b_sorted.begin(), b_sorted.end());
290 return std::equal(a_sorted.begin(), a_sorted.end(), b_sorted.begin());
291 }
292
293 bool IsValidAndroidFacetURI(const std::string& url) {
294 FacetURI facet = FacetURI::FromPotentiallyInvalidSpec(url);
295 return facet.IsValidAndroidFacetURI();
296 }
297
298 std::string GetHumanReadableOrigin(
299 const autofill::PasswordForm& password_form) {
300 FacetURI facet_uri =
301 FacetURI::FromPotentiallyInvalidSpec(password_form.signon_realm);
302 if (facet_uri.IsValidAndroidFacetURI())
303 return GetHumanReadableOriginForAndroidUri(facet_uri);
304
305 return base::UTF16ToUTF8(
306 url_formatter::FormatUrlForSecurityDisplay(password_form.origin));
307 }
308
309 std::string GetHumanReadableOriginForAndroidUri(const FacetURI facet_uri) {
310 DCHECK(facet_uri.IsValidAndroidFacetURI());
311 return facet_uri.scheme() + "://" + facet_uri.android_package_name();
312 }
313
314 } // namespace password_manager
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698