blob: 1277af4cb1bae071fae794cc19b96bcb3ea5bea5 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2012 The Chromium Authors
[email protected]7d791652010-12-01 16:34:492// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
danakj51d26a42024-04-25 14:23:565#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
[email protected]ef12d1e62012-03-21 20:55:0510#include "base/mac/authorization_util.h"
[email protected]7d791652010-12-01 16:34:4911
12#import <Foundation/Foundation.h>
avidbcc8412015-12-24 06:28:3213#include <stddef.h>
[email protected]7d791652010-12-01 16:34:4914#include <sys/wait.h>
15
16#include <string>
17
Avi Drissmand4f07082023-05-12 18:05:4418#include "base/apple/bundle_locations.h"
Avi Drissmaneac566b02023-08-18 02:56:2119#include "base/apple/foundation_util.h"
Avi Drissman280c58a2023-08-16 16:27:5520#include "base/apple/osstatus_logging.h"
[email protected]7d791652010-12-01 16:34:4921#include "base/logging.h"
[email protected]ef12d1e62012-03-21 20:55:0522#include "base/mac/scoped_authorizationref.h"
[email protected]2025d002012-11-14 20:54:3523#include "base/posix/eintr_wrapper.h"
[email protected]3ea1b182013-02-08 22:38:4124#include "base/strings/string_number_conversions.h"
[email protected]d529cb02013-06-10 19:06:5725#include "base/strings/string_util.h"
Avi Drissman21e25b12023-06-22 14:30:5726#include "base/strings/sys_string_conversions.h"
Olivier Li9646c09f2021-08-09 19:12:5527#include "base/threading/hang_watcher.h"
[email protected]7d791652010-12-01 16:34:4928
Avi Drissmanefca4122022-01-05 23:59:3629namespace base::mac {
[email protected]7d791652010-12-01 16:34:4930
Mark Rowe463ae622023-02-14 23:51:5731ScopedAuthorizationRef CreateAuthorization() {
[email protected]a6e349a62011-06-27 16:26:2432 ScopedAuthorizationRef authorization;
Avi Drissman70141bfa2022-12-30 20:52:5833 OSStatus status = AuthorizationCreate(
34 /*rights=*/nullptr, kAuthorizationEmptyEnvironment,
35 kAuthorizationFlagDefaults, authorization.InitializeInto());
[email protected]7d791652010-12-01 16:34:4936 if (status != errAuthorizationSuccess) {
[email protected]33b98202012-01-27 23:06:4937 OSSTATUS_LOG(ERROR, status) << "AuthorizationCreate";
Avi Drissman70141bfa2022-12-30 20:52:5838 return ScopedAuthorizationRef();
[email protected]7d791652010-12-01 16:34:4939 }
40
Mark Rowe463ae622023-02-14 23:51:5741 return authorization;
42}
43
44ScopedAuthorizationRef GetAuthorizationRightsWithPrompt(
45 AuthorizationRights* rights,
46 CFStringRef prompt,
47 AuthorizationFlags extra_flags) {
48 ScopedAuthorizationRef authorization = CreateAuthorization();
Mark Rowe31692c62023-02-16 12:05:0149 if (!authorization) {
Mark Rowe463ae622023-02-14 23:51:5750 return authorization;
51 }
52
Olivier Li9646c09f2021-08-09 19:12:5553 // 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
[email protected]429a6e02013-10-23 18:40:4861 AuthorizationFlags flags = kAuthorizationFlagDefaults |
62 kAuthorizationFlagInteractionAllowed |
63 kAuthorizationFlagExtendRights |
Avi Drissman70141bfa2022-12-30 20:52:5864 kAuthorizationFlagPreAuthorize | extra_flags;
[email protected]7d791652010-12-01 16:34:4965
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 =
Avi Drissmand4f07082023-05-12 18:05:4469 [base::apple::FrameworkBundle() pathForResource:@"product_logo_32"
70 ofType:@"png"];
[email protected]7d791652010-12-01 16:34:4971 const char* icon_path_c = [icon_path fileSystemRepresentation];
72 size_t icon_path_length = icon_path_c ? strlen(icon_path_c) : 0;
73
Avi Drissman6fdbaf082023-06-07 19:35:0974 // The OS will display |prompt| along with a sentence asking the user to type
vabra7553ab2015-10-16 08:48:1075 // the "password to allow this."
Avi Drissmancadb19a02023-07-25 20:46:2776 std::string prompt_string;
Avi Drissman21e25b12023-06-22 14:30:5777 const char* prompt_c = nullptr;
78 size_t prompt_length = 0;
79 if (prompt) {
Avi Drissmancadb19a02023-07-25 20:46:2780 prompt_string = SysCFStringRefToUTF8(prompt);
Avi Drissman21e25b12023-06-22 14:30:5781 prompt_c = prompt_string.c_str();
82 prompt_length = prompt_string.length();
83 }
[email protected]7d791652010-12-01 16:34:4984
85 AuthorizationItem environment_items[] = {
Peter Kasting134ef9af2024-12-28 02:30:0986 {kAuthorizationEnvironmentIcon, icon_path_length, (void*)icon_path_c, 0},
87 {kAuthorizationEnvironmentPrompt, prompt_length, (void*)prompt_c, 0}};
[email protected]7d791652010-12-01 16:34:4988
Daniel Chengf45f47602022-02-28 22:38:3289 AuthorizationEnvironment environment = {std::size(environment_items),
[email protected]7d791652010-12-01 16:34:4990 environment_items};
91
Mark Rowe463ae622023-02-14 23:51:5792 OSStatus status = AuthorizationCopyRights(authorization, rights, &environment,
93 flags, nullptr);
[email protected]429a6e02013-10-23 18:40:4894
[email protected]7d791652010-12-01 16:34:4995 if (status != errAuthorizationSuccess) {
96 if (status != errAuthorizationCanceled) {
[email protected]33b98202012-01-27 23:06:4997 OSSTATUS_LOG(ERROR, status) << "AuthorizationCopyRights";
[email protected]7d791652010-12-01 16:34:4998 }
Avi Drissman70141bfa2022-12-30 20:52:5899 return ScopedAuthorizationRef();
[email protected]7d791652010-12-01 16:34:49100 }
101
Avi Drissman70141bfa2022-12-30 20:52:58102 return authorization;
[email protected]7d791652010-12-01 16:34:49103}
104
Avi Drissman70141bfa2022-12-30 20:52:58105ScopedAuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt) {
[email protected]429a6e02013-10-23 18:40:48106 // Specify the "system.privilege.admin" right, which allows
107 // AuthorizationExecuteWithPrivileges to run commands as root.
108 AuthorizationItem right_items[] = {
Avi Drissman70141bfa2022-12-30 20:52:58109 {kAuthorizationRightExecute, 0, nullptr, 0}};
Daniel Chengf45f47602022-02-28 22:38:32110 AuthorizationRights rights = {std::size(right_items), right_items};
[email protected]429a6e02013-10-23 18:40:48111
Avi Drissman70141bfa2022-12-30 20:52:58112 return GetAuthorizationRightsWithPrompt(&rights, prompt, /*extra_flags=*/0);
[email protected]429a6e02013-10-23 18:40:48113}
114
[email protected]7d791652010-12-01 16:34:49115OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization,
116 const char* tool_path,
117 AuthorizationFlags options,
118 const char** arguments,
119 FILE** pipe,
120 pid_t* pid) {
121 // pipe may be NULL, but this function needs one. In that case, use a local
122 // pipe.
123 FILE* local_pipe;
124 FILE** pipe_pointer;
125 if (pipe) {
126 pipe_pointer = pipe;
127 } else {
128 pipe_pointer = &local_pipe;
129 }
130
erikchen1a2f8722016-10-05 16:12:51131// AuthorizationExecuteWithPrivileges is deprecated in macOS 10.7, but no good
132// replacement exists. https://crbug.com/593133.
erikchenbd736b02016-03-08 23:47:39133#pragma clang diagnostic push
134#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[email protected]7d791652010-12-01 16:34:49135 // AuthorizationExecuteWithPrivileges wants |char* const*| for |arguments|,
136 // but it doesn't actually modify the arguments, and that type is kind of
137 // silly and callers probably aren't dealing with that. Put the cast here
138 // to make things a little easier on callers.
Peter Kasting134ef9af2024-12-28 02:30:09139 OSStatus status = AuthorizationExecuteWithPrivileges(
140 authorization, tool_path, options, (char* const*)arguments, pipe_pointer);
erikchenbd736b02016-03-08 23:47:39141#pragma clang diagnostic pop
[email protected]7d791652010-12-01 16:34:49142 if (status != errAuthorizationSuccess) {
143 return status;
144 }
145
146 int line_pid = -1;
147 size_t line_length = 0;
148 char* line_c = fgetln(*pipe_pointer, &line_length);
149 if (line_c) {
150 if (line_length > 0 && line_c[line_length - 1] == '\n') {
151 // line_c + line_length is the start of the next line if there is one.
152 // Back up one character.
153 --line_length;
154 }
155 std::string line(line_c, line_length);
156 if (!base::StringToInt(line, &line_pid)) {
157 // StringToInt may have set line_pid to something, but if the conversion
158 // was imperfect, use -1.
159 LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: funny line: " << line;
160 line_pid = -1;
161 }
162 } else {
163 LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: no line";
164 }
165
166 if (!pipe) {
167 fclose(*pipe_pointer);
168 }
169
170 if (pid) {
171 *pid = line_pid;
172 }
173
174 return status;
175}
176
177OSStatus ExecuteWithPrivilegesAndWait(AuthorizationRef authorization,
178 const char* tool_path,
179 AuthorizationFlags options,
180 const char** arguments,
181 FILE** pipe,
182 int* exit_status) {
183 pid_t pid;
Peter Kasting134ef9af2024-12-28 02:30:09184 OSStatus status = ExecuteWithPrivilegesAndGetPID(
185 authorization, tool_path, options, arguments, pipe, &pid);
[email protected]7d791652010-12-01 16:34:49186 if (status != errAuthorizationSuccess) {
187 return status;
188 }
189
190 // exit_status may be NULL, but this function needs it. In that case, use a
191 // local version.
192 int local_exit_status;
193 int* exit_status_pointer;
194 if (exit_status) {
195 exit_status_pointer = exit_status;
196 } else {
197 exit_status_pointer = &local_exit_status;
198 }
199
200 if (pid != -1) {
201 pid_t wait_result = HANDLE_EINTR(waitpid(pid, exit_status_pointer, 0));
202 if (wait_result != pid) {
203 PLOG(ERROR) << "waitpid";
204 *exit_status_pointer = -1;
205 }
206 } else {
207 *exit_status_pointer = -1;
208 }
209
210 return status;
211}
212
Avi Drissmanefca4122022-01-05 23:59:36213} // namespace base::mac