OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2006-2009 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/password_manager/password_store_kwallet.h" |
| 6 |
| 7 #include <sstream> |
| 8 |
| 9 #include "base/logging.h" |
| 10 #include "base/md5.h" |
| 11 #include "base/pickle.h" |
| 12 #include "base/string_util.h" |
| 13 #include "base/task.h" |
| 14 |
| 15 using std::string; |
| 16 using std::vector; |
| 17 |
| 18 const char* PasswordStoreKWallet::kAppId = "Chrome"; |
| 19 const char* PasswordStoreKWallet::kKWalletFolder = "Chrome Form Data"; |
| 20 |
| 21 const char* PasswordStoreKWallet::kKWalletServiceName = "org.kde.kwalletd"; |
| 22 const char* PasswordStoreKWallet::kKWalletPath = "/modules/kwalletd"; |
| 23 const char* PasswordStoreKWallet::kKWalletInterface = "org.kde.KWallet"; |
| 24 const char* PasswordStoreKWallet::kKLauncherServiceName = "org.kde.klauncher"; |
| 25 const char* PasswordStoreKWallet::kKLauncherPath = "/KLauncher"; |
| 26 const char* PasswordStoreKWallet::kKLauncherInterface = "org.kde.KLauncher"; |
| 27 |
| 28 PasswordStoreKWallet::PasswordStoreKWallet() |
| 29 : error_(NULL), |
| 30 connection_(NULL), |
| 31 proxy_(NULL) { |
| 32 } |
| 33 |
| 34 PasswordStoreKWallet::~PasswordStoreKWallet() { |
| 35 if (proxy_) { |
| 36 g_object_unref(proxy_); |
| 37 } |
| 38 } |
| 39 |
| 40 bool PasswordStoreKWallet::Init() { |
| 41 thread_.reset(new base::Thread("Chrome_KeyringThread")); |
| 42 |
| 43 if (!thread_->Start()) { |
| 44 thread_.reset(NULL); |
| 45 return false; |
| 46 } |
| 47 |
| 48 // Initialize threading in dbus-glib - it should be fine for |
| 49 // dbus_g_thread_init to be called multiple times. |
| 50 if (!g_thread_supported()) |
| 51 g_thread_init(NULL); |
| 52 dbus_g_thread_init(); |
| 53 |
| 54 // Get a connection to the session bus. |
| 55 connection_ = dbus_g_bus_get(DBUS_BUS_SESSION, &error_); |
| 56 if (CheckError()) |
| 57 return false; |
| 58 |
| 59 if (!StartKWalletd()) return false; |
| 60 if (!InitWallet()) return false; |
| 61 |
| 62 return true; |
| 63 } |
| 64 |
| 65 bool PasswordStoreKWallet::StartKWalletd() { |
| 66 // Sadly kwalletd doesn't use DBUS activation, so we have to make a call to |
| 67 // klauncher to start it. |
| 68 DBusGProxy* klauncher_proxy = |
| 69 dbus_g_proxy_new_for_name(connection_, kKLauncherServiceName, |
| 70 kKLauncherPath, kKLauncherInterface); |
| 71 |
| 72 char* empty_string_list = NULL; |
| 73 int ret = 1; |
| 74 char* error = NULL; |
| 75 dbus_g_proxy_call(klauncher_proxy, "start_service_by_desktop_name", &error_, |
| 76 G_TYPE_STRING, "kwalletd", // serviceName |
| 77 G_TYPE_STRV, &empty_string_list, // urls |
| 78 G_TYPE_STRV, &empty_string_list, // envs |
| 79 G_TYPE_STRING, "", // startup_id |
| 80 G_TYPE_BOOLEAN, (gboolean) false, // blind |
| 81 G_TYPE_INVALID, |
| 82 G_TYPE_INT, &ret, // result |
| 83 G_TYPE_STRING, NULL, // dubsName |
| 84 G_TYPE_STRING, &error, // error |
| 85 G_TYPE_INT, NULL, // pid |
| 86 G_TYPE_INVALID); |
| 87 |
| 88 if (error && *error) { |
| 89 LOG(ERROR) << "Error launching kwalletd: " << error; |
| 90 ret = 1; // Make sure we return false after freeing. |
| 91 } |
| 92 |
| 93 g_free(error); |
| 94 g_object_unref(klauncher_proxy); |
| 95 |
| 96 if (CheckError() || ret != 0) |
| 97 return false; |
| 98 return true; |
| 99 } |
| 100 |
| 101 bool PasswordStoreKWallet::InitWallet() { |
| 102 // Make a proxy to KWallet. |
| 103 proxy_ = dbus_g_proxy_new_for_name(connection_, kKWalletServiceName, |
| 104 kKWalletPath, kKWalletInterface); |
| 105 |
| 106 // Check KWallet is enabled. |
| 107 gboolean is_enabled = false; |
| 108 dbus_g_proxy_call(proxy_, "isEnabled", &error_, |
| 109 G_TYPE_INVALID, |
| 110 G_TYPE_BOOLEAN, &is_enabled, |
| 111 G_TYPE_INVALID); |
| 112 if (CheckError() || !is_enabled) |
| 113 return false; |
| 114 |
| 115 // Get the wallet name. |
| 116 char* wallet_name = NULL; |
| 117 dbus_g_proxy_call(proxy_, "networkWallet", &error_, |
| 118 G_TYPE_INVALID, |
| 119 G_TYPE_STRING, &wallet_name, |
| 120 G_TYPE_INVALID); |
| 121 if (CheckError() || !wallet_name) |
| 122 return false; |
| 123 |
| 124 wallet_name_.assign(wallet_name); |
| 125 g_free(wallet_name); |
| 126 |
| 127 return true; |
| 128 } |
| 129 |
| 130 void PasswordStoreKWallet::AddLoginImpl(const PasswordForm& form) { |
| 131 AutoLock l(kwallet_lock_); |
| 132 int wallet_handle = WalletHandle(); |
| 133 if (wallet_handle == kInvalidKWalletHandle) |
| 134 return; |
| 135 |
| 136 PasswordFormList forms; |
| 137 GetLoginsList(&forms, form, wallet_handle); |
| 138 |
| 139 forms.push_back(const_cast<PasswordForm*>(&form)); |
| 140 |
| 141 SetLoginsList(forms, form, wallet_handle); |
| 142 } |
| 143 |
| 144 void PasswordStoreKWallet::UpdateLoginImpl(const PasswordForm& form) { |
| 145 AutoLock l(kwallet_lock_); |
| 146 int wallet_handle = WalletHandle(); |
| 147 if (wallet_handle == kInvalidKWalletHandle) |
| 148 return; |
| 149 |
| 150 PasswordFormList forms; |
| 151 GetLoginsList(&forms, form, wallet_handle); |
| 152 |
| 153 for (uint i = 0; i < forms.size(); ++i) { |
| 154 if (CompareForms(form, *forms[i])) { |
| 155 forms.erase(forms.begin() + i); |
| 156 forms.insert(forms.begin() + i, const_cast<PasswordForm*>(&form)); |
| 157 } |
| 158 } |
| 159 |
| 160 SetLoginsList(forms, form, wallet_handle); |
| 161 } |
| 162 |
| 163 void PasswordStoreKWallet::RemoveLoginImpl(const PasswordForm& form) { |
| 164 AutoLock l(kwallet_lock_); |
| 165 int wallet_handle = WalletHandle(); |
| 166 if (wallet_handle == kInvalidKWalletHandle) |
| 167 return; |
| 168 |
| 169 PasswordFormList forms; |
| 170 GetLoginsList(&forms, form, wallet_handle); |
| 171 |
| 172 for (uint i = 0; i < forms.size(); ++i) { |
| 173 if (CompareForms(form, *forms[i])) { |
| 174 forms.erase(forms.begin() + i); |
| 175 --i; |
| 176 } |
| 177 } |
| 178 |
| 179 if (forms.empty()) { |
| 180 // No items left? Remove the entry from the wallet. |
| 181 int ret = 0; |
| 182 dbus_g_proxy_call(proxy_, "removeEntry", &error_, |
| 183 G_TYPE_INT, wallet_handle, // handle |
| 184 G_TYPE_STRING, kKWalletFolder, // folder |
| 185 G_TYPE_STRING, form.signon_realm.c_str(), // key |
| 186 G_TYPE_STRING, kAppId, // appid |
| 187 G_TYPE_INVALID, |
| 188 G_TYPE_INT, &ret, |
| 189 G_TYPE_INVALID); |
| 190 CheckError(); |
| 191 if (ret) |
| 192 LOG(ERROR) << "Bad return code " << ret << " from kwallet removeEntry"; |
| 193 } else { |
| 194 // Otherwise update the entry in the wallet. |
| 195 SetLoginsList(forms, form, wallet_handle); |
| 196 } |
| 197 } |
| 198 |
| 199 void PasswordStoreKWallet::GetLoginsImpl(GetLoginsRequest* request) { |
| 200 PasswordFormList forms; |
| 201 |
| 202 AutoLock l(kwallet_lock_); |
| 203 int wallet_handle = WalletHandle(); |
| 204 if (wallet_handle != kInvalidKWalletHandle) |
| 205 GetLoginsList(&forms, request->form, wallet_handle); |
| 206 |
| 207 NotifyConsumer(request, forms); |
| 208 } |
| 209 |
| 210 void PasswordStoreKWallet::GetLoginsList(PasswordFormList* forms, |
| 211 const PasswordForm& key, |
| 212 int wallet_handle) { |
| 213 // Is there an entry in the wallet? |
| 214 gboolean has_entry = false; |
| 215 dbus_g_proxy_call(proxy_, "hasEntry", &error_, |
| 216 G_TYPE_INT, wallet_handle, // handle |
| 217 G_TYPE_STRING, kKWalletFolder, // folder |
| 218 G_TYPE_STRING, key.signon_realm.c_str(), // key |
| 219 G_TYPE_STRING, kAppId, // appid |
| 220 G_TYPE_INVALID, |
| 221 G_TYPE_BOOLEAN, &has_entry, |
| 222 G_TYPE_INVALID); |
| 223 |
| 224 if (CheckError() || !has_entry) |
| 225 return; |
| 226 |
| 227 GArray* byte_array = NULL; |
| 228 dbus_g_proxy_call(proxy_, "readEntry", &error_, |
| 229 G_TYPE_INT, wallet_handle, // handle |
| 230 G_TYPE_STRING, kKWalletFolder, // folder |
| 231 G_TYPE_STRING, key.signon_realm.c_str(), // key |
| 232 G_TYPE_STRING, kAppId, // appid |
| 233 G_TYPE_INVALID, |
| 234 DBUS_TYPE_G_UCHAR_ARRAY, &byte_array, |
| 235 G_TYPE_INVALID); |
| 236 |
| 237 if (CheckError() || !byte_array || !byte_array->len) |
| 238 return; |
| 239 |
| 240 Pickle pickle(byte_array->data, byte_array->len); |
| 241 DeserializeValue(key, pickle, forms); |
| 242 } |
| 243 |
| 244 void PasswordStoreKWallet::SetLoginsList(const PasswordFormList& forms, |
| 245 const PasswordForm& key, |
| 246 int wallet_handle) { |
| 247 Pickle value; |
| 248 SerializeValue(forms, &value); |
| 249 |
| 250 // Convert the pickled bytes to a GByteArray. |
| 251 GArray* byte_array = g_array_sized_new(false, false, sizeof(char), |
| 252 value.size()); |
| 253 g_array_append_vals(byte_array, value.data(), value.size()); |
| 254 |
| 255 // Make the call. |
| 256 int ret = 0; |
| 257 dbus_g_proxy_call(proxy_, "writeEntry", &error_, |
| 258 G_TYPE_INT, wallet_handle, // handle |
| 259 G_TYPE_STRING, kKWalletFolder, // folder |
| 260 G_TYPE_STRING, key.signon_realm.c_str(), // key |
| 261 DBUS_TYPE_G_UCHAR_ARRAY, byte_array, // value |
| 262 G_TYPE_STRING, kAppId, // appid |
| 263 G_TYPE_INVALID, |
| 264 G_TYPE_INT, &ret, |
| 265 G_TYPE_INVALID); |
| 266 g_array_free(byte_array, true); |
| 267 |
| 268 CheckError(); |
| 269 if (ret) |
| 270 LOG(ERROR) << "Bad return code " << ret << " from kwallet writeEntry"; |
| 271 } |
| 272 |
| 273 bool PasswordStoreKWallet::CompareForms(const PasswordForm& a, |
| 274 const PasswordForm& b) { |
| 275 return a.origin == b.origin && |
| 276 a.password_element == b.password_element && |
| 277 a.signon_realm == b.signon_realm && |
| 278 a.submit_element == b.submit_element && |
| 279 a.username_element == b.username_element && |
| 280 a.username_value == b.username_value; |
| 281 } |
| 282 |
| 283 void PasswordStoreKWallet::SerializeValue(const PasswordFormList& forms, |
| 284 Pickle* pickle) { |
| 285 pickle->WriteInt(forms.size()); |
| 286 for (PasswordFormList::const_iterator it = forms.begin() ; |
| 287 it != forms.end() ; ++it) { |
| 288 const PasswordForm* form = *it; |
| 289 pickle->WriteInt(form->scheme); |
| 290 pickle->WriteString(form->origin.spec()); |
| 291 pickle->WriteString(form->action.spec()); |
| 292 pickle->WriteWString(form->username_element); |
| 293 pickle->WriteWString(form->username_value); |
| 294 pickle->WriteWString(form->password_element); |
| 295 pickle->WriteWString(form->password_value); |
| 296 pickle->WriteWString(form->submit_element); |
| 297 pickle->WriteBool(form->ssl_valid); |
| 298 pickle->WriteBool(form->preferred); |
| 299 pickle->WriteBool(form->blacklisted_by_user); |
| 300 } |
| 301 } |
| 302 |
| 303 void PasswordStoreKWallet::DeserializeValue(const PasswordForm& key, |
| 304 const Pickle& pickle, |
| 305 PasswordFormList* forms) { |
| 306 void* iter = NULL; |
| 307 |
| 308 int count = 0; |
| 309 pickle.ReadInt(&iter, &count); |
| 310 |
| 311 for (int i = 0; i < count; ++i) { |
| 312 PasswordForm* form = new PasswordForm(); |
| 313 form->signon_realm.assign(key.signon_realm); |
| 314 |
| 315 pickle.ReadInt(&iter, reinterpret_cast<int*>(&form->scheme)); |
| 316 ReadGURL(pickle, &iter, &form->origin); |
| 317 ReadGURL(pickle, &iter, &form->action); |
| 318 pickle.ReadWString(&iter, &form->username_element); |
| 319 pickle.ReadWString(&iter, &form->username_value); |
| 320 pickle.ReadWString(&iter, &form->password_element); |
| 321 pickle.ReadWString(&iter, &form->password_value); |
| 322 pickle.ReadWString(&iter, &form->submit_element); |
| 323 pickle.ReadBool(&iter, &form->ssl_valid); |
| 324 pickle.ReadBool(&iter, &form->preferred); |
| 325 pickle.ReadBool(&iter, &form->blacklisted_by_user); |
| 326 forms->push_back(form); |
| 327 } |
| 328 } |
| 329 |
| 330 void PasswordStoreKWallet::ReadGURL(const Pickle& pickle, void** iter, |
| 331 GURL* url) { |
| 332 string url_string; |
| 333 pickle.ReadString(iter, &url_string); |
| 334 *url = GURL(url_string); |
| 335 } |
| 336 |
| 337 bool PasswordStoreKWallet::CheckError() { |
| 338 if (error_) { |
| 339 LOG(ERROR) << "Failed to complete KWallet call: " << error_->message; |
| 340 g_error_free(error_); |
| 341 error_ = NULL; |
| 342 return true; |
| 343 } |
| 344 return false; |
| 345 } |
| 346 |
| 347 int PasswordStoreKWallet::WalletHandle() { |
| 348 // Open the wallet. |
| 349 int handle = kInvalidKWalletHandle; |
| 350 dbus_g_proxy_call(proxy_, "open", &error_, |
| 351 G_TYPE_STRING, wallet_name_.c_str(), // wallet |
| 352 G_TYPE_INT64, 0LL, // wid |
| 353 G_TYPE_STRING, kAppId, // appid |
| 354 G_TYPE_INVALID, |
| 355 G_TYPE_INT, &handle, |
| 356 G_TYPE_INVALID); |
| 357 if (CheckError() || handle == kInvalidKWalletHandle) |
| 358 return kInvalidKWalletHandle; |
| 359 |
| 360 // Check if our folder exists. |
| 361 gboolean has_folder = false; |
| 362 dbus_g_proxy_call(proxy_, "hasFolder", &error_, |
| 363 G_TYPE_INT, handle, // handle |
| 364 G_TYPE_STRING, kKWalletFolder, // folder |
| 365 G_TYPE_STRING, kAppId, // appid |
| 366 G_TYPE_INVALID, |
| 367 G_TYPE_BOOLEAN, &has_folder, |
| 368 G_TYPE_INVALID); |
| 369 if (CheckError()) |
| 370 return kInvalidKWalletHandle; |
| 371 |
| 372 // Create it if it didn't. |
| 373 if (!has_folder) { |
| 374 gboolean success = false; |
| 375 dbus_g_proxy_call(proxy_, "createFolder", &error_, |
| 376 G_TYPE_INT, handle, // handle |
| 377 G_TYPE_STRING, kKWalletFolder, // folder |
| 378 G_TYPE_STRING, kAppId, // appid |
| 379 G_TYPE_INVALID, |
| 380 G_TYPE_BOOLEAN, &success, |
| 381 G_TYPE_INVALID); |
| 382 if (CheckError() || !success) |
| 383 return kInvalidKWalletHandle; |
| 384 } |
| 385 |
| 386 return handle; |
| 387 } |
OLD | NEW |