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"
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"
22 #endif // defined(USE_NSS)
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
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.
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 // The old style password is encoded in base64. They are identified
70 // by a leading '~'. Otherwise, we should decrypt the text.
72 if (crypt
[0] != '~') {
73 std::string decoded_data
;
74 base::Base64Decode(crypt
, &decoded_data
);
75 PK11SlotInfo
* slot
= GetKeySlotForDB();
76 SECStatus result
= PK11_Authenticate(slot
, PR_TRUE
, NULL
);
77 if (result
!= SECSuccess
) {
79 return base::string16();
83 request
.data
= reinterpret_cast<unsigned char*>(
84 const_cast<char*>(decoded_data
.data()));
85 request
.len
= static_cast<unsigned int>(decoded_data
.size());
90 result
= PK11SDR_DecryptWithSlot(slot
, &request
, &reply
, NULL
);
92 result
= PK11SDR_Decrypt(&request
, &reply
, NULL
);
93 #endif // defined(USE_NSS)
94 if (result
== SECSuccess
)
95 plain
.assign(reinterpret_cast<char*>(reply
.data
), reply
.len
);
97 SECITEM_FreeItem(&reply
, PR_FALSE
);
100 // Deletes the leading '~' before decoding.
101 base::Base64Decode(crypt
.substr(1), &plain
);
104 return base::UTF8ToUTF16(plain
);
107 // There are three versions of password files. They store saved user
108 // names and passwords.
110 // http://kb.mozillazine.org/Signons.txt
111 // http://kb.mozillazine.org/Signons2.txt
112 // http://kb.mozillazine.org/Signons3.txt
113 void NSSDecryptor::ParseSignons(
114 const std::string
& content
,
115 std::vector
<autofill::PasswordForm
>* forms
) {
118 // Splits the file content into lines.
119 std::vector
<std::string
> lines
;
120 base::SplitString(content
, '\n', &lines
);
122 // The first line is the file version. We skip the unknown versions.
126 if (lines
[0] == "#2c")
128 else if (lines
[0] == "#2d")
130 else if (lines
[0] == "#2e")
135 GURL::Replacements rep
;
141 // Reads never-saved list. Domains are stored one per line.
143 for (i
= 1; i
< lines
.size() && lines
[i
].compare(".") != 0; ++i
) {
144 autofill::PasswordForm form
;
145 form
.origin
= GURL(lines
[i
]).ReplaceComponents(rep
);
146 form
.signon_realm
= form
.origin
.GetOrigin().spec();
147 form
.blacklisted_by_user
= true;
148 forms
->push_back(form
);
152 // Reads saved passwords. The information is stored in blocks
153 // seperated by lines that only contain a dot. We find a block
154 // by the seperator and parse them one by one.
155 while (i
< lines
.size()) {
158 while (end
< lines
.size() && lines
[end
].compare(".") != 0)
162 // A block has at least five lines.
166 autofill::PasswordForm form
;
168 // The first line is the site URL.
169 // For HTTP authentication logins, the URL may contain http realm,
170 // which will be in bracket:
171 // sitename:8080 (realm)
174 const char kRealmBracketBegin
[] = " (";
175 const char kRealmBracketEnd
[] = ")";
176 if (lines
[begin
].find(kRealmBracketBegin
) != std::string::npos
) {
177 // In this case, the scheme may not exsit. We assume that the
179 if (lines
[begin
].find("://") == std::string::npos
)
180 lines
[begin
] = "http://" + lines
[begin
];
182 size_t start
= lines
[begin
].find(kRealmBracketBegin
);
183 url
= GURL(lines
[begin
].substr(0, start
));
185 start
+= std::string(kRealmBracketBegin
).size();
186 size_t end
= lines
[begin
].rfind(kRealmBracketEnd
);
187 realm
= lines
[begin
].substr(start
, end
- start
);
189 // Don't have http realm. It is the URL that the following passwords
191 url
= GURL(lines
[begin
]);
193 // Skips this block if the URL is not valid.
196 form
.origin
= url
.ReplaceComponents(rep
);
197 form
.signon_realm
= form
.origin
.GetOrigin().spec();
199 form
.signon_realm
+= realm
;
200 form
.ssl_valid
= form
.origin
.SchemeIsSecure();
203 // There may be multiple username/password pairs for this site.
204 // In this case, they are saved in one block without a seperated
205 // line (contains a dot).
206 while (begin
+ 4 < end
) {
208 form
.username_element
= base::UTF8ToUTF16(lines
[begin
++]);
209 form
.username_value
= Decrypt(lines
[begin
++]);
210 // The element name has a leading '*'.
211 if (lines
[begin
].at(0) == '*') {
212 form
.password_element
= base::UTF8ToUTF16(lines
[begin
++].substr(1));
213 form
.password_value
= Decrypt(lines
[begin
++]);
215 // Maybe the file is bad, we skip to next block.
218 // The action attribute from the form element. This line exists
219 // in versin 2 or above.
222 form
.action
= GURL(lines
[begin
]).ReplaceComponents(rep
);
225 // Version 3 has an extra line for further use.
230 forms
->push_back(form
);
235 bool NSSDecryptor::ReadAndParseSignons(const base::FilePath
& sqlite_file
,
236 std::vector
<autofill::PasswordForm
>* forms
) {
238 if (!db
.Open(sqlite_file
))
241 const char* query
= "SELECT hostname FROM moz_disabledHosts";
242 sql::Statement
s(db
.GetUniqueStatement(query
));
246 GURL::Replacements rep
;
251 // Read domains for which passwords are never saved.
253 autofill::PasswordForm form
;
254 form
.origin
= GURL(s
.ColumnString(0)).ReplaceComponents(rep
);
255 form
.signon_realm
= form
.origin
.GetOrigin().spec();
256 form
.blacklisted_by_user
= true;
257 forms
->push_back(form
);
260 const char* query2
= "SELECT hostname, httpRealm, formSubmitURL, "
261 "usernameField, passwordField, encryptedUsername, "
262 "encryptedPassword FROM moz_logins";
264 sql::Statement
s2(db
.GetUniqueStatement(query2
));
270 std::string
realm(s2
.ColumnString(1));
271 if (!realm
.empty()) {
272 // In this case, the scheme may not exsit. Assume HTTP.
273 std::string
host(s2
.ColumnString(0));
274 if (host
.find("://") == std::string::npos
)
275 host
= "http://" + host
;
278 url
= GURL(s2
.ColumnString(0));
280 // Skip this row if the URL is not valid.
284 autofill::PasswordForm form
;
285 form
.origin
= url
.ReplaceComponents(rep
);
286 form
.signon_realm
= form
.origin
.GetOrigin().spec();
288 form
.signon_realm
+= realm
;
289 form
.ssl_valid
= form
.origin
.SchemeIsSecure();
290 // The user name, password and action.
291 form
.username_element
= s2
.ColumnString16(3);
292 form
.username_value
= Decrypt(s2
.ColumnString(5));
293 form
.password_element
= s2
.ColumnString16(4);
294 form
.password_value
= Decrypt(s2
.ColumnString(6));
295 form
.action
= GURL(s2
.ColumnString(2)).ReplaceComponents(rep
);
296 forms
->push_back(form
);