Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / utility / importer / nss_decryptor.cc
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.
4
5 #include "chrome/utility/importer/nss_decryptor.h"
6
7 #include <string>
8 #include <vector>
9
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"
18
19 #if defined(USE_NSS)
20 #include <pk11pub.h>
21 #include <pk11sdr.h>
22 #endif  // defined(USE_NSS)
23
24 // This method is based on some Firefox code in
25 //   security/manager/ssl/src/nsSDR.cpp
26 // The license block is:
27
28 /* ***** BEGIN LICENSE BLOCK *****
29 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
30 *
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/
35 *
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.
40 *
41 * The Original Code is the Netscape security libraries.
42 *
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.
47 *
48 * Contributor(s):
49 *
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.
61 *
62 * ***** END LICENSE BLOCK ***** */
63
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();
68
69   if (crypt.empty())
70     return base::string16();
71
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();
84     }
85
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)
94     result = PK11SDR_DecryptWithSlot(slot, &request, &reply, NULL);
95 #else
96     result = PK11SDR_Decrypt(&request, &reply, NULL);
97 #endif  // defined(USE_NSS)
98     if (result == SECSuccess)
99       plain.assign(reinterpret_cast<char*>(reply.data), reply.len);
100
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();
107   }
108
109   return base::UTF8ToUTF16(plain);
110 }
111
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();
122
123   // Splits the file content into lines.
124   std::vector<std::string> lines;
125   base::SplitString(content, '\n', &lines);
126
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;
139
140   GURL::Replacements rep;
141   rep.ClearQuery();
142   rep.ClearRef();
143   rep.ClearUsername();
144   rep.ClearPassword();
145
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);
154   }
155   ++i;
156
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;
166
167     // A block has at least five lines.
168     if (end - begin < 5)
169       continue;
170
171     autofill::PasswordForm form;
172
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];
186
187       size_t start = lines[begin].find(kRealmBracketBegin);
188       url = GURL(lines[begin].substr(0, start));
189
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]);
197     }
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.SchemeIsSecure();
206     ++begin;
207
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;
222       }
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;
229       }
230       // Version 3 has an extra line for further use.
231       if (version == 3) {
232         ++begin;
233       }
234
235       forms->push_back(form);
236     }
237   }
238 }
239
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;
245
246   const char* query = "SELECT hostname FROM moz_disabledHosts";
247   sql::Statement s(db.GetUniqueStatement(query));
248   if (!s.is_valid())
249     return false;
250
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);
263   }
264
265   const char* query2 = "SELECT hostname, httpRealm, formSubmitURL, "
266                        "usernameField, passwordField, encryptedUsername, "
267                        "encryptedPassword FROM moz_logins";
268
269   sql::Statement s2(db.GetUniqueStatement(query2));
270   if (!s2.is_valid())
271     return false;
272
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));
284     }
285     // Skip this row if the URL is not valid.
286     if (!url.is_valid())
287       continue;
288
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;
298     }
299     form.ssl_valid = form.origin.SchemeIsSecure();
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);
307   }
308   return true;
309 }