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 64base::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 118void 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 240bool 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} 310