| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "remoting/host/me2me_preference_pane.h" | 5 #import "remoting/host/me2me_preference_pane.h" |
| 6 | 6 |
| 7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
| 8 #include <CommonCrypto/CommonHMAC.h> | 8 #include <CommonCrypto/CommonHMAC.h> |
| 9 #include <errno.h> |
| 9 #include <launch.h> | 10 #include <launch.h> |
| 10 #import <PreferencePanes/PreferencePanes.h> | 11 #import <PreferencePanes/PreferencePanes.h> |
| 11 #import <SecurityInterface/SFAuthorizationView.h> | 12 #import <SecurityInterface/SFAuthorizationView.h> |
| 13 #include <stdlib.h> |
| 12 #include <unistd.h> | 14 #include <unistd.h> |
| 13 | 15 |
| 14 #include <fstream> | 16 #include <fstream> |
| 15 | 17 |
| 16 #include "base/eintr_wrapper.h" | 18 #include "base/eintr_wrapper.h" |
| 17 #include "base/logging.h" | |
| 18 #include "base/mac/authorization_util.h" | |
| 19 #include "base/mac/foundation_util.h" | |
| 20 #include "base/mac/launchd.h" | |
| 21 #include "base/mac/mac_logging.h" | |
| 22 #include "base/mac/scoped_launch_data.h" | 19 #include "base/mac/scoped_launch_data.h" |
| 23 #include "base/memory/scoped_ptr.h" | 20 #include "base/memory/scoped_ptr.h" |
| 24 #include "base/stringprintf.h" | |
| 25 #include "base/sys_string_conversions.h" | |
| 26 #include "remoting/host/host_config.h" | 21 #include "remoting/host/host_config.h" |
| 27 #import "remoting/host/me2me_preference_pane_confirm_pin.h" | 22 #import "remoting/host/me2me_preference_pane_confirm_pin.h" |
| 28 #import "remoting/host/me2me_preference_pane_disable.h" | 23 #import "remoting/host/me2me_preference_pane_disable.h" |
| 29 #include "third_party/jsoncpp/source/include/json/reader.h" | 24 #include "third_party/jsoncpp/source/include/json/reader.h" |
| 30 #include "third_party/jsoncpp/source/include/json/writer.h" | 25 #include "third_party/jsoncpp/source/include/json/writer.h" |
| 31 #include "third_party/modp_b64/modp_b64.h" | 26 #include "third_party/modp_b64/modp_b64.h" |
| 32 | 27 |
| 33 namespace { | 28 namespace { |
| 34 // The name of the Remoting Host service that is registered with launchd. | 29 // The name of the Remoting Host service that is registered with launchd. |
| 35 #define kServiceName "org.chromium.chromoting" | 30 #define kServiceName "org.chromium.chromoting" |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 68 const std::string& host_secret_hash) { | 63 const std::string& host_secret_hash) { |
| 69 // TODO(lambroslambrou): Once the "base" target supports building for 64-bit | 64 // TODO(lambroslambrou): Once the "base" target supports building for 64-bit |
| 70 // on Mac OS X, remove this code and replace it with |VerifyHostPinHash()| | 65 // on Mac OS X, remove this code and replace it with |VerifyHostPinHash()| |
| 71 // from host/pin_hash.h. | 66 // from host/pin_hash.h. |
| 72 size_t separator = host_secret_hash.find(':'); | 67 size_t separator = host_secret_hash.find(':'); |
| 73 if (separator == std::string::npos) | 68 if (separator == std::string::npos) |
| 74 return false; | 69 return false; |
| 75 | 70 |
| 76 std::string method = host_secret_hash.substr(0, separator); | 71 std::string method = host_secret_hash.substr(0, separator); |
| 77 if (method != "hmac") { | 72 if (method != "hmac") { |
| 78 LOG(ERROR) << "Authentication method '" << method << "' not supported"; | 73 NSLog(@"Authentication method '%s' not supported", method.c_str()); |
| 79 return false; | 74 return false; |
| 80 } | 75 } |
| 81 | 76 |
| 82 std::string hash_base64 = host_secret_hash.substr(separator + 1); | 77 std::string hash_base64 = host_secret_hash.substr(separator + 1); |
| 83 | 78 |
| 84 // Convert |hash_base64| to |hash|, based on code from base/base64.cc. | 79 // Convert |hash_base64| to |hash|, based on code from base/base64.cc. |
| 85 int hash_base64_size = static_cast<int>(hash_base64.size()); | 80 int hash_base64_size = static_cast<int>(hash_base64.size()); |
| 86 std::string hash; | 81 std::string hash; |
| 87 hash.resize(modp_b64_decode_len(hash_base64_size)); | 82 hash.resize(modp_b64_decode_len(hash_base64_size)); |
| 88 | 83 |
| 89 // modp_b64_decode_len() returns at least 1, so hash[0] is safe here. | 84 // modp_b64_decode_len() returns at least 1, so hash[0] is safe here. |
| 90 int hash_size = modp_b64_decode(&(hash[0]), hash_base64.data(), | 85 int hash_size = modp_b64_decode(&(hash[0]), hash_base64.data(), |
| 91 hash_base64_size); | 86 hash_base64_size); |
| 92 if (hash_size < 0) { | 87 if (hash_size < 0) { |
| 93 LOG(ERROR) << "Failed to parse host_secret_hash"; | 88 NSLog(@"Failed to parse host_secret_hash"); |
| 94 return false; | 89 return false; |
| 95 } | 90 } |
| 96 hash.resize(hash_size); | 91 hash.resize(hash_size); |
| 97 | 92 |
| 98 std::string computed_hash; | 93 std::string computed_hash; |
| 99 computed_hash.resize(CC_SHA256_DIGEST_LENGTH); | 94 computed_hash.resize(CC_SHA256_DIGEST_LENGTH); |
| 100 | 95 |
| 101 CCHmac(kCCHmacAlgSHA256, | 96 CCHmac(kCCHmacAlgSHA256, |
| 102 host_id.data(), host_id.size(), | 97 host_id.data(), host_id.size(), |
| 103 pin.data(), pin.size(), | 98 pin.data(), pin.size(), |
| 104 &(computed_hash[0])); | 99 &(computed_hash[0])); |
| 105 | 100 |
| 106 // Normally, a constant-time comparison function would be used, but it is | 101 // Normally, a constant-time comparison function would be used, but it is |
| 107 // unnecessary here as the "secret" is already readable by the user | 102 // unnecessary here as the "secret" is already readable by the user |
| 108 // supplying input to this routine. | 103 // supplying input to this routine. |
| 109 return computed_hash == hash; | 104 return computed_hash == hash; |
| 110 } | 105 } |
| 111 | 106 |
| 112 } // namespace | 107 } // namespace |
| 113 | 108 |
| 109 // These methods are copied from base/mac, but with the logging changed to use |
| 110 // NSLog(). |
| 111 // |
| 112 // TODO(lambroslambrou): Once the "base" target supports building for 64-bit |
| 113 // on Mac OS X, remove these implementations and use the ones in base/mac. |
| 114 namespace base { |
| 115 namespace mac { |
| 116 |
| 117 // MessageForJob sends a single message to launchd with a simple dictionary |
| 118 // mapping |operation| to |job_label|, and returns the result of calling |
| 119 // launch_msg to send that message. On failure, returns NULL. The caller |
| 120 // assumes ownership of the returned launch_data_t object. |
| 121 launch_data_t MessageForJob(const std::string& job_label, |
| 122 const char* operation) { |
| 123 // launch_data_alloc returns something that needs to be freed. |
| 124 ScopedLaunchData message(launch_data_alloc(LAUNCH_DATA_DICTIONARY)); |
| 125 if (!message) { |
| 126 NSLog(@"launch_data_alloc"); |
| 127 return NULL; |
| 128 } |
| 129 |
| 130 // launch_data_new_string returns something that needs to be freed, but |
| 131 // the dictionary will assume ownership when launch_data_dict_insert is |
| 132 // called, so put it in a scoper and .release() it when given to the |
| 133 // dictionary. |
| 134 ScopedLaunchData job_label_launchd(launch_data_new_string(job_label.c_str())); |
| 135 if (!job_label_launchd) { |
| 136 NSLog(@"launch_data_new_string"); |
| 137 return NULL; |
| 138 } |
| 139 |
| 140 if (!launch_data_dict_insert(message, |
| 141 job_label_launchd.release(), |
| 142 operation)) { |
| 143 return NULL; |
| 144 } |
| 145 |
| 146 return launch_msg(message); |
| 147 } |
| 148 |
| 149 pid_t PIDForJob(const std::string& job_label) { |
| 150 ScopedLaunchData response(MessageForJob(job_label, LAUNCH_KEY_GETJOB)); |
| 151 if (!response) { |
| 152 return -1; |
| 153 } |
| 154 |
| 155 launch_data_type_t response_type = launch_data_get_type(response); |
| 156 if (response_type != LAUNCH_DATA_DICTIONARY) { |
| 157 if (response_type == LAUNCH_DATA_ERRNO) { |
| 158 NSLog(@"PIDForJob: error %d", launch_data_get_errno(response)); |
| 159 } else { |
| 160 NSLog(@"PIDForJob: expected dictionary, got %d", response_type); |
| 161 } |
| 162 return -1; |
| 163 } |
| 164 |
| 165 launch_data_t pid_data = launch_data_dict_lookup(response, |
| 166 LAUNCH_JOBKEY_PID); |
| 167 if (!pid_data) |
| 168 return 0; |
| 169 |
| 170 if (launch_data_get_type(pid_data) != LAUNCH_DATA_INTEGER) { |
| 171 NSLog(@"PIDForJob: expected integer"); |
| 172 return -1; |
| 173 } |
| 174 |
| 175 return launch_data_get_integer(pid_data); |
| 176 } |
| 177 |
| 178 OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization, |
| 179 const char* tool_path, |
| 180 AuthorizationFlags options, |
| 181 const char** arguments, |
| 182 FILE** pipe, |
| 183 pid_t* pid) { |
| 184 // pipe may be NULL, but this function needs one. In that case, use a local |
| 185 // pipe. |
| 186 FILE* local_pipe; |
| 187 FILE** pipe_pointer; |
| 188 if (pipe) { |
| 189 pipe_pointer = pipe; |
| 190 } else { |
| 191 pipe_pointer = &local_pipe; |
| 192 } |
| 193 |
| 194 // AuthorizationExecuteWithPrivileges wants |char* const*| for |arguments|, |
| 195 // but it doesn't actually modify the arguments, and that type is kind of |
| 196 // silly and callers probably aren't dealing with that. Put the cast here |
| 197 // to make things a little easier on callers. |
| 198 OSStatus status = AuthorizationExecuteWithPrivileges(authorization, |
| 199 tool_path, |
| 200 options, |
| 201 (char* const*)arguments, |
| 202 pipe_pointer); |
| 203 if (status != errAuthorizationSuccess) { |
| 204 return status; |
| 205 } |
| 206 |
| 207 long line_pid = -1; |
| 208 size_t line_length = 0; |
| 209 char* line_c = fgetln(*pipe_pointer, &line_length); |
| 210 if (line_c) { |
| 211 if (line_length > 0 && line_c[line_length - 1] == '\n') { |
| 212 // line_c + line_length is the start of the next line if there is one. |
| 213 // Back up one character. |
| 214 --line_length; |
| 215 } |
| 216 std::string line(line_c, line_length); |
| 217 |
| 218 // The version in base/mac used base::StringToInt() here. |
| 219 line_pid = strtol(line.c_str(), NULL, 10); |
| 220 if (line_pid == 0) { |
| 221 NSLog(@"ExecuteWithPrivilegesAndGetPid: funny line: %s", line.c_str()); |
| 222 line_pid = -1; |
| 223 } |
| 224 } else { |
| 225 NSLog(@"ExecuteWithPrivilegesAndGetPid: no line"); |
| 226 } |
| 227 |
| 228 if (!pipe) { |
| 229 fclose(*pipe_pointer); |
| 230 } |
| 231 |
| 232 if (pid) { |
| 233 *pid = line_pid; |
| 234 } |
| 235 |
| 236 return status; |
| 237 } |
| 238 |
| 239 } // namespace mac |
| 240 } // namespace base |
| 241 |
| 114 namespace remoting { | 242 namespace remoting { |
| 115 JsonHostConfig::JsonHostConfig(const std::string& filename) | 243 JsonHostConfig::JsonHostConfig(const std::string& filename) |
| 116 : filename_(filename) { | 244 : filename_(filename) { |
| 117 } | 245 } |
| 118 | 246 |
| 119 JsonHostConfig::~JsonHostConfig() { | 247 JsonHostConfig::~JsonHostConfig() { |
| 120 } | 248 } |
| 121 | 249 |
| 122 bool JsonHostConfig::Read() { | 250 bool JsonHostConfig::Read() { |
| 123 std::ifstream file(filename_.c_str()); | 251 std::ifstream file(filename_.c_str()); |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 208 if (!have_new_config_) { | 336 if (!have_new_config_) { |
| 209 // It shouldn't be possible to hit the button if there is no config to | 337 // It shouldn't be possible to hit the button if there is no config to |
| 210 // apply, but check anyway just in case it happens somehow. | 338 // apply, but check anyway just in case it happens somehow. |
| 211 return; | 339 return; |
| 212 } | 340 } |
| 213 | 341 |
| 214 // Ensure the authorization token is up-to-date before using it. | 342 // Ensure the authorization token is up-to-date before using it. |
| 215 [self updateAuthorizationStatus]; | 343 [self updateAuthorizationStatus]; |
| 216 [self updateUI]; | 344 [self updateUI]; |
| 217 | 345 |
| 218 std::string pin_utf8 = base::SysNSStringToUTF8(pin); | 346 std::string pin_utf8 = [pin UTF8String]; |
| 219 std::string host_id, host_secret_hash; | 347 std::string host_id, host_secret_hash; |
| 220 bool result = (config_->GetString(remoting::kHostIdConfigPath, &host_id) && | 348 bool result = (config_->GetString(remoting::kHostIdConfigPath, &host_id) && |
| 221 config_->GetString(remoting::kHostSecretHashConfigPath, | 349 config_->GetString(remoting::kHostSecretHashConfigPath, |
| 222 &host_secret_hash)); | 350 &host_secret_hash)); |
| 223 DCHECK(result); | 351 if (!result) { |
| 352 [self showError]; |
| 353 return; |
| 354 } |
| 224 if (!IsPinValid(pin_utf8, host_id, host_secret_hash)) { | 355 if (!IsPinValid(pin_utf8, host_id, host_secret_hash)) { |
| 225 [self showIncorrectPinMessage]; | 356 [self showIncorrectPinMessage]; |
| 226 return; | 357 return; |
| 227 } | 358 } |
| 228 | 359 |
| 229 [self applyNewServiceConfig]; | 360 [self applyNewServiceConfig]; |
| 230 [self updateUI]; | 361 [self updateUI]; |
| 231 } | 362 } |
| 232 | 363 |
| 233 - (void)onDisable:(id)sender { | 364 - (void)onDisable:(id)sender { |
| 234 // Ensure the authorization token is up-to-date before using it. | 365 // Ensure the authorization token is up-to-date before using it. |
| 235 [self updateAuthorizationStatus]; | 366 [self updateAuthorizationStatus]; |
| 236 [self updateUI]; | 367 [self updateUI]; |
| 237 if (!is_pane_unlocked_) | 368 if (!is_pane_unlocked_) |
| 238 return; | 369 return; |
| 239 | 370 |
| 240 if (![self runHelperAsRootWithCommand:"--disable" | 371 if (![self runHelperAsRootWithCommand:"--disable" |
| 241 inputData:""]) { | 372 inputData:""]) { |
| 242 LOG(ERROR) << "Failed to run the helper tool"; | 373 NSLog(@"Failed to run the helper tool"); |
| 243 [self showError]; | 374 [self showError]; |
| 244 [self notifyPlugin: kUpdateFailedNotificationName]; | 375 [self notifyPlugin: kUpdateFailedNotificationName]; |
| 245 return; | 376 return; |
| 246 } | 377 } |
| 247 | 378 |
| 248 // Stop the launchd job. This cannot easily be done by the helper tool, | 379 // Stop the launchd job. This cannot easily be done by the helper tool, |
| 249 // since the launchd job runs in the current user's context. | 380 // since the launchd job runs in the current user's context. |
| 250 [self sendJobControlMessage:LAUNCH_KEY_STOPJOB]; | 381 [self sendJobControlMessage:LAUNCH_KEY_STOPJOB]; |
| 251 awaiting_service_stop_ = YES; | 382 awaiting_service_stop_ = YES; |
| 252 } | 383 } |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 286 is_service_running_ = (job_pid > 0); | 417 is_service_running_ = (job_pid > 0); |
| 287 } | 418 } |
| 288 | 419 |
| 289 - (void)updateAuthorizationStatus { | 420 - (void)updateAuthorizationStatus { |
| 290 is_pane_unlocked_ = [authorization_view_ updateStatus:authorization_view_]; | 421 is_pane_unlocked_ = [authorization_view_ updateStatus:authorization_view_]; |
| 291 } | 422 } |
| 292 | 423 |
| 293 - (void)readNewConfig { | 424 - (void)readNewConfig { |
| 294 std::string file; | 425 std::string file; |
| 295 if (!GetTemporaryConfigFilePath(&file)) { | 426 if (!GetTemporaryConfigFilePath(&file)) { |
| 296 LOG(ERROR) << "Failed to get path of configuration data."; | 427 NSLog(@"Failed to get path of configuration data."); |
| 297 [self showError]; | 428 [self showError]; |
| 298 return; | 429 return; |
| 299 } | 430 } |
| 300 if (access(file.c_str(), F_OK) != 0) | 431 if (access(file.c_str(), F_OK) != 0) |
| 301 return; | 432 return; |
| 302 | 433 |
| 303 scoped_ptr<remoting::JsonHostConfig> new_config_( | 434 scoped_ptr<remoting::JsonHostConfig> new_config_( |
| 304 new remoting::JsonHostConfig(file)); | 435 new remoting::JsonHostConfig(file)); |
| 305 if (!new_config_->Read()) { | 436 if (!new_config_->Read()) { |
| 306 // Report the error, because the file exists but couldn't be read. The | 437 // Report the error, because the file exists but couldn't be read. The |
| 307 // case of non-existence is normal and expected. | 438 // case of non-existence is normal and expected. |
| 308 LOG(ERROR) << "Error reading configuration data from " << file.c_str(); | 439 NSLog(@"Error reading configuration data from %s", file.c_str()); |
| 309 [self showError]; | 440 [self showError]; |
| 310 return; | 441 return; |
| 311 } | 442 } |
| 312 remove(file.c_str()); | 443 remove(file.c_str()); |
| 313 if (!IsConfigValid(new_config_.get())) { | 444 if (!IsConfigValid(new_config_.get())) { |
| 314 LOG(ERROR) << "Invalid configuration data read."; | 445 NSLog(@"Invalid configuration data read."); |
| 315 [self showError]; | 446 [self showError]; |
| 316 return; | 447 return; |
| 317 } | 448 } |
| 318 | 449 |
| 319 config_.swap(new_config_); | 450 config_.swap(new_config_); |
| 320 have_new_config_ = YES; | 451 have_new_config_ = YES; |
| 321 | 452 |
| 322 [confirm_pin_view_ resetPin]; | 453 [confirm_pin_view_ resetPin]; |
| 323 } | 454 } |
| 324 | 455 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 345 message = @"Remote connections to this computer are disabled."; | 476 message = @"Remote connections to this computer are disabled."; |
| 346 } | 477 } |
| 347 } | 478 } |
| 348 [status_message_ setStringValue:message]; | 479 [status_message_ setStringValue:message]; |
| 349 | 480 |
| 350 std::string email; | 481 std::string email; |
| 351 if (config_.get()) { | 482 if (config_.get()) { |
| 352 bool result = config_->GetString(remoting::kXmppLoginConfigPath, &email); | 483 bool result = config_->GetString(remoting::kXmppLoginConfigPath, &email); |
| 353 | 484 |
| 354 // The config has already been checked by |IsConfigValid|. | 485 // The config has already been checked by |IsConfigValid|. |
| 355 DCHECK(result); | 486 if (!result) { |
| 487 [self showError]; |
| 488 return; |
| 489 } |
| 356 } | 490 } |
| 357 | |
| 358 [disable_view_ setEnabled:(is_pane_unlocked_ && is_service_running_ && | 491 [disable_view_ setEnabled:(is_pane_unlocked_ && is_service_running_ && |
| 359 !restart_pending_or_canceled_)]; | 492 !restart_pending_or_canceled_)]; |
| 360 [confirm_pin_view_ setEnabled:(is_pane_unlocked_ && | 493 [confirm_pin_view_ setEnabled:(is_pane_unlocked_ && |
| 361 !restart_pending_or_canceled_)]; | 494 !restart_pending_or_canceled_)]; |
| 362 [confirm_pin_view_ setEmail:base::SysUTF8ToNSString(email)]; | 495 [confirm_pin_view_ setEmail:[NSString stringWithUTF8String:email.c_str()]]; |
| 363 NSString* applyButtonText = is_service_running_ ? @"Confirm" : @"Enable"; | 496 NSString* applyButtonText = is_service_running_ ? @"Confirm" : @"Enable"; |
| 364 [confirm_pin_view_ setButtonText:applyButtonText]; | 497 [confirm_pin_view_ setButtonText:applyButtonText]; |
| 365 | 498 |
| 366 if (restart_pending_or_canceled_) | 499 if (restart_pending_or_canceled_) |
| 367 [authorization_view_ setEnabled:NO]; | 500 [authorization_view_ setEnabled:NO]; |
| 368 } | 501 } |
| 369 | 502 |
| 370 - (void)showError { | 503 - (void)showError { |
| 371 NSAlert* alert = [[NSAlert alloc] init]; | 504 NSAlert* alert = [[NSAlert alloc] init]; |
| 372 [alert setMessageText:@"An unexpected error occurred."]; | 505 [alert setMessageText:@"An unexpected error occurred."]; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 389 contextInfo:nil]; | 522 contextInfo:nil]; |
| 390 [alert release]; | 523 [alert release]; |
| 391 } | 524 } |
| 392 | 525 |
| 393 - (void)applyNewServiceConfig { | 526 - (void)applyNewServiceConfig { |
| 394 [self updateServiceStatus]; | 527 [self updateServiceStatus]; |
| 395 std::string serialized_config = config_->GetSerializedData(); | 528 std::string serialized_config = config_->GetSerializedData(); |
| 396 const char* command = is_service_running_ ? "--save-config" : "--enable"; | 529 const char* command = is_service_running_ ? "--save-config" : "--enable"; |
| 397 if (![self runHelperAsRootWithCommand:command | 530 if (![self runHelperAsRootWithCommand:command |
| 398 inputData:serialized_config]) { | 531 inputData:serialized_config]) { |
| 399 LOG(ERROR) << "Failed to run the helper tool"; | 532 NSLog(@"Failed to run the helper tool"); |
| 400 [self showError]; | 533 [self showError]; |
| 401 return; | 534 return; |
| 402 } | 535 } |
| 403 | 536 |
| 404 have_new_config_ = NO; | 537 have_new_config_ = NO; |
| 405 | 538 |
| 406 // If the service is running, send a signal to cause it to reload its | 539 // If the service is running, send a signal to cause it to reload its |
| 407 // configuration, otherwise start the service. | 540 // configuration, otherwise start the service. |
| 408 if (is_service_running_) { | 541 if (is_service_running_) { |
| 409 pid_t job_pid = base::mac::PIDForJob(kServiceName); | 542 pid_t job_pid = base::mac::PIDForJob(kServiceName); |
| 410 if (job_pid > 0) { | 543 if (job_pid > 0) { |
| 411 kill(job_pid, SIGHUP); | 544 kill(job_pid, SIGHUP); |
| 412 } else { | 545 } else { |
| 413 LOG(ERROR) << "Failed to obtain PID of service " << kServiceName; | 546 NSLog(@"Failed to obtain PID of service " kServiceName); |
| 414 [self showError]; | 547 [self showError]; |
| 415 } | 548 } |
| 416 } else { | 549 } else { |
| 417 [self sendJobControlMessage:LAUNCH_KEY_STARTJOB]; | 550 [self sendJobControlMessage:LAUNCH_KEY_STARTJOB]; |
| 418 } | 551 } |
| 419 | 552 |
| 420 // Broadcast a distributed notification to inform the plugin that the | 553 // Broadcast a distributed notification to inform the plugin that the |
| 421 // configuration has been applied. | 554 // configuration has been applied. |
| 422 [self notifyPlugin: kUpdateSucceededNotificationName]; | 555 [self notifyPlugin: kUpdateSucceededNotificationName]; |
| 423 } | 556 } |
| 424 | 557 |
| 425 - (BOOL)runHelperAsRootWithCommand:(const char*)command | 558 - (BOOL)runHelperAsRootWithCommand:(const char*)command |
| 426 inputData:(const std::string&)input_data { | 559 inputData:(const std::string&)input_data { |
| 427 AuthorizationRef authorization = | 560 AuthorizationRef authorization = |
| 428 [[authorization_view_ authorization] authorizationRef]; | 561 [[authorization_view_ authorization] authorizationRef]; |
| 429 if (!authorization) { | 562 if (!authorization) { |
| 430 LOG(ERROR) << "Failed to obtain authorizationRef"; | 563 NSLog(@"Failed to obtain authorizationRef"); |
| 431 return NO; | 564 return NO; |
| 432 } | 565 } |
| 433 | 566 |
| 434 // TODO(lambroslambrou): Replace the deprecated ExecuteWithPrivileges | 567 // TODO(lambroslambrou): Replace the deprecated ExecuteWithPrivileges |
| 435 // call with a launchd-based helper tool, which is more secure. | 568 // call with a launchd-based helper tool, which is more secure. |
| 436 // http://crbug.com/120903 | 569 // http://crbug.com/120903 |
| 437 const char* arguments[] = { command, NULL }; | 570 const char* arguments[] = { command, NULL }; |
| 438 FILE* pipe = NULL; | 571 FILE* pipe = NULL; |
| 439 pid_t pid; | 572 pid_t pid; |
| 440 OSStatus status = base::mac::ExecuteWithPrivilegesAndGetPID( | 573 OSStatus status = base::mac::ExecuteWithPrivilegesAndGetPID( |
| 441 authorization, | 574 authorization, |
| 442 kHelperTool, | 575 kHelperTool, |
| 443 kAuthorizationFlagDefaults, | 576 kAuthorizationFlagDefaults, |
| 444 arguments, | 577 arguments, |
| 445 &pipe, | 578 &pipe, |
| 446 &pid); | 579 &pid); |
| 447 if (status != errAuthorizationSuccess) { | 580 if (status != errAuthorizationSuccess) { |
| 448 OSSTATUS_LOG(ERROR, status) << "AuthorizationExecuteWithPrivileges"; | 581 NSLog(@"AuthorizationExecuteWithPrivileges: %s (%d)", |
| 582 GetMacOSStatusErrorString(status), static_cast<int>(status)); |
| 449 return NO; | 583 return NO; |
| 450 } | 584 } |
| 451 if (pid == -1) { | 585 if (pid == -1) { |
| 452 LOG(ERROR) << "Failed to get child PID"; | 586 NSLog(@"Failed to get child PID"); |
| 453 if (pipe) | 587 if (pipe) |
| 454 fclose(pipe); | 588 fclose(pipe); |
| 455 | 589 |
| 456 return NO; | 590 return NO; |
| 457 } | 591 } |
| 458 if (!pipe) { | 592 if (!pipe) { |
| 459 LOG(ERROR) << "Unexpected NULL pipe"; | 593 NSLog(@"Unexpected NULL pipe"); |
| 460 return NO; | 594 return NO; |
| 461 } | 595 } |
| 462 | 596 |
| 463 // Some cleanup is needed (closing the pipe and waiting for the child | 597 // Some cleanup is needed (closing the pipe and waiting for the child |
| 464 // process), so flag any errors before returning. | 598 // process), so flag any errors before returning. |
| 465 BOOL error = NO; | 599 BOOL error = NO; |
| 466 | 600 |
| 467 if (!input_data.empty()) { | 601 if (!input_data.empty()) { |
| 468 size_t bytes_written = fwrite(input_data.data(), sizeof(char), | 602 size_t bytes_written = fwrite(input_data.data(), sizeof(char), |
| 469 input_data.size(), pipe); | 603 input_data.size(), pipe); |
| 470 // According to the fwrite manpage, a partial count is returned only if a | 604 // According to the fwrite manpage, a partial count is returned only if a |
| 471 // write error has occurred. | 605 // write error has occurred. |
| 472 if (bytes_written != input_data.size()) { | 606 if (bytes_written != input_data.size()) { |
| 473 LOG(ERROR) << "Failed to write data to child process"; | 607 NSLog(@"Failed to write data to child process"); |
| 474 error = YES; | 608 error = YES; |
| 475 } | 609 } |
| 476 } | 610 } |
| 477 | 611 |
| 478 // In all cases, fclose() should be called with the returned FILE*. In the | 612 // In all cases, fclose() should be called with the returned FILE*. In the |
| 479 // case of sending data to the child, this needs to be done before calling | 613 // case of sending data to the child, this needs to be done before calling |
| 480 // waitpid(), since the child reads until EOF on its stdin, so calling | 614 // waitpid(), since the child reads until EOF on its stdin, so calling |
| 481 // waitpid() first would result in deadlock. | 615 // waitpid() first would result in deadlock. |
| 482 if (fclose(pipe) != 0) { | 616 if (fclose(pipe) != 0) { |
| 483 PLOG(ERROR) << "fclose"; | 617 NSLog(@"fclose failed with error %d", errno); |
| 484 error = YES; | 618 error = YES; |
| 485 } | 619 } |
| 486 | 620 |
| 487 int exit_status; | 621 int exit_status; |
| 488 pid_t wait_result = HANDLE_EINTR(waitpid(pid, &exit_status, 0)); | 622 pid_t wait_result = HANDLE_EINTR(waitpid(pid, &exit_status, 0)); |
| 489 if (wait_result != pid) { | 623 if (wait_result != pid) { |
| 490 PLOG(ERROR) << "waitpid"; | 624 NSLog(@"waitpid failed with error %d", errno); |
| 491 error = YES; | 625 error = YES; |
| 492 } | 626 } |
| 493 | 627 |
| 494 // No more cleanup needed. | 628 // No more cleanup needed. |
| 495 if (error) | 629 if (error) |
| 496 return NO; | 630 return NO; |
| 497 | 631 |
| 498 if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0) { | 632 if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0) { |
| 499 return YES; | 633 return YES; |
| 500 } else { | 634 } else { |
| 501 LOG(ERROR) << kHelperTool << " failed with exit status " << exit_status; | 635 NSLog(@"%s failed with exit status %d", kHelperTool, exit_status); |
| 502 return NO; | 636 return NO; |
| 503 } | 637 } |
| 504 } | 638 } |
| 505 | 639 |
| 506 - (BOOL)sendJobControlMessage:(const char*)launch_key { | 640 - (BOOL)sendJobControlMessage:(const char*)launch_key { |
| 507 base::mac::ScopedLaunchData response( | 641 base::mac::ScopedLaunchData response( |
| 508 base::mac::MessageForJob(kServiceName, launch_key)); | 642 base::mac::MessageForJob(kServiceName, launch_key)); |
| 509 if (!response) { | 643 if (!response) { |
| 510 LOG(ERROR) << "Failed to send message to launchd"; | 644 NSLog(@"Failed to send message to launchd"); |
| 511 [self showError]; | 645 [self showError]; |
| 512 return NO; | 646 return NO; |
| 513 } | 647 } |
| 514 | 648 |
| 515 // Expect a response of type LAUNCH_DATA_ERRNO. | 649 // Expect a response of type LAUNCH_DATA_ERRNO. |
| 516 launch_data_type_t type = launch_data_get_type(response.get()); | 650 launch_data_type_t type = launch_data_get_type(response.get()); |
| 517 if (type != LAUNCH_DATA_ERRNO) { | 651 if (type != LAUNCH_DATA_ERRNO) { |
| 518 LOG(ERROR) << "launchd returned unexpected type: " << type; | 652 NSLog(@"launchd returned unexpected type: %d", type); |
| 519 [self showError]; | 653 [self showError]; |
| 520 return NO; | 654 return NO; |
| 521 } | 655 } |
| 522 | 656 |
| 523 int error = launch_data_get_errno(response.get()); | 657 int error = launch_data_get_errno(response.get()); |
| 524 if (error) { | 658 if (error) { |
| 525 LOG(ERROR) << "launchd returned error: " << error; | 659 NSLog(@"launchd returned error: %d", error); |
| 526 [self showError]; | 660 [self showError]; |
| 527 return NO; | 661 return NO; |
| 528 } | 662 } |
| 529 return YES; | 663 return YES; |
| 530 } | 664 } |
| 531 | 665 |
| 532 - (void)notifyPlugin:(const char*)message { | 666 - (void)notifyPlugin:(const char*)message { |
| 533 NSDistributedNotificationCenter* center = | 667 NSDistributedNotificationCenter* center = |
| 534 [NSDistributedNotificationCenter defaultCenter]; | 668 [NSDistributedNotificationCenter defaultCenter]; |
| 535 NSString* name = [NSString stringWithUTF8String:message]; | 669 NSString* name = [NSString stringWithUTF8String:message]; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 549 NSString* this_version = [this_plist objectForKey:@"CFBundleVersion"]; | 683 NSString* this_version = [this_plist objectForKey:@"CFBundleVersion"]; |
| 550 | 684 |
| 551 NSString* bundle_path = [this_bundle bundlePath]; | 685 NSString* bundle_path = [this_bundle bundlePath]; |
| 552 NSString* plist_path = | 686 NSString* plist_path = |
| 553 [bundle_path stringByAppendingString:@"/Contents/Info.plist"]; | 687 [bundle_path stringByAppendingString:@"/Contents/Info.plist"]; |
| 554 NSDictionary* disk_plist = | 688 NSDictionary* disk_plist = |
| 555 [NSDictionary dictionaryWithContentsOfFile:plist_path]; | 689 [NSDictionary dictionaryWithContentsOfFile:plist_path]; |
| 556 NSString* disk_version = [disk_plist objectForKey:@"CFBundleVersion"]; | 690 NSString* disk_version = [disk_plist objectForKey:@"CFBundleVersion"]; |
| 557 | 691 |
| 558 if (disk_version == nil) { | 692 if (disk_version == nil) { |
| 559 LOG(ERROR) << "Failed to get installed version information"; | 693 NSLog(@"Failed to get installed version information"); |
| 560 [self showError]; | 694 [self showError]; |
| 561 return; | 695 return; |
| 562 } | 696 } |
| 563 | 697 |
| 564 if ([this_version isEqualToString:disk_version]) | 698 if ([this_version isEqualToString:disk_version]) |
| 565 return; | 699 return; |
| 566 | 700 |
| 567 restart_pending_or_canceled_ = YES; | 701 restart_pending_or_canceled_ = YES; |
| 568 [self updateUI]; | 702 [self updateUI]; |
| 569 } | 703 } |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 609 | 743 |
| 610 // If there is a new config file, delete it and notify the web-app of | 744 // If there is a new config file, delete it and notify the web-app of |
| 611 // failure to apply the config. Otherwise, the web-app will remain in a | 745 // failure to apply the config. Otherwise, the web-app will remain in a |
| 612 // spinning state until System Preferences eventually gets restarted and | 746 // spinning state until System Preferences eventually gets restarted and |
| 613 // the user visits this pane again. | 747 // the user visits this pane again. |
| 614 std::string file; | 748 std::string file; |
| 615 if (!GetTemporaryConfigFilePath(&file)) { | 749 if (!GetTemporaryConfigFilePath(&file)) { |
| 616 // There's no point in alerting the user here. The same error would | 750 // There's no point in alerting the user here. The same error would |
| 617 // happen when the pane is eventually restarted, so the user would be | 751 // happen when the pane is eventually restarted, so the user would be |
| 618 // alerted at that time. | 752 // alerted at that time. |
| 619 LOG(ERROR) << "Failed to get path of configuration data."; | 753 NSLog(@"Failed to get path of configuration data."); |
| 620 return; | 754 return; |
| 621 } | 755 } |
| 622 if (access(file.c_str(), F_OK) != 0) | 756 if (access(file.c_str(), F_OK) != 0) |
| 623 return; | 757 return; |
| 624 | 758 |
| 625 remove(file.c_str()); | 759 remove(file.c_str()); |
| 626 [self notifyPlugin:kUpdateFailedNotificationName]; | 760 [self notifyPlugin:kUpdateFailedNotificationName]; |
| 627 } | 761 } |
| 628 } | 762 } |
| 629 | 763 |
| 630 - (void)restartSystemPreferences { | 764 - (void)restartSystemPreferences { |
| 631 NSTask* task = [[NSTask alloc] init]; | 765 NSTask* task = [[NSTask alloc] init]; |
| 632 NSArray* arguments = [NSArray arrayWithObjects:@"--relaunch-prefpane", nil]; | 766 NSArray* arguments = [NSArray arrayWithObjects:@"--relaunch-prefpane", nil]; |
| 633 [task setLaunchPath:[NSString stringWithUTF8String:kHelperTool]]; | 767 [task setLaunchPath:[NSString stringWithUTF8String:kHelperTool]]; |
| 634 [task setArguments:arguments]; | 768 [task setArguments:arguments]; |
| 635 [task setStandardInput:[NSPipe pipe]]; | 769 [task setStandardInput:[NSPipe pipe]]; |
| 636 [task launch]; | 770 [task launch]; |
| 637 [task release]; | 771 [task release]; |
| 638 [NSApp terminate:nil]; | 772 [NSApp terminate:nil]; |
| 639 } | 773 } |
| 640 | 774 |
| 641 @end | 775 @end |
| OLD | NEW |