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

Side by Side Diff: net/base/x509_openssl_util.cc

Issue 4184004: Add support for certificate name checking (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: wtc comments, plus moved method to x509_openssl_util Created 10 years, 1 month 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/base/x509_openssl_util.h" 5 #include "net/base/x509_openssl_util.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/string_number_conversions.h" 8 #include "base/string_number_conversions.h"
9 #include "base/string_piece.h" 9 #include "base/string_piece.h"
10 #include "base/string_util.h"
10 #include "base/time.h" 11 #include "base/time.h"
12 #include "net/base/registry_controlled_domain.h"
11 13
12 namespace net { 14 namespace net {
13 15
14 namespace x509_openssl_util { 16 namespace x509_openssl_util {
15 17
16 namespace { 18 namespace {
17 19
18 // Helper for ParseDate. |*field| must contain at least |field_len| characters. 20 // Helper for ParseDate. |*field| must contain at least |field_len| characters.
19 // |*field| will be advanced by |field_len| on exit. |*ok| is set to false if 21 // |*field| will be advanced by |field_len| on exit. |*ok| is set to false if
20 // there is an error in parsing the number, but left untouched otherwise. 22 // there is an error in parsing the number, but left untouched otherwise.
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
101 103
102 if (!valid) { 104 if (!valid) {
103 NOTREACHED() << "can't parse x509 date " << str_date; 105 NOTREACHED() << "can't parse x509 date " << str_date;
104 return false; 106 return false;
105 } 107 }
106 108
107 *time = base::Time::FromUTCExploded(exploded); 109 *time = base::Time::FromUTCExploded(exploded);
108 return true; 110 return true;
109 } 111 }
110 112
113 // Implemented as per http://tools.ietf.org/html/draft-saintandre-tls-server-id- check-09#section-4.4.3
114 bool VerifyHostname(const std::string& hostname,
115 const std::vector<std::string>& cert_names) {
116 DCHECK(!hostname.empty());
117
118 // Simple host name validation. A valid domain name must only contain
119 // alpha, digits, hyphens, and dots. An IP address may have digits and dots,
120 // and also square braces and colons for IPv6 addresses.
wtc 2010/11/03 00:29:49 IPv6 address literals passed to this function shou
Ryan Sleevi 2010/11/03 03:42:21 BUG: I'm not sure that this function should consid
joth 2010/11/12 18:55:23 Done.
joth 2010/11/12 18:55:23 Done. Removed all attempt at IP support, for now.
121 bool found_alpha = false;
122 bool found_ip6_chars = false;
123 bool found_hyphen = false;
124
125 std::string reference_name;
126 size_t first_dot_index = std::string::npos;
127 reference_name.reserve(hostname.length());
128 for (std::string::const_iterator it = hostname.begin();
129 it != hostname.end(); ++it) {
130 char c = *it;
131 if (IsAsciiAlpha(c)) {
132 found_alpha = true;
133 c = ToLowerASCII(c);
134 } else if (c == '.') {
135 if (first_dot_index == std::string::npos)
136 first_dot_index = reference_name.length();
137 } else if (c == '[' || c == ']' || c == ':') {
138 found_ip6_chars = true;
139 } else if (c == '-') {
140 found_hyphen = true;
141 } else if (!IsAsciiDigit(c)) {
142 LOG(WARNING) << "Invalid char " << c << " in hostname " << hostname;
143 return false;
144 }
145 reference_name.push_back(c);
146 }
147 DCHECK(!reference_name.empty());
148 if (found_hyphen && found_ip6_chars) {
149 LOG(WARNING) << "Mixed IPv6 domain name " << hostname;
150 return false;
151 }
152
153 // |host_domain| is the remainder of |host| after the leading host component
154 // is stripped off, but includes the leading dot e.g. "www.f.com" -> ".f.com".
155 // If there is no meaningful domain part to |host| (e.g. it is an IP address
156 // or contains no dots) then |host_domain| will be empty.
157 base::StringPiece host_domain;
158 if (found_alpha && !found_ip6_chars && first_dot_index != std::string::npos) {
159 host_domain = reference_name;
160 host_domain.remove_prefix(first_dot_index);
161 DCHECK(host_domain.starts_with("."));
162
163 // Make sure the domain is not a registry domain, e.g. to avoid a dodgy cert
164 // claiming to be for *.org matching a hostname of chromium.org.
165 size_t registry_length = RegistryControlledDomainService::GetRegistryLength(
166 reference_name, true);
167 // The registry length does not include the leading dot, so add 1 for it.
168 if (host_domain.length() == registry_length + 1) {
wtc 2010/11/03 00:29:49 Nit: omit curly braces {} for one-liner "if" bodie
joth 2010/11/12 18:55:23 Done.
169 host_domain.clear();
170 }
171 }
172
173 for (std::vector<std::string>::const_iterator it = cert_names.begin();
174 it != cert_names.end(); ++it) {
175 // Catch badly corrupt cert names up front.
176 if (it->empty() || it->find('\0') != std::string::npos) {
177 LOG(WARNING) << "Bad name in cert: " << *it;
178 continue;
179 }
180 const std::string cert_name_string(StringToLowerASCII(*it));
181 base::StringPiece cert_match(cert_name_string);
182
183 // Remove trailing dot, if any.
184 if (cert_match.ends_with("."))
185 cert_match.remove_suffix(1);
186
187 // The hostname must be at least as long as the cert name it is matching,
188 // as we require the wildcard (if present) to match at least one character.
189 if (cert_match.length() > reference_name.length())
190 continue;
191
192 if (cert_match == reference_name)
193 return true;
194
195 // Next see if this cert name starts with a wildcard, so long as the
196 // hostname we're matching against has a valid 'domain' part to match.
197 // TODO(joth): The "-10" version of draft-saintandre-tls-server-id-check
198 // allows the wildcard to appear anywhere in the leftmost label. Once this
199 // is finalized, we may need to update this check to use MatchPattern.
Ryan Sleevi 2010/11/03 03:42:21 @wtc: here
joth 2010/11/12 18:55:23 Done.
200 if (host_domain.empty() || !cert_match.starts_with("*"))
201 continue;
202
203 // Erase the * but not the . from the domain, as we need to include the dot
204 // in the comparison.
205 cert_match.remove_prefix(1);
206
207 // Do character by character comparison on the remainder to see
208 // if we have a wildcard match. This intentionally does no special handling
209 // for any other wildcard characters in |domain|; alternatively it could
210 // detect these and skip those candidate cert names.
211 if (cert_match == host_domain)
212 return true;
213 }
214 DVLOG(1) << "Could not find any match for " << hostname
215 << " (canonicalized as " << reference_name
216 << ") in cert names " << JoinString(cert_names, '|');
217 return false;
218 }
219
111 } // namespace x509_openssl_util 220 } // namespace x509_openssl_util
112 221
113 } // namespace net 222 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698