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/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 20 namespace authorization_util { 21 22 AuthorizationRef 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 [base::mac::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 = base::mac::CFToNSCast(prompt); 52 const char* prompt_c = [prompt_ns UTF8String]; 53 size_t prompt_length = prompt_c ? strlen(prompt_c) : 0; 54 55 AuthorizationItem environment_items[] = { 56 {kAuthorizationEnvironmentIcon, icon_path_length, (void*)icon_path_c, 0}, 57 {kAuthorizationEnvironmentPrompt, prompt_length, (void*)prompt_c, 0} 58 }; 59 60 AuthorizationEnvironment environment = {arraysize(environment_items), 61 environment_items}; 62 63 AuthorizationFlags flags = kAuthorizationFlagDefaults | 64 kAuthorizationFlagInteractionAllowed | 65 kAuthorizationFlagExtendRights | 66 kAuthorizationFlagPreAuthorize; 67 68 status = AuthorizationCopyRights(authorization, 69 &rights, 70 &environment, 71 flags, 72 NULL); 73 if (status != errAuthorizationSuccess) { 74 if (status != errAuthorizationCanceled) { 75 LOG(ERROR) << "AuthorizationCopyRights: " << status; 76 } 77 return NULL; 78 } 79 80 return authorization.release(); 81 } 82 83 OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization, 84 const char* tool_path, 85 AuthorizationFlags options, 86 const char** arguments, 87 FILE** pipe, 88 pid_t* pid) { 89 // pipe may be NULL, but this function needs one. In that case, use a local 90 // pipe. 91 FILE* local_pipe; 92 FILE** pipe_pointer; 93 if (pipe) { 94 pipe_pointer = pipe; 95 } else { 96 pipe_pointer = &local_pipe; 97 } 98 99 // AuthorizationExecuteWithPrivileges wants |char* const*| for |arguments|, 100 // but it doesn't actually modify the arguments, and that type is kind of 101 // silly and callers probably aren't dealing with that. Put the cast here 102 // to make things a little easier on callers. 103 OSStatus status = AuthorizationExecuteWithPrivileges(authorization, 104 tool_path, 105 options, 106 (char* const*)arguments, 107 pipe_pointer); 108 if (status != errAuthorizationSuccess) { 109 return status; 110 } 111 112 int line_pid = -1; 113 size_t line_length = 0; 114 char* line_c = fgetln(*pipe_pointer, &line_length); 115 if (line_c) { 116 if (line_length > 0 && line_c[line_length - 1] == '\n') { 117 // line_c + line_length is the start of the next line if there is one. 118 // Back up one character. 119 --line_length; 120 } 121 std::string line(line_c, line_length); 122 if (!base::StringToInt(line, &line_pid)) { 123 // StringToInt may have set line_pid to something, but if the conversion 124 // was imperfect, use -1. 125 LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: funny line: " << line; 126 line_pid = -1; 127 } 128 } else { 129 LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: no line"; 130 } 131 132 if (!pipe) { 133 fclose(*pipe_pointer); 134 } 135 136 if (pid) { 137 *pid = line_pid; 138 } 139 140 return status; 141 } 142 143 OSStatus ExecuteWithPrivilegesAndWait(AuthorizationRef authorization, 144 const char* tool_path, 145 AuthorizationFlags options, 146 const char** arguments, 147 FILE** pipe, 148 int* exit_status) { 149 pid_t pid; 150 OSStatus status = ExecuteWithPrivilegesAndGetPID(authorization, 151 tool_path, 152 options, 153 arguments, 154 pipe, 155 &pid); 156 if (status != errAuthorizationSuccess) { 157 return status; 158 } 159 160 // exit_status may be NULL, but this function needs it. In that case, use a 161 // local version. 162 int local_exit_status; 163 int* exit_status_pointer; 164 if (exit_status) { 165 exit_status_pointer = exit_status; 166 } else { 167 exit_status_pointer = &local_exit_status; 168 } 169 170 if (pid != -1) { 171 pid_t wait_result = HANDLE_EINTR(waitpid(pid, exit_status_pointer, 0)); 172 if (wait_result != pid) { 173 PLOG(ERROR) << "waitpid"; 174 *exit_status_pointer = -1; 175 } 176 } else { 177 *exit_status_pointer = -1; 178 } 179 180 return status; 181 } 182 183 } // namespace authorization_util 184