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