1// Copyright 2012 The Chromium Authors 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifdef UNSAFE_BUFFERS_BUILD 6// TODO(crbug.com/40284755): Remove this and spanify to fix the errors. 7#pragma allow_unsafe_buffers 8#endif 9 10#include "base/mac/authorization_util.h" 11 12#import <Foundation/Foundation.h> 13#include <stddef.h> 14#include <sys/wait.h> 15 16#include <string> 17 18#include "base/apple/bundle_locations.h" 19#include "base/apple/foundation_util.h" 20#include "base/apple/osstatus_logging.h" 21#include "base/logging.h" 22#include "base/mac/scoped_authorizationref.h" 23#include "base/posix/eintr_wrapper.h" 24#include "base/strings/string_number_conversions.h" 25#include "base/strings/string_util.h" 26#include "base/strings/sys_string_conversions.h" 27#include "base/threading/hang_watcher.h" 28 29namespace base::mac { 30 31ScopedAuthorizationRef CreateAuthorization() { 32 ScopedAuthorizationRef authorization; 33 OSStatus status = AuthorizationCreate( 34 /*rights=*/nullptr, kAuthorizationEmptyEnvironment, 35 kAuthorizationFlagDefaults, authorization.InitializeInto()); 36 if (status != errAuthorizationSuccess) { 37 OSSTATUS_LOG(ERROR, status) << "AuthorizationCreate"; 38 return ScopedAuthorizationRef(); 39 } 40 41 return authorization; 42} 43 44ScopedAuthorizationRef GetAuthorizationRightsWithPrompt( 45 AuthorizationRights* rights, 46 CFStringRef prompt, 47 AuthorizationFlags extra_flags) { 48 ScopedAuthorizationRef authorization = CreateAuthorization(); 49 if (!authorization) { 50 return authorization; 51 } 52 53 // Never consider the current WatchHangsInScope as hung. There was most likely 54 // one created in ThreadControllerWithMessagePumpImpl::DoWork(). The current 55 // hang watching deadline is not valid since the user can take unbounded time 56 // to answer the password prompt. HangWatching will resume when the next task 57 // or event is pumped in MessagePumpCFRunLoop so there is not need to 58 // reactivate it. You can see the function comments for more details. 59 base::HangWatcher::InvalidateActiveExpectations(); 60 61 AuthorizationFlags flags = kAuthorizationFlagDefaults | 62 kAuthorizationFlagInteractionAllowed | 63 kAuthorizationFlagExtendRights | 64 kAuthorizationFlagPreAuthorize | extra_flags; 65 66 // product_logo_32.png is used instead of app.icns because Authorization 67 // Services can't deal with .icns files. 68 NSString* icon_path = 69 [base::apple::FrameworkBundle() pathForResource:@"product_logo_32" 70 ofType:@"png"]; 71 const char* icon_path_c = [icon_path fileSystemRepresentation]; 72 size_t icon_path_length = icon_path_c ? strlen(icon_path_c) : 0; 73 74 // The OS will display |prompt| along with a sentence asking the user to type 75 // the "password to allow this." 76 std::string prompt_string; 77 const char* prompt_c = nullptr; 78 size_t prompt_length = 0; 79 if (prompt) { 80 prompt_string = SysCFStringRefToUTF8(prompt); 81 prompt_c = prompt_string.c_str(); 82 prompt_length = prompt_string.length(); 83 } 84 85 AuthorizationItem environment_items[] = { 86 {kAuthorizationEnvironmentIcon, icon_path_length, (void*)icon_path_c, 0}, 87 {kAuthorizationEnvironmentPrompt, prompt_length, (void*)prompt_c, 0} 88 }; 89 90 AuthorizationEnvironment environment = {std::size(environment_items), 91 environment_items}; 92 93 OSStatus status = AuthorizationCopyRights(authorization, rights, &environment, 94 flags, nullptr); 95 96 if (status != errAuthorizationSuccess) { 97 if (status != errAuthorizationCanceled) { 98 OSSTATUS_LOG(ERROR, status) << "AuthorizationCopyRights"; 99 } 100 return ScopedAuthorizationRef(); 101 } 102 103 return authorization; 104} 105 106ScopedAuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt) { 107 // Specify the "system.privilege.admin" right, which allows 108 // AuthorizationExecuteWithPrivileges to run commands as root. 109 AuthorizationItem right_items[] = { 110 {kAuthorizationRightExecute, 0, nullptr, 0}}; 111 AuthorizationRights rights = {std::size(right_items), right_items}; 112 113 return GetAuthorizationRightsWithPrompt(&rights, prompt, /*extra_flags=*/0); 114} 115 116OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization, 117 const char* tool_path, 118 AuthorizationFlags options, 119 const char** arguments, 120 FILE** pipe, 121 pid_t* pid) { 122 // pipe may be NULL, but this function needs one. In that case, use a local 123 // pipe. 124 FILE* local_pipe; 125 FILE** pipe_pointer; 126 if (pipe) { 127 pipe_pointer = pipe; 128 } else { 129 pipe_pointer = &local_pipe; 130 } 131 132// AuthorizationExecuteWithPrivileges is deprecated in macOS 10.7, but no good 133// replacement exists. https://crbug.com/593133. 134#pragma clang diagnostic push 135#pragma clang diagnostic ignored "-Wdeprecated-declarations" 136 // AuthorizationExecuteWithPrivileges wants |char* const*| for |arguments|, 137 // but it doesn't actually modify the arguments, and that type is kind of 138 // silly and callers probably aren't dealing with that. Put the cast here 139 // to make things a little easier on callers. 140 OSStatus status = AuthorizationExecuteWithPrivileges(authorization, 141 tool_path, 142 options, 143 (char* const*)arguments, 144 pipe_pointer); 145#pragma clang diagnostic pop 146 if (status != errAuthorizationSuccess) { 147 return status; 148 } 149 150 int line_pid = -1; 151 size_t line_length = 0; 152 char* line_c = fgetln(*pipe_pointer, &line_length); 153 if (line_c) { 154 if (line_length > 0 && line_c[line_length - 1] == '\n') { 155 // line_c + line_length is the start of the next line if there is one. 156 // Back up one character. 157 --line_length; 158 } 159 std::string line(line_c, line_length); 160 if (!base::StringToInt(line, &line_pid)) { 161 // StringToInt may have set line_pid to something, but if the conversion 162 // was imperfect, use -1. 163 LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: funny line: " << line; 164 line_pid = -1; 165 } 166 } else { 167 LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: no line"; 168 } 169 170 if (!pipe) { 171 fclose(*pipe_pointer); 172 } 173 174 if (pid) { 175 *pid = line_pid; 176 } 177 178 return status; 179} 180 181OSStatus ExecuteWithPrivilegesAndWait(AuthorizationRef authorization, 182 const char* tool_path, 183 AuthorizationFlags options, 184 const char** arguments, 185 FILE** pipe, 186 int* exit_status) { 187 pid_t pid; 188 OSStatus status = ExecuteWithPrivilegesAndGetPID(authorization, 189 tool_path, 190 options, 191 arguments, 192 pipe, 193 &pid); 194 if (status != errAuthorizationSuccess) { 195 return status; 196 } 197 198 // exit_status may be NULL, but this function needs it. In that case, use a 199 // local version. 200 int local_exit_status; 201 int* exit_status_pointer; 202 if (exit_status) { 203 exit_status_pointer = exit_status; 204 } else { 205 exit_status_pointer = &local_exit_status; 206 } 207 208 if (pid != -1) { 209 pid_t wait_result = HANDLE_EINTR(waitpid(pid, exit_status_pointer, 0)); 210 if (wait_result != pid) { 211 PLOG(ERROR) << "waitpid"; 212 *exit_status_pointer = -1; 213 } 214 } else { 215 *exit_status_pointer = -1; 216 } 217 218 return status; 219} 220 221} // namespace base::mac 222