authorization_util.mm revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2009 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/cocoa/authorization_util.h" 6 7#import <Foundation/Foundation.h> 8#include <sys/wait.h> 9 10#include <string> 11 12#include "base/basictypes.h" 13#include "base/eintr_wrapper.h" 14#include "base/logging.h" 15#import "base/mac_util.h" 16#include "base/string_number_conversions.h" 17#include "base/string_util.h" 18#include "chrome/browser/cocoa/scoped_authorizationref.h" 19 20namespace authorization_util { 21 22AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt) { 23 // Create an empty AuthorizationRef. 24 scoped_AuthorizationRef authorization; 25 OSStatus status = AuthorizationCreate(NULL, 26 kAuthorizationEmptyEnvironment, 27 kAuthorizationFlagDefaults, 28 &authorization); 29 if (status != errAuthorizationSuccess) { 30 LOG(ERROR) << "AuthorizationCreate: " << status; 31 return NULL; 32 } 33 34 // Specify the "system.privilege.admin" right, which allows 35 // AuthorizationExecuteWithPrivileges to run commands as root. 36 AuthorizationItem right_items[] = { 37 {kAuthorizationRightExecute, 0, NULL, 0} 38 }; 39 AuthorizationRights rights = {arraysize(right_items), right_items}; 40 41 // product_logo_32.png is used instead of app.icns because Authorization 42 // Services can't deal with .icns files. 43 NSString* icon_path = 44 [mac_util::MainAppBundle() pathForResource:@"product_logo_32" 45 ofType:@"png"]; 46 const char* icon_path_c = [icon_path fileSystemRepresentation]; 47 size_t icon_path_length = icon_path_c ? strlen(icon_path_c) : 0; 48 49 // The OS will append " Type an administrator's name and password to allow 50 // <CFBundleDisplayName> to make changes." 51 NSString* prompt_ns = const_cast<NSString*>( 52 reinterpret_cast<const NSString*>(prompt)); 53 const char* prompt_c = [prompt_ns UTF8String]; 54 size_t prompt_length = prompt_c ? strlen(prompt_c) : 0; 55 56 AuthorizationItem environment_items[] = { 57 {kAuthorizationEnvironmentIcon, icon_path_length, (void*)icon_path_c, 0}, 58 {kAuthorizationEnvironmentPrompt, prompt_length, (void*)prompt_c, 0} 59 }; 60 61 AuthorizationEnvironment environment = {arraysize(environment_items), 62 environment_items}; 63 64 AuthorizationFlags flags = kAuthorizationFlagDefaults | 65 kAuthorizationFlagInteractionAllowed | 66 kAuthorizationFlagExtendRights | 67 kAuthorizationFlagPreAuthorize; 68 69 status = AuthorizationCopyRights(authorization, 70 &rights, 71 &environment, 72 flags, 73 NULL); 74 if (status != errAuthorizationSuccess) { 75 if (status != errAuthorizationCanceled) { 76 LOG(ERROR) << "AuthorizationCopyRights: " << status; 77 } 78 return NULL; 79 } 80 81 return authorization.release(); 82} 83 84OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization, 85 const char* tool_path, 86 AuthorizationFlags options, 87 const char** arguments, 88 FILE** pipe, 89 pid_t* pid) { 90 // pipe may be NULL, but this function needs one. In that case, use a local 91 // pipe. 92 FILE* local_pipe; 93 FILE** pipe_pointer; 94 if (pipe) { 95 pipe_pointer = pipe; 96 } else { 97 pipe_pointer = &local_pipe; 98 } 99 100 // AuthorizationExecuteWithPrivileges wants |char* const*| for |arguments|, 101 // but it doesn't actually modify the arguments, and that type is kind of 102 // silly and callers probably aren't dealing with that. Put the cast here 103 // to make things a little easier on callers. 104 OSStatus status = AuthorizationExecuteWithPrivileges(authorization, 105 tool_path, 106 options, 107 (char* const*)arguments, 108 pipe_pointer); 109 if (status != errAuthorizationSuccess) { 110 return status; 111 } 112 113 int line_pid = -1; 114 size_t line_length = 0; 115 char* line_c = fgetln(*pipe_pointer, &line_length); 116 if (line_c) { 117 if (line_length > 0 && line_c[line_length - 1] == '\n') { 118 // line_c + line_length is the start of the next line if there is one. 119 // Back up one character. 120 --line_length; 121 } 122 std::string line(line_c, line_length); 123 if (!base::StringToInt(line, &line_pid)) { 124 // StringToInt may have set line_pid to something, but if the conversion 125 // was imperfect, use -1. 126 LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: funny line: " << line; 127 line_pid = -1; 128 } 129 } else { 130 LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: no line"; 131 } 132 133 if (!pipe) { 134 fclose(*pipe_pointer); 135 } 136 137 if (pid) { 138 *pid = line_pid; 139 } 140 141 return status; 142} 143 144OSStatus ExecuteWithPrivilegesAndWait(AuthorizationRef authorization, 145 const char* tool_path, 146 AuthorizationFlags options, 147 const char** arguments, 148 FILE** pipe, 149 int* exit_status) { 150 pid_t pid; 151 OSStatus status = ExecuteWithPrivilegesAndGetPID(authorization, 152 tool_path, 153 options, 154 arguments, 155 pipe, 156 &pid); 157 if (status != errAuthorizationSuccess) { 158 return status; 159 } 160 161 // exit_status may be NULL, but this function needs it. In that case, use a 162 // local version. 163 int local_exit_status; 164 int* exit_status_pointer; 165 if (exit_status) { 166 exit_status_pointer = exit_status; 167 } else { 168 exit_status_pointer = &local_exit_status; 169 } 170 171 if (pid != -1) { 172 pid_t wait_result = HANDLE_EINTR(waitpid(pid, exit_status_pointer, 0)); 173 if (wait_result != pid) { 174 PLOG(ERROR) << "waitpid"; 175 *exit_status_pointer = -1; 176 } 177 } else { 178 *exit_status_pointer = -1; 179 } 180 181 return status; 182} 183 184} // namespace authorization_util 185