Fix broken path in extensions/common/PRESUBMIT.py
[chromium-blink-merge.git] / chrome / utility / importer / nss_decryptor.cc
blob01bdb0ab351fa4753b7e661273e21874fca4cbce
1 // Copyright (c) 2012 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.
5 #include "chrome/utility/importer/nss_decryptor.h"
7 #include <string>
8 #include <vector>
10 #include "base/base64.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "components/autofill/core/common/password_form.h"
16 #include "sql/connection.h"
17 #include "sql/statement.h"
19 #if defined(USE_NSS_CERTS)
20 #include <pk11pub.h>
21 #include <pk11sdr.h>
22 #endif // defined(USE_NSS_CERTS)
24 // This method is based on some Firefox code in
25 // security/manager/ssl/src/nsSDR.cpp
26 // The license block is:
28 /* ***** BEGIN LICENSE BLOCK *****
29 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
31 * The contents of this file are subject to the Mozilla Public License Version
32 * 1.1 (the "License"); you may not use this file except in compliance with
33 * the License. You may obtain a copy of the License at
34 * http://www.mozilla.org/MPL/
36 * Software distributed under the License is distributed on an "AS IS" basis,
37 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
38 * for the specific language governing rights and limitations under the
39 * License.
41 * The Original Code is the Netscape security libraries.
43 * The Initial Developer of the Original Code is
44 * Netscape Communications Corporation.
45 * Portions created by the Initial Developer are Copyright (C) 1994-2000
46 * the Initial Developer. All Rights Reserved.
48 * Contributor(s):
50 * Alternatively, the contents of this file may be used under the terms of
51 * either the GNU General Public License Version 2 or later (the "GPL"), or
52 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
53 * in which case the provisions of the GPL or the LGPL are applicable instead
54 * of those above. If you wish to allow use of your version of this file only
55 * under the terms of either the GPL or the LGPL, and not to allow others to
56 * use your version of this file under the terms of the MPL, indicate your
57 * decision by deleting the provisions above and replace them with the notice
58 * and other provisions required by the GPL or the LGPL. If you do not delete
59 * the provisions above, a recipient may use your version of this file under
60 * the terms of any one of the MPL, the GPL or the LGPL.
62 * ***** END LICENSE BLOCK ***** */
64 base::string16 NSSDecryptor::Decrypt(const std::string& crypt) const {
65 // Do nothing if NSS is not loaded.
66 if (!is_nss_initialized_)
67 return base::string16();
69 if (crypt.empty())
70 return base::string16();
72 // The old style password is encoded in base64. They are identified
73 // by a leading '~'. Otherwise, we should decrypt the text.
74 std::string plain;
75 if (crypt[0] != '~') {
76 std::string decoded_data;
77 if (!base::Base64Decode(crypt, &decoded_data))
78 return base::string16();
79 PK11SlotInfo* slot = GetKeySlotForDB();
80 SECStatus result = PK11_Authenticate(slot, PR_TRUE, NULL);
81 if (result != SECSuccess) {
82 FreeSlot(slot);
83 return base::string16();
86 SECItem request;
87 request.data = reinterpret_cast<unsigned char*>(
88 const_cast<char*>(decoded_data.data()));
89 request.len = static_cast<unsigned int>(decoded_data.size());
90 SECItem reply;
91 reply.data = NULL;
92 reply.len = 0;
93 #if defined(USE_NSS_CERTS)
94 result = PK11SDR_DecryptWithSlot(slot, &request, &reply, NULL);
95 #else
96 result = PK11SDR_Decrypt(&request, &reply, NULL);
97 #endif // defined(USE_NSS_CERTS)
98 if (result == SECSuccess)
99 plain.assign(reinterpret_cast<char*>(reply.data), reply.len);
101 SECITEM_FreeItem(&reply, PR_FALSE);
102 FreeSlot(slot);
103 } else {
104 // Deletes the leading '~' before decoding.
105 if (!base::Base64Decode(crypt.substr(1), &plain))
106 return base::string16();
109 return base::UTF8ToUTF16(plain);
112 // There are three versions of password files. They store saved user
113 // names and passwords.
114 // References:
115 // http://kb.mozillazine.org/Signons.txt
116 // http://kb.mozillazine.org/Signons2.txt
117 // http://kb.mozillazine.org/Signons3.txt
118 void NSSDecryptor::ParseSignons(
119 const std::string& content,
120 std::vector<autofill::PasswordForm>* forms) {
121 forms->clear();
123 // Splits the file content into lines.
124 std::vector<std::string> lines;
125 base::SplitString(content, '\n', &lines);
127 // The first line is the file version. We skip the unknown versions.
128 if (lines.empty())
129 return;
130 int version;
131 if (lines[0] == "#2c")
132 version = 1;
133 else if (lines[0] == "#2d")
134 version = 2;
135 else if (lines[0] == "#2e")
136 version = 3;
137 else
138 return;
140 GURL::Replacements rep;
141 rep.ClearQuery();
142 rep.ClearRef();
143 rep.ClearUsername();
144 rep.ClearPassword();
146 // Reads never-saved list. Domains are stored one per line.
147 size_t i;
148 for (i = 1; i < lines.size() && lines[i].compare(".") != 0; ++i) {
149 autofill::PasswordForm form;
150 form.origin = GURL(lines[i]).ReplaceComponents(rep);
151 form.signon_realm = form.origin.GetOrigin().spec();
152 form.blacklisted_by_user = true;
153 forms->push_back(form);
155 ++i;
157 // Reads saved passwords. The information is stored in blocks
158 // seperated by lines that only contain a dot. We find a block
159 // by the seperator and parse them one by one.
160 while (i < lines.size()) {
161 size_t begin = i;
162 size_t end = i + 1;
163 while (end < lines.size() && lines[end].compare(".") != 0)
164 ++end;
165 i = end + 1;
167 // A block has at least five lines.
168 if (end - begin < 5)
169 continue;
171 autofill::PasswordForm form;
173 // The first line is the site URL.
174 // For HTTP authentication logins, the URL may contain http realm,
175 // which will be in bracket:
176 // sitename:8080 (realm)
177 GURL url;
178 std::string realm;
179 const char kRealmBracketBegin[] = " (";
180 const char kRealmBracketEnd[] = ")";
181 if (lines[begin].find(kRealmBracketBegin) != std::string::npos) {
182 // In this case, the scheme may not exsit. We assume that the
183 // scheme is HTTP.
184 if (lines[begin].find("://") == std::string::npos)
185 lines[begin] = "http://" + lines[begin];
187 size_t start = lines[begin].find(kRealmBracketBegin);
188 url = GURL(lines[begin].substr(0, start));
190 start += std::string(kRealmBracketBegin).size();
191 size_t end = lines[begin].rfind(kRealmBracketEnd);
192 realm = lines[begin].substr(start, end - start);
193 } else {
194 // Don't have http realm. It is the URL that the following passwords
195 // belong to.
196 url = GURL(lines[begin]);
198 // Skips this block if the URL is not valid.
199 if (!url.is_valid())
200 continue;
201 form.origin = url.ReplaceComponents(rep);
202 form.signon_realm = form.origin.GetOrigin().spec();
203 if (!realm.empty())
204 form.signon_realm += realm;
205 form.ssl_valid = form.origin.SchemeIsCryptographic();
206 ++begin;
208 // There may be multiple username/password pairs for this site.
209 // In this case, they are saved in one block without a seperated
210 // line (contains a dot).
211 while (begin + 4 < end) {
212 // The user name.
213 form.username_element = base::UTF8ToUTF16(lines[begin++]);
214 form.username_value = Decrypt(lines[begin++]);
215 // The element name has a leading '*'.
216 if (lines[begin].at(0) == '*') {
217 form.password_element = base::UTF8ToUTF16(lines[begin++].substr(1));
218 form.password_value = Decrypt(lines[begin++]);
219 } else {
220 // Maybe the file is bad, we skip to next block.
221 break;
223 // The action attribute from the form element. This line exists
224 // in versin 2 or above.
225 if (version >= 2) {
226 if (begin < end)
227 form.action = GURL(lines[begin]).ReplaceComponents(rep);
228 ++begin;
230 // Version 3 has an extra line for further use.
231 if (version == 3) {
232 ++begin;
235 forms->push_back(form);
240 bool NSSDecryptor::ReadAndParseSignons(const base::FilePath& sqlite_file,
241 std::vector<autofill::PasswordForm>* forms) {
242 sql::Connection db;
243 if (!db.Open(sqlite_file))
244 return false;
246 const char query[] = "SELECT hostname FROM moz_disabledHosts";
247 sql::Statement s(db.GetUniqueStatement(query));
248 if (!s.is_valid())
249 return false;
251 GURL::Replacements rep;
252 rep.ClearQuery();
253 rep.ClearRef();
254 rep.ClearUsername();
255 rep.ClearPassword();
256 // Read domains for which passwords are never saved.
257 while (s.Step()) {
258 autofill::PasswordForm form;
259 form.origin = GURL(s.ColumnString(0)).ReplaceComponents(rep);
260 form.signon_realm = form.origin.GetOrigin().spec();
261 form.blacklisted_by_user = true;
262 forms->push_back(form);
265 const char query2[] = "SELECT hostname, httpRealm, formSubmitURL, "
266 "usernameField, passwordField, encryptedUsername, "
267 "encryptedPassword FROM moz_logins";
269 sql::Statement s2(db.GetUniqueStatement(query2));
270 if (!s2.is_valid())
271 return false;
273 while (s2.Step()) {
274 GURL url;
275 std::string realm(s2.ColumnString(1));
276 if (!realm.empty()) {
277 // In this case, the scheme may not exsit. Assume HTTP.
278 std::string host(s2.ColumnString(0));
279 if (host.find("://") == std::string::npos)
280 host = "http://" + host;
281 url = GURL(host);
282 } else {
283 url = GURL(s2.ColumnString(0));
285 // Skip this row if the URL is not valid.
286 if (!url.is_valid())
287 continue;
289 autofill::PasswordForm form;
290 form.origin = url.ReplaceComponents(rep);
291 form.signon_realm = form.origin.GetOrigin().spec();
292 if (!realm.empty()) {
293 form.signon_realm += realm;
294 // Non-empty realm indicates that it's not html form authentication entry.
295 // Extracted data doesn't allow us to distinguish basic_auth entry from
296 // digest_auth entry, so let's assume basic_auth.
297 form.scheme = autofill::PasswordForm::SCHEME_BASIC;
299 form.ssl_valid = form.origin.SchemeIsCryptographic();
300 // The user name, password and action.
301 form.username_element = s2.ColumnString16(3);
302 form.username_value = Decrypt(s2.ColumnString(5));
303 form.password_element = s2.ColumnString16(4);
304 form.password_value = Decrypt(s2.ColumnString(6));
305 form.action = GURL(s2.ColumnString(2)).ReplaceComponents(rep);
306 forms->push_back(form);
308 return true;