xref: /aosp_15_r20/external/cronet/base/mac/authorization_util.mm (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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