| OLD | NEW |
| (Empty) |
| 1 // Copyright 2007-2010 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 | |
| 16 #include "omaha/net/network_config.h" | |
| 17 | |
| 18 #include <winhttp.h> | |
| 19 #include <atlconv.h> | |
| 20 #include <atlsecurity.h> | |
| 21 #include <algorithm> | |
| 22 #include <hash_set> | |
| 23 #include <vector> | |
| 24 #include "base/error.h" | |
| 25 #include "base/scoped_ptr.h" | |
| 26 #include "base/scope_guard.h" | |
| 27 #include "omaha/base/browser_utils.h" | |
| 28 #include "omaha/base/const_object_names.h" | |
| 29 #include "omaha/base/constants.h" | |
| 30 #include "omaha/base/debug.h" | |
| 31 #include "omaha/base/error.h" | |
| 32 #include "omaha/base/encrypt.h" | |
| 33 #include "omaha/base/logging.h" | |
| 34 #include "omaha/base/omaha_version.h" | |
| 35 #include "omaha/base/path.h" | |
| 36 #include "omaha/base/reg_key.h" | |
| 37 #include "omaha/base/scoped_ptr_address.h" | |
| 38 #include "omaha/base/string.h" | |
| 39 #include "omaha/base/system.h" | |
| 40 #include "omaha/base/user_info.h" | |
| 41 #include "omaha/base/utils.h" | |
| 42 #include "omaha/common/config_manager.h" | |
| 43 #include "omaha/net/cup_request.h" | |
| 44 #include "omaha/net/http_client.h" | |
| 45 | |
| 46 using omaha::encrypt::EncryptData; | |
| 47 using omaha::encrypt::DecryptData; | |
| 48 | |
| 49 namespace omaha { | |
| 50 | |
| 51 // Computes the hash value of a ProxyConfig object. Names in the stdext | |
| 52 // namespace are not currently part of the ISO C++ standard. | |
| 53 uint32 hash_value(const ProxyConfig& config) { | |
| 54 uint32 hash = stdext::hash_value(config.auto_detect) ^ | |
| 55 stdext::hash_value(config.auto_config_url.GetString()) ^ | |
| 56 stdext::hash_value(config.proxy.GetString()) ^ | |
| 57 stdext::hash_value(config.proxy_bypass.GetString()); | |
| 58 return hash; | |
| 59 } | |
| 60 | |
| 61 const TCHAR* const NetworkConfigManager::kNetworkSubkey = _T("network"); | |
| 62 const TCHAR* const NetworkConfigManager::kNetworkCupSubkey = _T("secure"); | |
| 63 const TCHAR* const NetworkConfigManager::kCupClientSecretKey = _T("sk"); | |
| 64 const TCHAR* const NetworkConfigManager::kCupClientCookie = _T("c"); | |
| 65 | |
| 66 const TCHAR* const NetworkConfig::kUserAgent = _T("Google Update/%s"); | |
| 67 | |
| 68 const TCHAR* const NetworkConfig::kRegKeyProxy = GOOPDATE_MAIN_KEY _T("proxy"); | |
| 69 const TCHAR* const NetworkConfig::kRegValueSource = _T("source"); | |
| 70 | |
| 71 const TCHAR* const NetworkConfig::kWPADIdentifier = _T("auto"); | |
| 72 const TCHAR* const NetworkConfig::kDirectConnectionIdentifier = _T("direct"); | |
| 73 | |
| 74 NetworkConfig::NetworkConfig(bool is_machine) | |
| 75 : is_machine_(is_machine), | |
| 76 is_initialized_(false) {} | |
| 77 | |
| 78 NetworkConfig::~NetworkConfig() { | |
| 79 if (session_.session_handle && http_client_.get()) { | |
| 80 http_client_->Close(session_.session_handle); | |
| 81 session_.session_handle = NULL; | |
| 82 } | |
| 83 Clear(); | |
| 84 } | |
| 85 | |
| 86 // Initialize creates or opens a global lock to synchronize access to | |
| 87 // registry where CUP credentials are stored. Each user including non-elevated | |
| 88 // admins stores network configuration data, such as the CUP password in | |
| 89 // its HKCU. The admin users, including the LOCAL_SYSTEM, store data in HKLM. | |
| 90 // Therefore, the naming of the global lock is different: users have their | |
| 91 // lock postfixed with their sid, so the serialization only occurs within the | |
| 92 // same user's programs. Admin users use the same named lock since they store | |
| 93 // data in a shared HKLM. The data of the admin users is disambiguated by | |
| 94 // postfixing their registry sub key with sids. | |
| 95 // In conclusion, users have sid-postfixed locks and their data goes in | |
| 96 // their respective HKCU. Admin users have the same lock and their data goes | |
| 97 // under HKLM in sid-postfixed stores. | |
| 98 // | |
| 99 // The named lock is created in the global namespace to account for users | |
| 100 // logging in from different TS sessions. | |
| 101 // | |
| 102 // The CUP credentials must be protected with ACLs so non-elevated admins can't | |
| 103 // read elevated-admins' keys and attack the protocol. | |
| 104 // | |
| 105 // Also, an Internet session is created. | |
| 106 HRESULT NetworkConfig::Initialize() { | |
| 107 ASSERT1(!is_initialized_); | |
| 108 | |
| 109 http_client_.reset(CreateHttpClient()); | |
| 110 ASSERT1(http_client_.get()); | |
| 111 if (!http_client_.get()) { | |
| 112 NET_LOG(LE, (_T("[CreateHttpClient failed]"))); | |
| 113 return E_UNEXPECTED; | |
| 114 } | |
| 115 HRESULT hr = http_client_->Initialize(); | |
| 116 if (FAILED(hr)) { | |
| 117 // TODO(omaha): This makes an assumption that only WinHttp is | |
| 118 // supported by the network code. | |
| 119 NET_LOG(LE, (_T("[http_client_->Initialize() failed][0x%x]"), hr)); | |
| 120 return OMAHA_NET_E_WINHTTP_NOT_AVAILABLE; | |
| 121 } | |
| 122 | |
| 123 // Initializes the WinHttp session and configures WinHttp to work in | |
| 124 // asynchronous mode. In this mode, the network requests are non-blocking | |
| 125 // and asynchronous events are generated when a request is complete. | |
| 126 hr = http_client_->Open(NULL, | |
| 127 WINHTTP_ACCESS_TYPE_NO_PROXY, | |
| 128 WINHTTP_NO_PROXY_NAME, | |
| 129 WINHTTP_NO_PROXY_BYPASS, | |
| 130 WINHTTP_FLAG_ASYNC, | |
| 131 &session_.session_handle); | |
| 132 if (FAILED(hr)) { | |
| 133 NET_LOG(LE, (_T("[http_client_->Open() failed][0x%x]"), hr)); | |
| 134 return hr; | |
| 135 } | |
| 136 | |
| 137 Add(new UpdateDevProxyDetector); | |
| 138 BrowserType browser_type(BROWSER_UNKNOWN); | |
| 139 GetDefaultBrowserType(&browser_type); | |
| 140 if (browser_type == BROWSER_FIREFOX) { | |
| 141 Add(new FirefoxProxyDetector); | |
| 142 } | |
| 143 // There is no Chrome detector because it uses the same proxy settings as IE. | |
| 144 Add(new IEProxyDetector); | |
| 145 Add(new DefaultProxyDetector); | |
| 146 | |
| 147 // Use a global network configuration override if available. | |
| 148 ConfigManager* config_manager = ConfigManager::Instance(); | |
| 149 CString net_config; | |
| 150 if (SUCCEEDED(config_manager->GetNetConfig(&net_config))) { | |
| 151 ProxyConfig config_override = NetworkConfig::ParseNetConfig(net_config); | |
| 152 SetConfigurationOverride(&config_override); | |
| 153 } | |
| 154 | |
| 155 ConfigureProxyAuth(); | |
| 156 | |
| 157 is_initialized_ = true; | |
| 158 return S_OK; | |
| 159 } | |
| 160 | |
| 161 void NetworkConfig::Add(ProxyDetectorInterface* detector) { | |
| 162 ASSERT1(detector); | |
| 163 __mutexBlock(lock_) { | |
| 164 detectors_.push_back(detector); | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 void NetworkConfig::Clear() { | |
| 169 __mutexBlock(lock_) { | |
| 170 for (size_t i = 0; i != detectors_.size(); ++i) { | |
| 171 delete detectors_[i]; | |
| 172 } | |
| 173 detectors_.clear(); | |
| 174 configurations_.clear(); | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 HRESULT NetworkConfig::Detect() { | |
| 179 __mutexBlock(lock_) { | |
| 180 std::vector<ProxyConfig> configurations; | |
| 181 | |
| 182 for (size_t i = 0; i != detectors_.size(); ++i) { | |
| 183 ProxyConfig config; | |
| 184 if (SUCCEEDED(detectors_[i]->Detect(&config))) { | |
| 185 configurations.push_back(config); | |
| 186 } | |
| 187 } | |
| 188 configurations_.swap(configurations); | |
| 189 } | |
| 190 | |
| 191 return S_OK; | |
| 192 } | |
| 193 | |
| 194 void NetworkConfig::SortProxies(std::vector<ProxyConfig>* configurations) { | |
| 195 ASSERT1(configurations); | |
| 196 | |
| 197 std::stable_sort(configurations->begin(), configurations->end(), | |
| 198 ProxySortPredicate); | |
| 199 } | |
| 200 | |
| 201 HRESULT NetworkConfig::ConfigFromIdentifier(const CString& id, | |
| 202 ProxyConfig* config) { | |
| 203 ASSERT1(config); | |
| 204 | |
| 205 *config = ProxyConfig(); | |
| 206 if (id == kWPADIdentifier) { | |
| 207 config->source = kWPADIdentifier; | |
| 208 config->auto_detect = true; | |
| 209 } else if (id == kDirectConnectionIdentifier) { | |
| 210 config->source = kDirectConnectionIdentifier; | |
| 211 } else { | |
| 212 return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); | |
| 213 } | |
| 214 | |
| 215 return S_OK; | |
| 216 } | |
| 217 | |
| 218 void NetworkConfig::AppendLastKnownGoodProxyConfig( | |
| 219 std::vector<ProxyConfig>* configurations) const { | |
| 220 ASSERT1(configurations); | |
| 221 ProxyConfig last_known_good_config; | |
| 222 if (SUCCEEDED(LoadProxyConfig(&last_known_good_config))) { | |
| 223 configurations->push_back(last_known_good_config); | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 void NetworkConfig::AppendStaticProxyConfigs( | |
| 228 std::vector<ProxyConfig>* configurations) { | |
| 229 ASSERT1(configurations); | |
| 230 ProxyConfig config; | |
| 231 | |
| 232 HRESULT hr = ConfigFromIdentifier(kWPADIdentifier, &config); | |
| 233 if (SUCCEEDED(hr)) { | |
| 234 configurations->push_back(config); | |
| 235 } | |
| 236 | |
| 237 hr = ConfigFromIdentifier(kDirectConnectionIdentifier, &config); | |
| 238 if (SUCCEEDED(hr)) { | |
| 239 configurations->push_back(config); | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 HRESULT NetworkConfig::Detect(const CString& proxy_source, | |
| 244 ProxyConfig* config) const { | |
| 245 ASSERT1(config); | |
| 246 __mutexBlock(lock_) { | |
| 247 std::vector<ProxyConfig> configurations; | |
| 248 for (size_t i = 0; i != detectors_.size(); ++i) { | |
| 249 if (proxy_source == detectors_[i]->source()) { | |
| 250 return detectors_[i]->Detect(config); | |
| 251 } | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); | |
| 256 } | |
| 257 | |
| 258 std::vector<ProxyConfig> NetworkConfig::GetConfigurations() const { | |
| 259 std::vector<ProxyConfig> configurations; | |
| 260 __mutexBlock(lock_) { | |
| 261 configurations = configurations_; | |
| 262 } | |
| 263 return configurations; | |
| 264 } | |
| 265 | |
| 266 HRESULT NetworkConfig::GetConfigurationOverride(ProxyConfig* config) { | |
| 267 ASSERT1(config); | |
| 268 __mutexBlock(lock_) { | |
| 269 if (configuration_override_.get()) { | |
| 270 *config = *configuration_override_; | |
| 271 return S_OK; | |
| 272 } | |
| 273 } | |
| 274 return E_FAIL; | |
| 275 } | |
| 276 | |
| 277 void NetworkConfig::SetConfigurationOverride( | |
| 278 const ProxyConfig* configuration_override) { | |
| 279 __mutexBlock(lock_) { | |
| 280 if (configuration_override) { | |
| 281 configuration_override_.reset(new ProxyConfig); | |
| 282 *configuration_override_ = *configuration_override; | |
| 283 configuration_override_->source = _T("updatedev/netconfig"); | |
| 284 } else { | |
| 285 configuration_override_.reset(); | |
| 286 } | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 HRESULT NetworkConfig::GetCupCredentials( | |
| 291 CupCredentials* cup_credentials) const { | |
| 292 return NetworkConfigManager::Instance().GetCupCredentials(cup_credentials); | |
| 293 } | |
| 294 | |
| 295 HRESULT NetworkConfig::SetCupCredentials( | |
| 296 const CupCredentials* cup_credentials) const { | |
| 297 NetworkConfigManager& network_manager = NetworkConfigManager::Instance(); | |
| 298 if (cup_credentials == NULL) { | |
| 299 network_manager.ClearCupCredentials(); | |
| 300 return S_OK; | |
| 301 } | |
| 302 | |
| 303 return network_manager.SetCupCredentials(*cup_credentials); | |
| 304 } | |
| 305 | |
| 306 // Serializes configurations for debugging purposes. | |
| 307 CString NetworkConfig::ToString(const ProxyConfig& config) { | |
| 308 CString result; | |
| 309 result.AppendFormat(_T("priority=%u, source=%s, "), | |
| 310 config.priority, config.source); | |
| 311 | |
| 312 switch (GetAccessType(config)) { | |
| 313 case WINHTTP_ACCESS_TYPE_NO_PROXY: | |
| 314 result.AppendFormat(_T("direct connection")); | |
| 315 break; | |
| 316 case WINHTTP_ACCESS_TYPE_NAMED_PROXY: | |
| 317 result.AppendFormat(_T("named proxy=%s, bypass=%s"), | |
| 318 config.proxy, config.proxy_bypass); | |
| 319 break; | |
| 320 case WINHTTP_ACCESS_TYPE_AUTO_DETECT: | |
| 321 result.AppendFormat(_T("wpad=%d, script=%s"), | |
| 322 config.auto_detect, config.auto_config_url); | |
| 323 break; | |
| 324 default: | |
| 325 ASSERT1(false); | |
| 326 break; | |
| 327 } | |
| 328 return result; | |
| 329 } | |
| 330 | |
| 331 CString NetworkConfig::ToString(const std::vector<ProxyConfig>& config) { | |
| 332 CString result; | |
| 333 for (size_t i = 0; i != config.size(); ++i) { | |
| 334 result.Append(NetworkConfig::ToString(config[i])); | |
| 335 result.Append(_T("\r\n")); | |
| 336 } | |
| 337 return result; | |
| 338 } | |
| 339 | |
| 340 int NetworkConfig::GetAccessType(const ProxyConfig& config) { | |
| 341 if (config.auto_detect || !config.auto_config_url.IsEmpty()) { | |
| 342 return WINHTTP_ACCESS_TYPE_AUTO_DETECT; | |
| 343 } else if (!config.proxy.IsEmpty()) { | |
| 344 return WINHTTP_ACCESS_TYPE_NAMED_PROXY; | |
| 345 } else { | |
| 346 return WINHTTP_ACCESS_TYPE_NO_PROXY; | |
| 347 } | |
| 348 } | |
| 349 | |
| 350 bool NetworkConfig::IsUsingCupTestKeys() { | |
| 351 DWORD value = 0; | |
| 352 if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV, | |
| 353 kRegValueCupKeys, | |
| 354 &value))) { | |
| 355 return value != 0; | |
| 356 } else { | |
| 357 return false; | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 void NetworkConfig::ConfigureProxyAuth() { | |
| 362 const uint32 kProxyMaxPrompts = 1; | |
| 363 return proxy_auth_.ConfigureProxyAuth(is_machine_, kProxyMaxPrompts); | |
| 364 } | |
| 365 | |
| 366 bool NetworkConfig::GetProxyCredentials(bool allow_ui, | |
| 367 bool force_ui, | |
| 368 const CString& proxy_settings, | |
| 369 const ProxyAuthConfig& config, | |
| 370 bool is_https, | |
| 371 CString* username, | |
| 372 CString* password, | |
| 373 uint32* auth_scheme) { | |
| 374 ASSERT1(username); | |
| 375 ASSERT1(password); | |
| 376 ASSERT1(auth_scheme); | |
| 377 | |
| 378 const CString& proxy = ProxyAuth::ExtractProxy(proxy_settings, is_https); | |
| 379 return proxy_auth_.GetProxyCredentials(allow_ui, force_ui, proxy, | |
| 380 config, username, | |
| 381 password, auth_scheme); | |
| 382 } | |
| 383 | |
| 384 HRESULT NetworkConfig::SetProxyAuthScheme(const CString& proxy_settings, | |
| 385 bool is_https, | |
| 386 uint32 auth_scheme) { | |
| 387 ASSERT1(auth_scheme != UNKNOWN_AUTH_SCHEME); | |
| 388 const CString& proxy = ProxyAuth::ExtractProxy(proxy_settings, is_https); | |
| 389 return proxy_auth_.SetProxyAuthScheme(proxy, auth_scheme); | |
| 390 } | |
| 391 | |
| 392 // TODO(omaha): the code does WPAD auto detect in all cases. It is possible for | |
| 393 // a configuration to specify no auto detection but provide the proxy script | |
| 394 // url. The current code does not account for this yet. | |
| 395 HRESULT NetworkConfig::GetProxyForUrl(const CString& url, | |
| 396 const CString& auto_config_url, | |
| 397 HttpClient::ProxyInfo* proxy_info) { | |
| 398 ASSERT1(proxy_info); | |
| 399 | |
| 400 NET_LOG(L3, (_T("[NetworkConfig::GetProxyForUrl][%s]"), url)); | |
| 401 | |
| 402 HttpClient::AutoProxyOptions auto_proxy_options = {0}; | |
| 403 auto_proxy_options.flags = WINHTTP_AUTOPROXY_AUTO_DETECT; | |
| 404 auto_proxy_options.auto_detect_flags = WINHTTP_AUTO_DETECT_TYPE_DHCP | | |
| 405 WINHTTP_AUTO_DETECT_TYPE_DNS_A; | |
| 406 if (!auto_config_url.IsEmpty()) { | |
| 407 auto_proxy_options.auto_config_url = auto_config_url; | |
| 408 auto_proxy_options.flags |= WINHTTP_AUTOPROXY_CONFIG_URL; | |
| 409 } | |
| 410 auto_proxy_options.auto_logon_if_challenged = true; | |
| 411 | |
| 412 HRESULT hr = http_client_->GetProxyForUrl(session_.session_handle, | |
| 413 url, | |
| 414 &auto_proxy_options, | |
| 415 proxy_info); | |
| 416 | |
| 417 if (FAILED(hr) && ::UrlIsFileUrl(auto_config_url)) { | |
| 418 // Some HttpClient implementations, namely WinHTTP, only support PAC files | |
| 419 // with http or https schemes. Attempt an alternate resolution scheme using | |
| 420 // jsproxy.dll if the initial attempt fails. | |
| 421 ASSERT1(user_info::IsThreadImpersonating()); | |
| 422 | |
| 423 CString local_file; | |
| 424 hr = ConvertFileUriToLocalPath(auto_config_url, &local_file); | |
| 425 if (FAILED(hr)) { | |
| 426 NET_LOG(LE, (_T("[ConvertFileUriToLocalPath failed][0x%08x]"), hr)); | |
| 427 return hr; | |
| 428 } | |
| 429 | |
| 430 hr = GetProxyForUrlLocal(url, local_file, proxy_info); | |
| 431 } | |
| 432 | |
| 433 return hr; | |
| 434 } | |
| 435 | |
| 436 CString NetworkConfig::GetUserAgent() { | |
| 437 CString user_agent; | |
| 438 user_agent.Format(kUserAgent, GetVersionString()); | |
| 439 return user_agent; | |
| 440 } | |
| 441 | |
| 442 CString NetworkConfig::GetMID() { | |
| 443 CString mid; | |
| 444 RegKey::GetValue(MACHINE_REG_UPDATE_DEV, kRegValueMID, &mid); | |
| 445 return mid; | |
| 446 } | |
| 447 | |
| 448 CString NetworkConfig::JoinStrings(const TCHAR* s1, | |
| 449 const TCHAR* s2, | |
| 450 const TCHAR* delim) { | |
| 451 CString result; | |
| 452 const TCHAR* components[] = {s1, s2}; | |
| 453 JoinStringsInArray(components, arraysize(components), delim, &result); | |
| 454 return result; | |
| 455 } | |
| 456 | |
| 457 // Using std::hash_set adds about 2K uncompressed code size. Using a CAtlMap | |
| 458 // adds about 1.5K. Usually, there are only five detected configurations so | |
| 459 // an O(n^2) algorithm would work well. The advantage of the current | |
| 460 // implementation is simplicity. It also does not handle conflicts. Conflicts | |
| 461 // are not expected, due to how the ProxyConfig structure is being used. | |
| 462 // TODO(omaha): consider not using the hash_set and save about 1K of code. | |
| 463 void NetworkConfig::RemoveDuplicates(std::vector<ProxyConfig>* config) { | |
| 464 ASSERT1(config); | |
| 465 | |
| 466 // Iterate over the input configurations, remember the hash of each | |
| 467 // distinct configuration, and remove the duplicates by skipping the | |
| 468 // configurations seen before. | |
| 469 std::vector<ProxyConfig> input(*config); | |
| 470 config->clear(); | |
| 471 | |
| 472 typedef stdext::hash_set<uint32> Keys; | |
| 473 Keys keys; | |
| 474 for (size_t i = 0; i != input.size(); ++i) { | |
| 475 std::pair<Keys::iterator, bool> result(keys.insert(hash_value(input[i]))); | |
| 476 if (result.second) { | |
| 477 config->push_back(input[i]); | |
| 478 } | |
| 479 } | |
| 480 } | |
| 481 | |
| 482 HRESULT NetworkConfig::CreateProxyConfigRegKey(RegKey* key) { | |
| 483 ASSERT1(key); | |
| 484 CString config_root; | |
| 485 | |
| 486 if (!user_info::IsRunningAsSystem()) { | |
| 487 scoped_hkey user_root_key; | |
| 488 HRESULT hr = ::RegOpenCurrentUser(KEY_READ | KEY_WRITE, | |
| 489 address(user_root_key)); | |
| 490 if (FAILED(hr)) { | |
| 491 return hr; | |
| 492 } | |
| 493 return key->Create(get(user_root_key), kRegKeyProxy); | |
| 494 } else { | |
| 495 return key->Create(HKEY_LOCAL_MACHINE, kRegKeyProxy); | |
| 496 } | |
| 497 } | |
| 498 | |
| 499 HRESULT NetworkConfig::SaveProxyConfig(const ProxyConfig& config) { | |
| 500 const CString& new_configuration = config.source; | |
| 501 NET_LOG(L3, (_T("[NetworkConfig::SaveProxyConfig][%s]"), new_configuration)); | |
| 502 | |
| 503 RegKey key; | |
| 504 HRESULT hr = CreateProxyConfigRegKey(&key); | |
| 505 if (FAILED(hr)) { | |
| 506 return hr; | |
| 507 } | |
| 508 | |
| 509 CString current_configuration; | |
| 510 if (SUCCEEDED(key.GetValue(kRegValueSource, ¤t_configuration)) && | |
| 511 current_configuration != new_configuration) { | |
| 512 NET_LOG(L3, (_T("[Network configuration changed from %s to %s"), | |
| 513 current_configuration, new_configuration)); | |
| 514 } | |
| 515 | |
| 516 return key.SetValue(kRegValueSource, new_configuration); | |
| 517 } | |
| 518 | |
| 519 HRESULT NetworkConfig::LoadProxyConfig(ProxyConfig* config) const { | |
| 520 ASSERT1(config); | |
| 521 | |
| 522 *config = ProxyConfig(); | |
| 523 | |
| 524 RegKey key; | |
| 525 HRESULT hr = CreateProxyConfigRegKey(&key); | |
| 526 if (FAILED(hr)) { | |
| 527 return hr; | |
| 528 } | |
| 529 | |
| 530 CString source; | |
| 531 hr = key.GetValue(kRegValueSource, &source); | |
| 532 if (FAILED(hr)) { | |
| 533 return hr; | |
| 534 } | |
| 535 | |
| 536 hr = NetworkConfig::ConfigFromIdentifier(source, config); | |
| 537 if (FAILED(hr)) { | |
| 538 hr = Detect(source, config); | |
| 539 if (FAILED(hr)) { | |
| 540 return hr; | |
| 541 } | |
| 542 } | |
| 543 | |
| 544 config->priority = ProxyConfig::PROXY_PRIORITY_LAST_KNOWN_GOOD; | |
| 545 | |
| 546 return S_OK; | |
| 547 } | |
| 548 | |
| 549 ProxyConfig NetworkConfig::ParseNetConfig(const CString& net_config) { | |
| 550 ProxyConfig config; | |
| 551 int pos(0); | |
| 552 CString token = net_config.Tokenize(_T(";"), pos); | |
| 553 while (pos != -1) { | |
| 554 CString name, value; | |
| 555 if (ParseNameValuePair(token, _T('='), &name, &value)) { | |
| 556 bool auto_detect(false); | |
| 557 if (name == _T("wpad") && | |
| 558 SUCCEEDED(String_StringToBool(value, &auto_detect))) { | |
| 559 config.auto_detect = auto_detect; | |
| 560 } else if (name == _T("script")) { | |
| 561 config.auto_config_url = value; | |
| 562 } else if (name == _T("proxy")) { | |
| 563 config.proxy = value; | |
| 564 } | |
| 565 } | |
| 566 token = net_config.Tokenize(_T(";"), pos); | |
| 567 } | |
| 568 return config; | |
| 569 } | |
| 570 | |
| 571 // Note: The jsproxy functions are exposed to public users as part of the | |
| 572 // DOJ consent decree and are not formally supported by Microsoft. | |
| 573 | |
| 574 GPA_WRAP(jsproxy.dll, | |
| 575 InternetInitializeAutoProxyDll, | |
| 576 (DWORD dwVersion, LPSTR lpszDownloadedTempFile, LPSTR lpszMime, LPCVOID
lpAutoProxyCallbacks, LPCVOID lpAutoProxyScriptBuffer), // NOLINT | |
| 577 (dwVersion, lpszDownloadedTempFile, lpszMime, lpAutoProxyCallbacks, lpA
utoProxyScriptBuffer), // NOLINT | |
| 578 WINAPI, | |
| 579 BOOL, | |
| 580 FALSE); | |
| 581 | |
| 582 GPA_WRAP(jsproxy.dll, | |
| 583 InternetGetProxyInfo, | |
| 584 (LPCSTR lpszUrl, DWORD dwUrlLength, LPSTR lpszUrlHostName, DWORD dwUrlH
ostNameLength, LPSTR *lplpszProxyHostName, LPDWORD lpdwProxyHostNameLength), //
NOLINT | |
| 585 (lpszUrl, dwUrlLength, lpszUrlHostName, dwUrlHostNameLength, lplpszProx
yHostName, lpdwProxyHostNameLength), // NOLINT | |
| 586 WINAPI, | |
| 587 BOOL, | |
| 588 FALSE); | |
| 589 | |
| 590 GPA_WRAP(jsproxy.dll, | |
| 591 InternetDeInitializeAutoProxyDll, | |
| 592 (LPSTR lpszMime, DWORD dwReserved), | |
| 593 (lpszMime, dwReserved), | |
| 594 WINAPI, | |
| 595 BOOL, | |
| 596 FALSE); | |
| 597 | |
| 598 HRESULT NetworkConfig::GetProxyForUrlLocal(const CString& url, | |
| 599 const CString& path_to_pac_file, | |
| 600 HttpClient::ProxyInfo* proxy_info) { | |
| 601 scoped_library jsproxy_lib(::LoadLibrary(_T("jsproxy.dll"))); | |
| 602 ASSERT1(jsproxy_lib); | |
| 603 if (!jsproxy_lib) { | |
| 604 HRESULT hr = HRESULTFromLastError(); | |
| 605 NET_LOG(LE, (_T("[GetProxyForUrlLocal][jsproxy not loaded][0x%08x]"), hr)); | |
| 606 return hr; | |
| 607 } | |
| 608 | |
| 609 // Convert the inputs to ANSI, and call into JSProxy to execute the PAC | |
| 610 // script; we should get back out a PAC-format list of proxies to use. | |
| 611 // | |
| 612 // TODO(omaha3): The MSDN prototypes specify LPSTR, and I've assumed this | |
| 613 // implies CP_ACP. However, depending on how this was implemented internally, | |
| 614 // conversion to UTF8 might work better. Investigate this later and confirm. | |
| 615 CStringA path_a(path_to_pac_file); | |
| 616 | |
| 617 if (FALSE == InternetInitializeAutoProxyDllWrap(0, CStrBufA(path_a, MAX_PATH), | |
| 618 NULL, NULL, NULL)) { | |
| 619 HRESULT hr = HRESULTFromLastError(); | |
| 620 NET_LOG(LE, (_T("[GetProxyForUrlLocal][jsproxy init failed][0x%08x]"), hr)); | |
| 621 return hr; | |
| 622 } | |
| 623 | |
| 624 ON_SCOPE_EXIT(InternetDeInitializeAutoProxyDllWrap, (LPSTR)NULL, 0); | |
| 625 | |
| 626 CStringA url_a(url); | |
| 627 CStringA url_hostname_a(GetUriHostNameHostOnly(url, false)); | |
| 628 | |
| 629 scoped_hglobal proxy_ptr; | |
| 630 DWORD proxy_len = 0; | |
| 631 if (FALSE == InternetGetProxyInfoWrap( | |
| 632 url_a, | |
| 633 url_a.GetLength(), | |
| 634 CStrBufA(url_hostname_a, url_hostname_a.GetLength()), | |
| 635 url_hostname_a.GetLength(), | |
| 636 reinterpret_cast<LPSTR*>(address(proxy_ptr)), | |
| 637 &proxy_len)) { | |
| 638 HRESULT hr = HRESULTFromLastError(); | |
| 639 NET_LOG(LE, (_T("[GetProxyForUrlLocal][jsproxy failed][0x%08x]"), hr)); | |
| 640 return hr; | |
| 641 } | |
| 642 | |
| 643 ASSERT1(proxy_ptr && proxy_len > 0); | |
| 644 CStringA proxy(reinterpret_cast<LPSTR>(get(proxy_ptr)), proxy_len); | |
| 645 ConvertPacResponseToProxyInfo(proxy, proxy_info); | |
| 646 return S_OK; | |
| 647 } | |
| 648 | |
| 649 void NetworkConfig::ConvertPacResponseToProxyInfo( | |
| 650 const CStringA& response, | |
| 651 HttpClient::ProxyInfo* proxy_info) { | |
| 652 ASSERT1(proxy_info); | |
| 653 | |
| 654 NET_LOG(L4, (_T("[ConvertPacResponseToProxyInfo][%s]"), CString(response))); | |
| 655 | |
| 656 // The proxy list response from a PAC file for a file is a string of proxies | |
| 657 // to attempt in order, delimited by semicolons, with a keyword denoting how | |
| 658 // to use the proxy. For example: | |
| 659 // | |
| 660 // PROXY prx1.samp.com; PROXY prx2.test.com:8080; SOCKS prx3.test.com; DIRECT | |
| 661 // | |
| 662 // We convert this to a direct semicolon-separated list of host/ports. We | |
| 663 // stop parsing if we see DIRECT; we omit any non-PROXY entries. | |
| 664 CString proxy_list; | |
| 665 for (int start = 0; start >= 0 && start < response.GetLength();) { | |
| 666 int semi_pos = response.Find(';', start); | |
| 667 if (semi_pos < 0) { | |
| 668 semi_pos = response.GetLength(); | |
| 669 } | |
| 670 | |
| 671 CStringA entry = response.Mid(start, semi_pos - start).Trim().MakeLower(); | |
| 672 if (entry == "direct") { | |
| 673 break; | |
| 674 } | |
| 675 if (0 == entry.Find("proxy ")) { | |
| 676 // This is a valid proxy entry. Strip the leading "PROXY " and add it | |
| 677 // to our parsed list. | |
| 678 if (!proxy_list.IsEmpty()) { | |
| 679 proxy_list.AppendChar(_T(';')); | |
| 680 } | |
| 681 proxy_list.Append(CString(entry.Mid(6))); | |
| 682 } | |
| 683 | |
| 684 start = semi_pos + 1; | |
| 685 } | |
| 686 | |
| 687 if (proxy_list.IsEmpty()) { | |
| 688 proxy_info->access_type = WINHTTP_ACCESS_TYPE_NO_PROXY; | |
| 689 proxy_info->proxy = NULL; | |
| 690 proxy_info->proxy_bypass = NULL; | |
| 691 } else { | |
| 692 // The convention is that any strings in a WINHTTP_PROXY_INFO are expected | |
| 693 // to be freed by the caller using GlobalFree(). Convert our intermediary | |
| 694 // CString to a GlobalAlloc() buffer and write that out. | |
| 695 size_t list_len = (proxy_list.GetLength() + 1) * sizeof(TCHAR); | |
| 696 TCHAR* list_hglob = reinterpret_cast<TCHAR*>(::GlobalAlloc(GPTR, list_len)); | |
| 697 memcpy(list_hglob, proxy_list.GetString(), list_len); | |
| 698 proxy_info->access_type = WINHTTP_ACCESS_TYPE_NAMED_PROXY; | |
| 699 proxy_info->proxy = list_hglob; | |
| 700 proxy_info->proxy_bypass = NULL; | |
| 701 } | |
| 702 } | |
| 703 | |
| 704 const NetworkConfigManager* const NetworkConfigManager::kInvalidInstance = | |
| 705 reinterpret_cast<const NetworkConfigManager* const>(-1); | |
| 706 NetworkConfigManager* NetworkConfigManager::instance_ = NULL; | |
| 707 LLock NetworkConfigManager::instance_lock_; | |
| 708 bool NetworkConfigManager::is_machine_ = false; | |
| 709 | |
| 710 NetworkConfigManager::NetworkConfigManager() { | |
| 711 } | |
| 712 | |
| 713 NetworkConfigManager::~NetworkConfigManager() { | |
| 714 SaveCupCredentialsToRegistry(); | |
| 715 } | |
| 716 | |
| 717 HRESULT NetworkConfigManager::CreateInstance() { | |
| 718 __mutexScope(instance_lock_); | |
| 719 ASSERT1(instance_ != kInvalidInstance); | |
| 720 if (!instance_) { | |
| 721 NET_LOG(L1, (_T("[NetworkConfigManager::CreateInstance][is_machine: %d]"), | |
| 722 is_machine_)); | |
| 723 instance_ = new NetworkConfigManager(); | |
| 724 VERIFY1(SUCCEEDED(instance_->InitializeLock())); | |
| 725 VERIFY1(SUCCEEDED(instance_->InitializeRegistryKey())); | |
| 726 instance_->LoadCupCredentialsFromRegistry(); | |
| 727 } | |
| 728 | |
| 729 return S_OK; | |
| 730 } | |
| 731 | |
| 732 void NetworkConfigManager::DeleteInstance() { | |
| 733 ASSERT1(instance_ != kInvalidInstance); | |
| 734 | |
| 735 NetworkConfigManager* instance = | |
| 736 omaha::interlocked_exchange_pointer(&instance_, kInvalidInstance); | |
| 737 | |
| 738 if (kInvalidInstance != instance && NULL != instance) { | |
| 739 instance->DeleteInstanceInternal(); | |
| 740 delete instance; | |
| 741 } | |
| 742 } | |
| 743 | |
| 744 NetworkConfigManager& NetworkConfigManager::Instance() { | |
| 745 __mutexScope(instance_lock_); | |
| 746 if (!instance_) { | |
| 747 VERIFY1(SUCCEEDED(NetworkConfigManager::CreateInstance())); | |
| 748 } | |
| 749 return *instance_; | |
| 750 } | |
| 751 | |
| 752 void NetworkConfigManager::set_is_machine(bool is_machine) { | |
| 753 __mutexScope(instance_lock_); | |
| 754 if (instance_) { | |
| 755 NET_LOG(LE, (_T("set_is_machine called after instance created."))); | |
| 756 } | |
| 757 | |
| 758 is_machine_ = is_machine; | |
| 759 } | |
| 760 | |
| 761 void NetworkConfigManager::DeleteInstanceInternal() { | |
| 762 __mutexBlock(lock_) { | |
| 763 std::map<CString, NetworkConfig*>::iterator it; | |
| 764 | |
| 765 for (it = user_network_config_map_.begin(); | |
| 766 it != user_network_config_map_.end(); | |
| 767 ++it) { | |
| 768 if (NULL != it->second) { | |
| 769 delete it->second; | |
| 770 } | |
| 771 } | |
| 772 user_network_config_map_.clear(); | |
| 773 } | |
| 774 } | |
| 775 | |
| 776 HRESULT NetworkConfigManager::GetUserNetworkConfig( | |
| 777 NetworkConfig** network_config) { | |
| 778 CString sid; | |
| 779 HRESULT hr = user_info::GetEffectiveUserSid(&sid); | |
| 780 if (FAILED(hr)) { | |
| 781 NET_LOG(LE, (_T("[GetEffectiveUserSid failed][0x%x]"), hr)); | |
| 782 return hr; | |
| 783 } | |
| 784 | |
| 785 __mutexBlock(lock_) { | |
| 786 std::map<CString, NetworkConfig*>::iterator it; | |
| 787 it = user_network_config_map_.find(sid); | |
| 788 | |
| 789 if (user_network_config_map_.end() != it) { | |
| 790 *network_config = it->second; | |
| 791 return S_OK; | |
| 792 } | |
| 793 | |
| 794 hr = CreateNetworkConfigInstance(network_config, is_machine_); | |
| 795 if (SUCCEEDED(hr)) { | |
| 796 user_network_config_map_.insert(std::make_pair(sid, *network_config)); | |
| 797 } | |
| 798 | |
| 799 return hr; | |
| 800 } | |
| 801 | |
| 802 return E_FAIL; | |
| 803 } | |
| 804 | |
| 805 HRESULT NetworkConfigManager::CreateNetworkConfigInstance( | |
| 806 NetworkConfig** network_config_ptr, | |
| 807 bool is_machine) { | |
| 808 ASSERT1(network_config_ptr); | |
| 809 | |
| 810 NetworkConfig* network_config(new NetworkConfig(is_machine)); | |
| 811 HRESULT hr = network_config->Initialize(); | |
| 812 if (FAILED(hr)) { | |
| 813 NET_LOG(LE, (_T("[NetworkConfig::Initialize() failed][0x%x]"), hr)); | |
| 814 delete network_config; | |
| 815 return hr; | |
| 816 } | |
| 817 | |
| 818 *network_config_ptr = network_config; | |
| 819 return S_OK; | |
| 820 } | |
| 821 | |
| 822 HRESULT NetworkConfigManager::InitializeLock() { | |
| 823 NamedObjectAttributes lock_attr; | |
| 824 GetNamedObjectAttributes(kNetworkConfigLock, is_machine_, &lock_attr); | |
| 825 return global_lock_.InitializeWithSecAttr(lock_attr.name, &lock_attr.sa) ? | |
| 826 S_OK : E_FAIL; | |
| 827 } | |
| 828 | |
| 829 HRESULT NetworkConfigManager::InitializeRegistryKey() { | |
| 830 // The registry path under which to store persistent network configuration. | |
| 831 // The "network" subkey is created with default security. Below "network", | |
| 832 // the "secure" key is created so that only system and administrators have | |
| 833 // access to it. | |
| 834 CString reg_path = is_machine_ ? MACHINE_REG_UPDATE : USER_REG_UPDATE; | |
| 835 reg_path = AppendRegKeyPath(reg_path, kNetworkSubkey); | |
| 836 RegKey reg_key_network; | |
| 837 DWORD disposition = 0; | |
| 838 HRESULT hr = reg_key_network.Create(reg_path, | |
| 839 NULL, // Class. | |
| 840 0, // Options. | |
| 841 KEY_CREATE_SUB_KEY, // SAM desired. | |
| 842 NULL, // Security attrs. | |
| 843 &disposition); | |
| 844 if (FAILED(hr)) { | |
| 845 return hr; | |
| 846 } | |
| 847 | |
| 848 // When initializing for machine, grant access to administrators and system. | |
| 849 scoped_ptr<CSecurityAttributes> sa; | |
| 850 if (is_machine_) { | |
| 851 sa.reset(new CSecurityAttributes); | |
| 852 GetAdminDaclSecurityAttributes(sa.get(), GENERIC_ALL); | |
| 853 } | |
| 854 | |
| 855 disposition = 0; | |
| 856 RegKey reg_key_network_secure; | |
| 857 hr = reg_key_network_secure.Create(reg_key_network.Key(), // Parent. | |
| 858 kNetworkCupSubkey, // Subkey name. | |
| 859 NULL, // Class. | |
| 860 0, // Options. | |
| 861 KEY_READ, // SAM desired. | |
| 862 sa.get(), // Security attrs. | |
| 863 &disposition); | |
| 864 if (FAILED(hr)) { | |
| 865 return hr; | |
| 866 } | |
| 867 return S_OK; | |
| 868 } | |
| 869 | |
| 870 HRESULT NetworkConfigManager::SetCupCredentials( | |
| 871 const CupCredentials& cup_credentials) { | |
| 872 __mutexScope(lock_); | |
| 873 | |
| 874 const std::vector<uint8>& sk_in(cup_credentials.sk); | |
| 875 | |
| 876 if (sk_in.empty()) { | |
| 877 return E_INVALIDARG; | |
| 878 } | |
| 879 | |
| 880 std::vector<uint8> sk_out; | |
| 881 HRESULT hr = EncryptData(NULL, 0, &sk_in.front(), sk_in.size(), &sk_out); | |
| 882 if (FAILED(hr)) { | |
| 883 return hr; | |
| 884 } | |
| 885 | |
| 886 cup_credentials_.reset(new CupCredentials); | |
| 887 | |
| 888 cup_credentials_->sk.swap(sk_out); | |
| 889 cup_credentials_->c.SetString(cup_credentials.c); | |
| 890 | |
| 891 return S_OK; | |
| 892 } | |
| 893 | |
| 894 HRESULT NetworkConfigManager::GetCupCredentials( | |
| 895 CupCredentials* cup_credentials) { | |
| 896 ASSERT1(cup_credentials); | |
| 897 __mutexScope(lock_); | |
| 898 if (cup_credentials_ == NULL || cup_credentials_->sk.empty()) { | |
| 899 return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); | |
| 900 } | |
| 901 | |
| 902 std::vector<uint8> decrypted_sk; | |
| 903 HRESULT hr = DecryptData(NULL, | |
| 904 0, | |
| 905 &cup_credentials_->sk.front(), | |
| 906 cup_credentials_->sk.size(), | |
| 907 &decrypted_sk); | |
| 908 if (FAILED(hr)) { | |
| 909 return hr; | |
| 910 } | |
| 911 cup_credentials->sk.swap(decrypted_sk); | |
| 912 cup_credentials->c.SetString(cup_credentials_->c); | |
| 913 | |
| 914 return S_OK; | |
| 915 } | |
| 916 | |
| 917 // This function should be called in singleton creation stage, | |
| 918 // thus no lock is needed. | |
| 919 HRESULT NetworkConfigManager::LoadCupCredentialsFromRegistry() { | |
| 920 __mutexScope(global_lock_); | |
| 921 | |
| 922 CString reg_path = is_machine_ ? MACHINE_REG_UPDATE : USER_REG_UPDATE; | |
| 923 reg_path = AppendRegKeyPath(reg_path, kNetworkSubkey); | |
| 924 CString key_name = AppendRegKeyPath(reg_path, kNetworkCupSubkey); | |
| 925 RegKey reg_key; | |
| 926 HRESULT hr = reg_key.Open(key_name, KEY_READ); | |
| 927 if (FAILED(hr)) { | |
| 928 return hr; | |
| 929 } | |
| 930 scoped_array<byte> buf; | |
| 931 DWORD buf_length = 0; | |
| 932 hr = reg_key.GetValue(kCupClientSecretKey, address(buf), &buf_length); | |
| 933 if (FAILED(hr)) { | |
| 934 return hr; | |
| 935 } | |
| 936 CString cookie; | |
| 937 hr = reg_key.GetValue(kCupClientCookie, &cookie); | |
| 938 if (FAILED(hr)) { | |
| 939 return hr; | |
| 940 } | |
| 941 if (buf_length == 0) { | |
| 942 return E_FAIL; | |
| 943 } | |
| 944 cup_credentials_.reset(new CupCredentials); | |
| 945 cup_credentials_->sk.resize(buf_length); | |
| 946 memcpy(&cup_credentials_->sk.front(), buf.get(), buf_length); | |
| 947 cup_credentials_->c = CT2A(cookie); | |
| 948 | |
| 949 return S_OK; | |
| 950 } | |
| 951 | |
| 952 // This function is called in the destructor only thus no lock is needed. | |
| 953 HRESULT NetworkConfigManager::SaveCupCredentialsToRegistry() { | |
| 954 __mutexScope(global_lock_); | |
| 955 | |
| 956 CString reg_path = is_machine_ ? MACHINE_REG_UPDATE : USER_REG_UPDATE; | |
| 957 reg_path = AppendRegKeyPath(reg_path, kNetworkSubkey); | |
| 958 CString key_name = AppendRegKeyPath(reg_path, kNetworkCupSubkey); | |
| 959 RegKey reg_key; | |
| 960 HRESULT hr = reg_key.Open(key_name, KEY_WRITE); | |
| 961 if (FAILED(hr)) { | |
| 962 NET_LOG(L2, (_T("[Registry key open failed][%s][0x%08x]"), key_name, hr)); | |
| 963 return hr; | |
| 964 } | |
| 965 | |
| 966 if (cup_credentials_ == NULL || cup_credentials_->sk.empty()) { | |
| 967 HRESULT hr1 = reg_key.DeleteValue(kCupClientSecretKey); | |
| 968 HRESULT hr2 = reg_key.DeleteValue(kCupClientCookie); | |
| 969 return (SUCCEEDED(hr1) && SUCCEEDED(hr2)) ? S_OK : HRESULTFromLastError(); | |
| 970 } | |
| 971 | |
| 972 hr = reg_key.SetValue(kCupClientSecretKey, | |
| 973 static_cast<const byte*>(&cup_credentials_->sk.front()), | |
| 974 cup_credentials_->sk.size()); | |
| 975 if (FAILED(hr)) { | |
| 976 return hr; | |
| 977 } | |
| 978 hr = reg_key.SetValue(kCupClientCookie, CA2T(cup_credentials_->c)); | |
| 979 if (FAILED(hr)) { | |
| 980 return hr; | |
| 981 } | |
| 982 return S_OK; | |
| 983 } | |
| 984 | |
| 985 void NetworkConfigManager::ClearCupCredentials() { | |
| 986 __mutexScope(lock_); | |
| 987 cup_credentials_.reset(NULL); | |
| 988 } | |
| 989 | |
| 990 } // namespace omaha | |
| 991 | |
| OLD | NEW |