|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/mac/authorization_util.h" | 
|  |  | 
|  | #import <Foundation/Foundation.h> | 
|  | #include <stddef.h> | 
|  | #include <sys/wait.h> | 
|  |  | 
|  | #include <string> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/mac/bundle_locations.h" | 
|  | #include "base/mac/foundation_util.h" | 
|  | #include "base/mac/mac_logging.h" | 
|  | #include "base/mac/scoped_authorizationref.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/posix/eintr_wrapper.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/strings/string_util.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace mac { | 
|  |  | 
|  | AuthorizationRef GetAuthorizationRightsWithPrompt( | 
|  | AuthorizationRights* rights, | 
|  | CFStringRef prompt, | 
|  | AuthorizationFlags extraFlags) { | 
|  | // Create an empty AuthorizationRef. | 
|  | ScopedAuthorizationRef authorization; | 
|  | OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, | 
|  | kAuthorizationFlagDefaults, | 
|  | authorization.get_pointer()); | 
|  | if (status != errAuthorizationSuccess) { | 
|  | OSSTATUS_LOG(ERROR, status) << "AuthorizationCreate"; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | AuthorizationFlags flags = kAuthorizationFlagDefaults | | 
|  | kAuthorizationFlagInteractionAllowed | | 
|  | kAuthorizationFlagExtendRights | | 
|  | kAuthorizationFlagPreAuthorize | | 
|  | extraFlags; | 
|  |  | 
|  | // product_logo_32.png is used instead of app.icns because Authorization | 
|  | // Services can't deal with .icns files. | 
|  | NSString* icon_path = | 
|  | [base::mac::FrameworkBundle() pathForResource:@"product_logo_32" | 
|  | ofType:@"png"]; | 
|  | const char* icon_path_c = [icon_path fileSystemRepresentation]; | 
|  | size_t icon_path_length = icon_path_c ? strlen(icon_path_c) : 0; | 
|  |  | 
|  | // The OS will dispay |prompt| along with a sentence asking the user to type | 
|  | // the "password to allow this." | 
|  | NSString* prompt_ns = base::mac::CFToNSCast(prompt); | 
|  | const char* prompt_c = [prompt_ns UTF8String]; | 
|  | size_t prompt_length = prompt_c ? strlen(prompt_c) : 0; | 
|  |  | 
|  | AuthorizationItem environment_items[] = { | 
|  | {kAuthorizationEnvironmentIcon, icon_path_length, (void*)icon_path_c, 0}, | 
|  | {kAuthorizationEnvironmentPrompt, prompt_length, (void*)prompt_c, 0} | 
|  | }; | 
|  |  | 
|  | AuthorizationEnvironment environment = {arraysize(environment_items), | 
|  | environment_items}; | 
|  |  | 
|  | status = AuthorizationCopyRights(authorization, | 
|  | rights, | 
|  | &environment, | 
|  | flags, | 
|  | NULL); | 
|  |  | 
|  | if (status != errAuthorizationSuccess) { | 
|  | if (status != errAuthorizationCanceled) { | 
|  | OSSTATUS_LOG(ERROR, status) << "AuthorizationCopyRights"; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | return authorization.release(); | 
|  | } | 
|  |  | 
|  | AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt) { | 
|  | // Specify the "system.privilege.admin" right, which allows | 
|  | // AuthorizationExecuteWithPrivileges to run commands as root. | 
|  | AuthorizationItem right_items[] = { | 
|  | {kAuthorizationRightExecute, 0, NULL, 0} | 
|  | }; | 
|  | AuthorizationRights rights = {arraysize(right_items), right_items}; | 
|  |  | 
|  | return GetAuthorizationRightsWithPrompt(&rights, prompt, 0); | 
|  | } | 
|  |  | 
|  | OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization, | 
|  | const char* tool_path, | 
|  | AuthorizationFlags options, | 
|  | const char** arguments, | 
|  | FILE** pipe, | 
|  | pid_t* pid) { | 
|  | // pipe may be NULL, but this function needs one.  In that case, use a local | 
|  | // pipe. | 
|  | FILE* local_pipe; | 
|  | FILE** pipe_pointer; | 
|  | if (pipe) { | 
|  | pipe_pointer = pipe; | 
|  | } else { | 
|  | pipe_pointer = &local_pipe; | 
|  | } | 
|  |  | 
|  | // AuthorizationExecuteWithPrivileges is deprecated in macOS 10.7, but no good | 
|  | // replacement exists. https://crbug.com/593133. | 
|  | #pragma clang diagnostic push | 
|  | #pragma clang diagnostic ignored "-Wdeprecated-declarations" | 
|  | // AuthorizationExecuteWithPrivileges wants |char* const*| for |arguments|, | 
|  | // but it doesn't actually modify the arguments, and that type is kind of | 
|  | // silly and callers probably aren't dealing with that.  Put the cast here | 
|  | // to make things a little easier on callers. | 
|  | OSStatus status = AuthorizationExecuteWithPrivileges(authorization, | 
|  | tool_path, | 
|  | options, | 
|  | (char* const*)arguments, | 
|  | pipe_pointer); | 
|  | #pragma clang diagnostic pop | 
|  | if (status != errAuthorizationSuccess) { | 
|  | return status; | 
|  | } | 
|  |  | 
|  | int line_pid = -1; | 
|  | size_t line_length = 0; | 
|  | char* line_c = fgetln(*pipe_pointer, &line_length); | 
|  | if (line_c) { | 
|  | if (line_length > 0 && line_c[line_length - 1] == '\n') { | 
|  | // line_c + line_length is the start of the next line if there is one. | 
|  | // Back up one character. | 
|  | --line_length; | 
|  | } | 
|  | std::string line(line_c, line_length); | 
|  | if (!base::StringToInt(line, &line_pid)) { | 
|  | // StringToInt may have set line_pid to something, but if the conversion | 
|  | // was imperfect, use -1. | 
|  | LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: funny line: " << line; | 
|  | line_pid = -1; | 
|  | } | 
|  | } else { | 
|  | LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: no line"; | 
|  | } | 
|  |  | 
|  | if (!pipe) { | 
|  | fclose(*pipe_pointer); | 
|  | } | 
|  |  | 
|  | if (pid) { | 
|  | *pid = line_pid; | 
|  | } | 
|  |  | 
|  | return status; | 
|  | } | 
|  |  | 
|  | OSStatus ExecuteWithPrivilegesAndWait(AuthorizationRef authorization, | 
|  | const char* tool_path, | 
|  | AuthorizationFlags options, | 
|  | const char** arguments, | 
|  | FILE** pipe, | 
|  | int* exit_status) { | 
|  | pid_t pid; | 
|  | OSStatus status = ExecuteWithPrivilegesAndGetPID(authorization, | 
|  | tool_path, | 
|  | options, | 
|  | arguments, | 
|  | pipe, | 
|  | &pid); | 
|  | if (status != errAuthorizationSuccess) { | 
|  | return status; | 
|  | } | 
|  |  | 
|  | // exit_status may be NULL, but this function needs it.  In that case, use a | 
|  | // local version. | 
|  | int local_exit_status; | 
|  | int* exit_status_pointer; | 
|  | if (exit_status) { | 
|  | exit_status_pointer = exit_status; | 
|  | } else { | 
|  | exit_status_pointer = &local_exit_status; | 
|  | } | 
|  |  | 
|  | if (pid != -1) { | 
|  | pid_t wait_result = HANDLE_EINTR(waitpid(pid, exit_status_pointer, 0)); | 
|  | if (wait_result != pid) { | 
|  | PLOG(ERROR) << "waitpid"; | 
|  | *exit_status_pointer = -1; | 
|  | } | 
|  | } else { | 
|  | *exit_status_pointer = -1; | 
|  | } | 
|  |  | 
|  | return status; | 
|  | } | 
|  |  | 
|  | }  // namespace mac | 
|  | }  // namespace base |