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