Chromium Code Reviews| 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 #include "third_party/jsoncpp/source/include/json/reader.h" | 22 #include "third_party/jsoncpp/source/include/json/reader.h" |
| 28 #include "third_party/jsoncpp/source/include/json/writer.h" | 23 #include "third_party/jsoncpp/source/include/json/writer.h" |
| 29 #include "third_party/modp_b64/modp_b64.h" | 24 #include "third_party/modp_b64/modp_b64.h" |
| 30 | 25 |
| 31 namespace { | 26 namespace { |
| 32 // The name of the Remoting Host service that is registered with launchd. | 27 // The name of the Remoting Host service that is registered with launchd. |
| 33 #define kServiceName "org.chromium.chromoting" | 28 #define kServiceName "org.chromium.chromoting" |
| 34 | 29 |
| 35 // Use separate named notifications for success and failure because sandboxed | 30 // Use separate named notifications for success and failure because sandboxed |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 66 const std::string& host_secret_hash) { | 61 const std::string& host_secret_hash) { |
| 67 // TODO(lambroslambrou): Once the "base" target supports building for 64-bit | 62 // TODO(lambroslambrou): Once the "base" target supports building for 64-bit |
| 68 // on Mac OS X, remove this code and replace it with |VerifyHostPinHash()| | 63 // on Mac OS X, remove this code and replace it with |VerifyHostPinHash()| |
| 69 // from host/pin_hash.h. | 64 // from host/pin_hash.h. |
| 70 size_t separator = host_secret_hash.find(':'); | 65 size_t separator = host_secret_hash.find(':'); |
| 71 if (separator == std::string::npos) | 66 if (separator == std::string::npos) |
| 72 return false; | 67 return false; |
| 73 | 68 |
| 74 std::string method = host_secret_hash.substr(0, separator); | 69 std::string method = host_secret_hash.substr(0, separator); |
| 75 if (method != "hmac") { | 70 if (method != "hmac") { |
| 76 LOG(ERROR) << "Authentication method '" << method << "' not supported"; | 71 NSLog(@"Authentication method '%s' not supported", method.c_str()); |
| 77 return false; | 72 return false; |
| 78 } | 73 } |
| 79 | 74 |
| 80 std::string hash_base64 = host_secret_hash.substr(separator + 1); | 75 std::string hash_base64 = host_secret_hash.substr(separator + 1); |
| 81 | 76 |
| 82 // Convert |hash_base64| to |hash|, based on code from base/base64.cc. | 77 // Convert |hash_base64| to |hash|, based on code from base/base64.cc. |
| 83 int hash_base64_size = static_cast<int>(hash_base64.size()); | 78 int hash_base64_size = static_cast<int>(hash_base64.size()); |
| 84 std::string hash; | 79 std::string hash; |
| 85 hash.resize(modp_b64_decode_len(hash_base64_size)); | 80 hash.resize(modp_b64_decode_len(hash_base64_size)); |
| 86 int hash_size = modp_b64_decode(&(hash[0]), hash_base64.data(), | 81 int hash_size = modp_b64_decode(&(hash[0]), hash_base64.data(), |
| 87 hash_base64_size); | 82 hash_base64_size); |
| 88 if (hash_size < 0) { | 83 if (hash_size < 0) { |
| 89 LOG(ERROR) << "Failed to parse host_secret_hash"; | 84 NSLog(@"Failed to parse host_secret_hash"); |
| 90 return false; | 85 return false; |
| 91 } | 86 } |
| 92 hash.resize(hash_size); | 87 hash.resize(hash_size); |
| 93 | 88 |
| 94 std::string computed_hash; | 89 std::string computed_hash; |
| 95 computed_hash.resize(CC_SHA256_DIGEST_LENGTH); | 90 computed_hash.resize(CC_SHA256_DIGEST_LENGTH); |
| 96 | 91 |
| 97 CCHmac(kCCHmacAlgSHA256, | 92 CCHmac(kCCHmacAlgSHA256, |
| 98 host_id.data(), host_id.size(), | 93 host_id.data(), host_id.size(), |
| 99 pin.data(), pin.size(), | 94 pin.data(), pin.size(), |
| 100 &(computed_hash[0])); | 95 &(computed_hash[0])); |
| 101 | 96 |
| 102 return computed_hash == hash; | 97 return computed_hash == hash; |
| 103 } | 98 } |
| 104 | 99 |
| 105 } // namespace | 100 } // namespace |
| 106 | 101 |
| 102 // These methods are copied from base/mac, but with the logging changed to use | |
| 103 // NSLog(). | |
| 104 // | |
| 105 // TODO(lambroslambrou): Once the "base" target supports building for 64-bit | |
| 106 // on Mac OS X, remove these implementations and use the ones in base/mac. | |
| 107 namespace base { | |
| 108 namespace mac { | |
| 109 | |
| 110 // MessageForJob sends a single message to launchd with a simple dictionary | |
| 111 // mapping |operation| to |job_label|, and returns the result of calling | |
| 112 // launch_msg to send that message. On failure, returns NULL. The caller | |
| 113 // assumes ownership of the returned launch_data_t object. | |
| 114 launch_data_t MessageForJob(const std::string& job_label, | |
| 115 const char* operation) { | |
| 116 // launch_data_alloc returns something that needs to be freed. | |
| 117 ScopedLaunchData message(launch_data_alloc(LAUNCH_DATA_DICTIONARY)); | |
| 118 if (!message) { | |
| 119 NSLog(@"launch_data_alloc"); | |
| 120 return NULL; | |
| 121 } | |
| 122 | |
| 123 // launch_data_new_string returns something that needs to be freed, but | |
| 124 // the dictionary will assume ownership when launch_data_dict_insert is | |
| 125 // called, so put it in a scoper and .release() it when given to the | |
| 126 // dictionary. | |
| 127 ScopedLaunchData job_label_launchd(launch_data_new_string(job_label.c_str())); | |
| 128 if (!job_label_launchd) { | |
| 129 NSLog(@"launch_data_new_string"); | |
| 130 return NULL; | |
| 131 } | |
| 132 | |
| 133 if (!launch_data_dict_insert(message, | |
| 134 job_label_launchd.release(), | |
| 135 operation)) { | |
| 136 return NULL; | |
| 137 } | |
| 138 | |
| 139 return launch_msg(message); | |
| 140 } | |
| 141 | |
| 142 pid_t PIDForJob(const std::string& job_label) { | |
|
Robert Sesek
2012/05/16 14:34:04
http://code.google.com/codesearch#OAMlx_jo-ck/src/
Robert Sesek
2012/05/16 14:37:43
Sorry, just saw your comment. Got confused by seei
Lambros
2012/05/16 17:43:44
mark@ told me it works, but it's currently unteste
| |
| 143 ScopedLaunchData response(MessageForJob(job_label, LAUNCH_KEY_GETJOB)); | |
| 144 if (!response) { | |
| 145 return -1; | |
| 146 } | |
| 147 | |
| 148 launch_data_type_t response_type = launch_data_get_type(response); | |
| 149 if (response_type != LAUNCH_DATA_DICTIONARY) { | |
| 150 if (response_type == LAUNCH_DATA_ERRNO) { | |
| 151 NSLog(@"PIDForJob: error %d", launch_data_get_errno(response)); | |
| 152 } else { | |
| 153 NSLog(@"PIDForJob: expected dictionary, got %d", response_type); | |
| 154 } | |
| 155 return -1; | |
| 156 } | |
| 157 | |
| 158 launch_data_t pid_data = launch_data_dict_lookup(response, | |
| 159 LAUNCH_JOBKEY_PID); | |
| 160 if (!pid_data) | |
| 161 return 0; | |
| 162 | |
| 163 if (launch_data_get_type(pid_data) != LAUNCH_DATA_INTEGER) { | |
| 164 NSLog(@"PIDForJob: expected integer"); | |
| 165 return -1; | |
| 166 } | |
| 167 | |
| 168 return launch_data_get_integer(pid_data); | |
| 169 } | |
| 170 | |
| 171 OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization, | |
| 172 const char* tool_path, | |
| 173 AuthorizationFlags options, | |
| 174 const char** arguments, | |
| 175 FILE** pipe, | |
| 176 pid_t* pid) { | |
| 177 // pipe may be NULL, but this function needs one. In that case, use a local | |
| 178 // pipe. | |
| 179 FILE* local_pipe; | |
| 180 FILE** pipe_pointer; | |
| 181 if (pipe) { | |
| 182 pipe_pointer = pipe; | |
| 183 } else { | |
| 184 pipe_pointer = &local_pipe; | |
| 185 } | |
| 186 | |
| 187 // AuthorizationExecuteWithPrivileges wants |char* const*| for |arguments|, | |
| 188 // but it doesn't actually modify the arguments, and that type is kind of | |
| 189 // silly and callers probably aren't dealing with that. Put the cast here | |
| 190 // to make things a little easier on callers. | |
| 191 OSStatus status = AuthorizationExecuteWithPrivileges(authorization, | |
| 192 tool_path, | |
| 193 options, | |
| 194 (char* const*)arguments, | |
| 195 pipe_pointer); | |
| 196 if (status != errAuthorizationSuccess) { | |
| 197 return status; | |
| 198 } | |
| 199 | |
| 200 long line_pid = -1; | |
| 201 size_t line_length = 0; | |
| 202 char* line_c = fgetln(*pipe_pointer, &line_length); | |
| 203 if (line_c) { | |
| 204 if (line_length > 0 && line_c[line_length - 1] == '\n') { | |
| 205 // line_c + line_length is the start of the next line if there is one. | |
| 206 // Back up one character. | |
| 207 --line_length; | |
| 208 } | |
| 209 std::string line(line_c, line_length); | |
| 210 | |
| 211 // The version in base/mac used base::StringToInt() here. | |
| 212 line_pid = strtol(line.c_str(), NULL, 10); | |
| 213 if (line_pid == 0) { | |
| 214 NSLog(@"ExecuteWithPrivilegesAndGetPid: funny line: %s", line.c_str()); | |
| 215 line_pid = -1; | |
| 216 } | |
| 217 } else { | |
| 218 NSLog(@"ExecuteWithPrivilegesAndGetPid: no line"); | |
| 219 } | |
| 220 | |
| 221 if (!pipe) { | |
| 222 fclose(*pipe_pointer); | |
| 223 } | |
| 224 | |
| 225 if (pid) { | |
| 226 *pid = line_pid; | |
| 227 } | |
| 228 | |
| 229 return status; | |
| 230 } | |
| 231 | |
| 232 } // namespace mac | |
| 233 } // namespace base | |
| 234 | |
| 107 namespace remoting { | 235 namespace remoting { |
| 108 JsonHostConfig::JsonHostConfig(const std::string& filename) | 236 JsonHostConfig::JsonHostConfig(const std::string& filename) |
| 109 : filename_(filename) { | 237 : filename_(filename) { |
| 110 } | 238 } |
| 111 | 239 |
| 112 JsonHostConfig::~JsonHostConfig() { | 240 JsonHostConfig::~JsonHostConfig() { |
| 113 } | 241 } |
| 114 | 242 |
| 115 bool JsonHostConfig::Read() { | 243 bool JsonHostConfig::Read() { |
| 116 std::ifstream file(filename_.c_str()); | 244 std::ifstream file(filename_.c_str()); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 188 if (!have_new_config_) { | 316 if (!have_new_config_) { |
| 189 // It shouldn't be possible to hit the button if there is no config to | 317 // It shouldn't be possible to hit the button if there is no config to |
| 190 // apply, but check anyway just in case it happens somehow. | 318 // apply, but check anyway just in case it happens somehow. |
| 191 return; | 319 return; |
| 192 } | 320 } |
| 193 | 321 |
| 194 // Ensure the authorization token is up-to-date before using it. | 322 // Ensure the authorization token is up-to-date before using it. |
| 195 [self updateAuthorizationStatus]; | 323 [self updateAuthorizationStatus]; |
| 196 [self updateUI]; | 324 [self updateUI]; |
| 197 | 325 |
| 198 std::string pin = base::SysNSStringToUTF8([pin_ stringValue]); | 326 std::string pin = [[pin_ stringValue] UTF8String]; |
| 199 std::string host_id, host_secret_hash; | 327 std::string host_id, host_secret_hash; |
| 200 bool result = (config_->GetString(remoting::kHostIdConfigPath, &host_id) && | 328 bool result = (config_->GetString(remoting::kHostIdConfigPath, &host_id) && |
| 201 config_->GetString(remoting::kHostSecretHashConfigPath, | 329 config_->GetString(remoting::kHostSecretHashConfigPath, |
| 202 &host_secret_hash)); | 330 &host_secret_hash)); |
| 203 DCHECK(result); | 331 if (!result) { |
|
Jamie
2012/05/16 20:31:34
In changing these DCHECKs, you're changing the Rel
| |
| 332 [self showError]; | |
| 333 return; | |
| 334 } | |
| 204 if (!IsPinValid(pin, host_id, host_secret_hash)) { | 335 if (!IsPinValid(pin, host_id, host_secret_hash)) { |
| 205 [self showIncorrectPinMessage]; | 336 [self showIncorrectPinMessage]; |
| 206 return; | 337 return; |
| 207 } | 338 } |
| 208 | 339 |
| 209 [self applyNewServiceConfig]; | 340 [self applyNewServiceConfig]; |
| 210 [self updateUI]; | 341 [self updateUI]; |
| 211 } | 342 } |
| 212 | 343 |
| 213 - (void)onDisable:(id)sender { | 344 - (void)onDisable:(id)sender { |
| 214 // Ensure the authorization token is up-to-date before using it. | 345 // Ensure the authorization token is up-to-date before using it. |
| 215 [self updateAuthorizationStatus]; | 346 [self updateAuthorizationStatus]; |
| 216 [self updateUI]; | 347 [self updateUI]; |
| 217 if (!is_pane_unlocked_) | 348 if (!is_pane_unlocked_) |
| 218 return; | 349 return; |
| 219 | 350 |
| 220 if (![self runHelperAsRootWithCommand:"--disable" | 351 if (![self runHelperAsRootWithCommand:"--disable" |
| 221 inputData:""]) { | 352 inputData:""]) { |
| 222 LOG(ERROR) << "Failed to run the helper tool"; | 353 NSLog(@"Failed to run the helper tool"); |
| 223 [self showError]; | 354 [self showError]; |
| 224 return; | 355 return; |
| 225 } | 356 } |
| 226 | 357 |
| 227 // Stop the launchd job. This cannot easily be done by the helper tool, | 358 // Stop the launchd job. This cannot easily be done by the helper tool, |
| 228 // since the launchd job runs in the current user's context. | 359 // since the launchd job runs in the current user's context. |
| 229 [self sendJobControlMessage:LAUNCH_KEY_STOPJOB]; | 360 [self sendJobControlMessage:LAUNCH_KEY_STOPJOB]; |
| 230 } | 361 } |
| 231 | 362 |
| 232 - (void)onNewConfigFile:(NSNotification*)notification { | 363 - (void)onNewConfigFile:(NSNotification*)notification { |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 256 is_service_running_ = (job_pid > 0); | 387 is_service_running_ = (job_pid > 0); |
| 257 } | 388 } |
| 258 | 389 |
| 259 - (void)updateAuthorizationStatus { | 390 - (void)updateAuthorizationStatus { |
| 260 is_pane_unlocked_ = [authorization_view_ updateStatus:authorization_view_]; | 391 is_pane_unlocked_ = [authorization_view_ updateStatus:authorization_view_]; |
| 261 } | 392 } |
| 262 | 393 |
| 263 - (void)readNewConfig { | 394 - (void)readNewConfig { |
| 264 std::string file; | 395 std::string file; |
| 265 if (!GetTemporaryConfigFilePath(&file)) { | 396 if (!GetTemporaryConfigFilePath(&file)) { |
| 266 LOG(ERROR) << "Failed to get path of configuration data."; | 397 NSLog(@"Failed to get path of configuration data."); |
| 267 [self showError]; | 398 [self showError]; |
| 268 return; | 399 return; |
| 269 } | 400 } |
| 270 if (access(file.c_str(), F_OK) != 0) | 401 if (access(file.c_str(), F_OK) != 0) |
| 271 return; | 402 return; |
| 272 | 403 |
| 273 scoped_ptr<remoting::JsonHostConfig> new_config_( | 404 scoped_ptr<remoting::JsonHostConfig> new_config_( |
| 274 new remoting::JsonHostConfig(file)); | 405 new remoting::JsonHostConfig(file)); |
| 275 if (!new_config_->Read()) { | 406 if (!new_config_->Read()) { |
| 276 // Report the error, because the file exists but couldn't be read. The | 407 // Report the error, because the file exists but couldn't be read. The |
| 277 // case of non-existence is normal and expected. | 408 // case of non-existence is normal and expected. |
| 278 LOG(ERROR) << "Error reading configuration data from " << file.c_str(); | 409 NSLog(@"Error reading configuration data from %s", file.c_str()); |
| 279 [self showError]; | 410 [self showError]; |
| 280 return; | 411 return; |
| 281 } | 412 } |
| 282 remove(file.c_str()); | 413 remove(file.c_str()); |
| 283 if (!IsConfigValid(new_config_.get())) { | 414 if (!IsConfigValid(new_config_.get())) { |
| 284 LOG(ERROR) << "Invalid configuration data read."; | 415 NSLog(@"Invalid configuration data read."); |
| 285 [self showError]; | 416 [self showError]; |
| 286 return; | 417 return; |
| 287 } | 418 } |
| 288 | 419 |
| 289 config_.swap(new_config_); | 420 config_.swap(new_config_); |
| 290 have_new_config_ = YES; | 421 have_new_config_ = YES; |
| 291 } | 422 } |
| 292 | 423 |
| 293 - (void)updateUI { | 424 - (void)updateUI { |
| 294 // TODO(lambroslambrou): These strings should be localized. | 425 // TODO(lambroslambrou): These strings should be localized. |
| 295 #ifdef OFFICIAL_BUILD | 426 #ifdef OFFICIAL_BUILD |
| 296 NSString* name = @"Chrome Remote Desktop"; | 427 NSString* name = @"Chrome Remote Desktop"; |
| 297 #else | 428 #else |
| 298 NSString* name = @"Chromoting"; | 429 NSString* name = @"Chromoting"; |
| 299 #endif | 430 #endif |
| 300 NSString* message; | 431 NSString* message; |
| 301 if (is_service_running_) { | 432 if (is_service_running_) { |
| 302 message = [NSString stringWithFormat:@"%@ is enabled", name]; | 433 message = [NSString stringWithFormat:@"%@ is enabled", name]; |
| 303 } else { | 434 } else { |
| 304 message = [NSString stringWithFormat:@"%@ is disabled", name]; | 435 message = [NSString stringWithFormat:@"%@ is disabled", name]; |
| 305 } | 436 } |
| 306 [status_message_ setStringValue:message]; | 437 [status_message_ setStringValue:message]; |
| 307 | 438 |
| 308 std::string email; | 439 std::string email; |
| 309 if (config_.get()) { | 440 if (config_.get()) { |
| 310 bool result = config_->GetString(remoting::kXmppLoginConfigPath, &email); | 441 bool result = config_->GetString(remoting::kXmppLoginConfigPath, &email); |
| 311 | 442 |
| 312 // The config has already been checked by |IsConfigValid|. | 443 // The config has already been checked by |IsConfigValid|. |
| 313 DCHECK(result); | 444 if (!result) { |
| 445 [self showError]; | |
| 446 return; | |
| 447 } | |
| 314 } | 448 } |
| 315 [email_ setStringValue:base::SysUTF8ToNSString(email)]; | 449 [email_ setStringValue:[NSString stringWithUTF8String:email.c_str()]]; |
| 316 | 450 |
| 317 [disable_button_ setEnabled:(is_pane_unlocked_ && is_service_running_)]; | 451 [disable_button_ setEnabled:(is_pane_unlocked_ && is_service_running_)]; |
| 318 [pin_instruction_message_ setEnabled:have_new_config_]; | 452 [pin_instruction_message_ setEnabled:have_new_config_]; |
| 319 [email_ setEnabled:have_new_config_]; | 453 [email_ setEnabled:have_new_config_]; |
| 320 [pin_ setEnabled:have_new_config_]; | 454 [pin_ setEnabled:have_new_config_]; |
| 321 [apply_button_ setEnabled:(is_pane_unlocked_ && have_new_config_)]; | 455 [apply_button_ setEnabled:(is_pane_unlocked_ && have_new_config_)]; |
| 322 } | 456 } |
| 323 | 457 |
| 324 - (void)showError { | 458 - (void)showError { |
| 325 NSAlert* alert = [[NSAlert alloc] init]; | 459 NSAlert* alert = [[NSAlert alloc] init]; |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 343 contextInfo:nil]; | 477 contextInfo:nil]; |
| 344 [alert release]; | 478 [alert release]; |
| 345 } | 479 } |
| 346 | 480 |
| 347 - (void)applyNewServiceConfig { | 481 - (void)applyNewServiceConfig { |
| 348 [self updateServiceStatus]; | 482 [self updateServiceStatus]; |
| 349 std::string serialized_config = config_->GetSerializedData(); | 483 std::string serialized_config = config_->GetSerializedData(); |
| 350 const char* command = is_service_running_ ? "--save-config" : "--enable"; | 484 const char* command = is_service_running_ ? "--save-config" : "--enable"; |
| 351 if (![self runHelperAsRootWithCommand:command | 485 if (![self runHelperAsRootWithCommand:command |
| 352 inputData:serialized_config]) { | 486 inputData:serialized_config]) { |
| 353 LOG(ERROR) << "Failed to run the helper tool"; | 487 NSLog(@"Failed to run the helper tool"); |
| 354 [self showError]; | 488 [self showError]; |
| 355 return; | 489 return; |
| 356 } | 490 } |
| 357 | 491 |
| 358 have_new_config_ = NO; | 492 have_new_config_ = NO; |
| 359 | 493 |
| 360 // If the service is running, send a signal to cause it to reload its | 494 // If the service is running, send a signal to cause it to reload its |
| 361 // configuration, otherwise start the service. | 495 // configuration, otherwise start the service. |
| 362 if (is_service_running_) { | 496 if (is_service_running_) { |
| 363 pid_t job_pid = base::mac::PIDForJob(kServiceName); | 497 pid_t job_pid = base::mac::PIDForJob(kServiceName); |
| 364 if (job_pid > 0) { | 498 if (job_pid > 0) { |
| 365 kill(job_pid, SIGHUP); | 499 kill(job_pid, SIGHUP); |
| 366 } else { | 500 } else { |
| 367 LOG(ERROR) << "Failed to obtain PID of service " << kServiceName; | 501 NSLog(@"Failed to obtain PID of service " kServiceName); |
| 368 [self showError]; | 502 [self showError]; |
| 369 } | 503 } |
| 370 } else { | 504 } else { |
| 371 [self sendJobControlMessage:LAUNCH_KEY_STARTJOB]; | 505 [self sendJobControlMessage:LAUNCH_KEY_STARTJOB]; |
| 372 } | 506 } |
| 373 | 507 |
| 374 // Broadcast a distributed notification to inform the plugin that the | 508 // Broadcast a distributed notification to inform the plugin that the |
| 375 // configuration has been applied. | 509 // configuration has been applied. |
| 376 [self notifyPlugin: kUpdateSucceededNotificationName]; | 510 [self notifyPlugin: kUpdateSucceededNotificationName]; |
| 377 } | 511 } |
| 378 | 512 |
| 379 - (BOOL)runHelperAsRootWithCommand:(const char*)command | 513 - (BOOL)runHelperAsRootWithCommand:(const char*)command |
| 380 inputData:(const std::string&)input_data { | 514 inputData:(const std::string&)input_data { |
| 381 AuthorizationRef authorization = | 515 AuthorizationRef authorization = |
| 382 [[authorization_view_ authorization] authorizationRef]; | 516 [[authorization_view_ authorization] authorizationRef]; |
| 383 if (!authorization) { | 517 if (!authorization) { |
| 384 LOG(ERROR) << "Failed to obtain authorizationRef"; | 518 NSLog(@"Failed to obtain authorizationRef"); |
| 385 return NO; | 519 return NO; |
| 386 } | 520 } |
| 387 | 521 |
| 388 // TODO(lambroslambrou): Replace the deprecated ExecuteWithPrivileges | 522 // TODO(lambroslambrou): Replace the deprecated ExecuteWithPrivileges |
| 389 // call with a launchd-based helper tool, which is more secure. | 523 // call with a launchd-based helper tool, which is more secure. |
| 390 // http://crbug.com/120903 | 524 // http://crbug.com/120903 |
| 391 const char* arguments[] = { command, NULL }; | 525 const char* arguments[] = { command, NULL }; |
| 392 FILE* pipe = NULL; | 526 FILE* pipe = NULL; |
| 393 pid_t pid; | 527 pid_t pid; |
| 394 OSStatus status = base::mac::ExecuteWithPrivilegesAndGetPID( | 528 OSStatus status = base::mac::ExecuteWithPrivilegesAndGetPID( |
| 395 authorization, | 529 authorization, |
| 396 kHelperTool, | 530 kHelperTool, |
| 397 kAuthorizationFlagDefaults, | 531 kAuthorizationFlagDefaults, |
| 398 arguments, | 532 arguments, |
| 399 &pipe, | 533 &pipe, |
| 400 &pid); | 534 &pid); |
| 401 if (status != errAuthorizationSuccess) { | 535 if (status != errAuthorizationSuccess) { |
| 402 OSSTATUS_LOG(ERROR, status) << "AuthorizationExecuteWithPrivileges"; | 536 NSLog(@"AuthorizationExecuteWithPrivileges: %s (%d)", |
| 537 GetMacOSStatusErrorString(status), static_cast<int>(status)); | |
| 403 return NO; | 538 return NO; |
| 404 } | 539 } |
| 405 if (pid == -1) { | 540 if (pid == -1) { |
| 406 LOG(ERROR) << "Failed to get child PID"; | 541 NSLog(@"Failed to get child PID"); |
| 407 if (pipe) | 542 if (pipe) |
| 408 fclose(pipe); | 543 fclose(pipe); |
| 409 | 544 |
| 410 return NO; | 545 return NO; |
| 411 } | 546 } |
| 412 if (!pipe) { | 547 if (!pipe) { |
| 413 LOG(ERROR) << "Unexpected NULL pipe"; | 548 NSLog(@"Unexpected NULL pipe"); |
| 414 return NO; | 549 return NO; |
| 415 } | 550 } |
| 416 | 551 |
| 417 // Some cleanup is needed (closing the pipe and waiting for the child | 552 // Some cleanup is needed (closing the pipe and waiting for the child |
| 418 // process), so flag any errors before returning. | 553 // process), so flag any errors before returning. |
| 419 BOOL error = NO; | 554 BOOL error = NO; |
| 420 | 555 |
| 421 if (!input_data.empty()) { | 556 if (!input_data.empty()) { |
| 422 size_t bytes_written = fwrite(input_data.data(), sizeof(char), | 557 size_t bytes_written = fwrite(input_data.data(), sizeof(char), |
| 423 input_data.size(), pipe); | 558 input_data.size(), pipe); |
| 424 // According to the fwrite manpage, a partial count is returned only if a | 559 // According to the fwrite manpage, a partial count is returned only if a |
| 425 // write error has occurred. | 560 // write error has occurred. |
| 426 if (bytes_written != input_data.size()) { | 561 if (bytes_written != input_data.size()) { |
| 427 LOG(ERROR) << "Failed to write data to child process"; | 562 NSLog(@"Failed to write data to child process"); |
| 428 error = YES; | 563 error = YES; |
| 429 } | 564 } |
| 430 } | 565 } |
| 431 | 566 |
| 432 // In all cases, fclose() should be called with the returned FILE*. In the | 567 // In all cases, fclose() should be called with the returned FILE*. In the |
| 433 // case of sending data to the child, this needs to be done before calling | 568 // case of sending data to the child, this needs to be done before calling |
| 434 // waitpid(), since the child reads until EOF on its stdin, so calling | 569 // waitpid(), since the child reads until EOF on its stdin, so calling |
| 435 // waitpid() first would result in deadlock. | 570 // waitpid() first would result in deadlock. |
| 436 if (fclose(pipe) != 0) { | 571 if (fclose(pipe) != 0) { |
| 437 PLOG(ERROR) << "fclose"; | 572 NSLog(@"fclose failed with error %d", errno); |
| 438 error = YES; | 573 error = YES; |
| 439 } | 574 } |
| 440 | 575 |
| 441 int exit_status; | 576 int exit_status; |
| 442 pid_t wait_result = HANDLE_EINTR(waitpid(pid, &exit_status, 0)); | 577 pid_t wait_result = HANDLE_EINTR(waitpid(pid, &exit_status, 0)); |
| 443 if (wait_result != pid) { | 578 if (wait_result != pid) { |
| 444 PLOG(ERROR) << "waitpid"; | 579 NSLog(@"waitpid failed with error %d", errno); |
| 445 error = YES; | 580 error = YES; |
| 446 } | 581 } |
| 447 | 582 |
| 448 // No more cleanup needed. | 583 // No more cleanup needed. |
| 449 if (error) | 584 if (error) |
| 450 return NO; | 585 return NO; |
| 451 | 586 |
| 452 if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0) { | 587 if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0) { |
| 453 return YES; | 588 return YES; |
| 454 } else { | 589 } else { |
| 455 LOG(ERROR) << kHelperTool << " failed with exit status " << exit_status; | 590 NSLog(@"%s failed with exit status %d", kHelperTool, exit_status); |
| 456 return NO; | 591 return NO; |
| 457 } | 592 } |
| 458 } | 593 } |
| 459 | 594 |
| 460 - (BOOL)sendJobControlMessage:(const char*)launch_key { | 595 - (BOOL)sendJobControlMessage:(const char*)launch_key { |
| 461 base::mac::ScopedLaunchData response( | 596 base::mac::ScopedLaunchData response( |
| 462 base::mac::MessageForJob(kServiceName, launch_key)); | 597 base::mac::MessageForJob(kServiceName, launch_key)); |
| 463 if (!response) { | 598 if (!response) { |
| 464 LOG(ERROR) << "Failed to send message to launchd"; | 599 NSLog(@"Failed to send message to launchd"); |
| 465 [self showError]; | 600 [self showError]; |
| 466 return NO; | 601 return NO; |
| 467 } | 602 } |
| 468 | 603 |
| 469 // Expect a response of type LAUNCH_DATA_ERRNO. | 604 // Expect a response of type LAUNCH_DATA_ERRNO. |
| 470 launch_data_type_t type = launch_data_get_type(response.get()); | 605 launch_data_type_t type = launch_data_get_type(response.get()); |
| 471 if (type != LAUNCH_DATA_ERRNO) { | 606 if (type != LAUNCH_DATA_ERRNO) { |
| 472 LOG(ERROR) << "launchd returned unexpected type: " << type; | 607 NSLog(@"launchd returned unexpected type: %d", type); |
| 473 [self showError]; | 608 [self showError]; |
| 474 return NO; | 609 return NO; |
| 475 } | 610 } |
| 476 | 611 |
| 477 int error = launch_data_get_errno(response.get()); | 612 int error = launch_data_get_errno(response.get()); |
| 478 if (error) { | 613 if (error) { |
| 479 LOG(ERROR) << "launchd returned error: " << error; | 614 NSLog(@"launchd returned error: %d", error); |
| 480 [self showError]; | 615 [self showError]; |
| 481 return NO; | 616 return NO; |
| 482 } | 617 } |
| 483 return YES; | 618 return YES; |
| 484 } | 619 } |
| 485 | 620 |
| 486 - (void)notifyPlugin:(const char*)message { | 621 - (void)notifyPlugin:(const char*)message { |
| 487 NSDistributedNotificationCenter* center = | 622 NSDistributedNotificationCenter* center = |
| 488 [NSDistributedNotificationCenter defaultCenter]; | 623 [NSDistributedNotificationCenter defaultCenter]; |
| 489 NSString* name = [NSString stringWithUTF8String:message]; | 624 NSString* name = [NSString stringWithUTF8String:message]; |
| 490 [center postNotificationName:name | 625 [center postNotificationName:name |
| 491 object:nil | 626 object:nil |
| 492 userInfo:nil]; | 627 userInfo:nil]; |
| 493 } | 628 } |
| 494 | 629 |
| 495 @end | 630 @end |
| OLD | NEW |