authorization_util.mm 7.5 KB

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