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