authorization_util.mm revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2009 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
5#include "chrome/browser/cocoa/authorization_util.h"
6
7#import <Foundation/Foundation.h>
8#include <sys/wait.h>
9
10#include <string>
11
12#include "base/basictypes.h"
13#include "base/eintr_wrapper.h"
14#include "base/logging.h"
15#import "base/mac_util.h"
16#include "base/string_number_conversions.h"
17#include "base/string_util.h"
18#include "chrome/browser/cocoa/scoped_authorizationref.h"
19
20namespace authorization_util {
21
22AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt) {
23  // Create an empty AuthorizationRef.
24  scoped_AuthorizationRef authorization;
25  OSStatus status = AuthorizationCreate(NULL,
26                                        kAuthorizationEmptyEnvironment,
27                                        kAuthorizationFlagDefaults,
28                                        &authorization);
29  if (status != errAuthorizationSuccess) {
30    LOG(ERROR) << "AuthorizationCreate: " << status;
31    return NULL;
32  }
33
34  // Specify the "system.privilege.admin" right, which allows
35  // AuthorizationExecuteWithPrivileges to run commands as root.
36  AuthorizationItem right_items[] = {
37    {kAuthorizationRightExecute, 0, NULL, 0}
38  };
39  AuthorizationRights rights = {arraysize(right_items), right_items};
40
41  // product_logo_32.png is used instead of app.icns because Authorization
42  // Services can't deal with .icns files.
43  NSString* icon_path =
44      [mac_util::MainAppBundle() pathForResource:@"product_logo_32"
45                                          ofType:@"png"];
46  const char* icon_path_c = [icon_path fileSystemRepresentation];
47  size_t icon_path_length = icon_path_c ? strlen(icon_path_c) : 0;
48
49  // The OS will append " Type an administrator's name and password to allow
50  // <CFBundleDisplayName> to make changes."
51  NSString* prompt_ns = const_cast<NSString*>(
52      reinterpret_cast<const NSString*>(prompt));
53  const char* prompt_c = [prompt_ns UTF8String];
54  size_t prompt_length = prompt_c ? strlen(prompt_c) : 0;
55
56  AuthorizationItem environment_items[] = {
57    {kAuthorizationEnvironmentIcon, icon_path_length, (void*)icon_path_c, 0},
58    {kAuthorizationEnvironmentPrompt, prompt_length, (void*)prompt_c, 0}
59  };
60
61  AuthorizationEnvironment environment = {arraysize(environment_items),
62                                          environment_items};
63
64  AuthorizationFlags flags = kAuthorizationFlagDefaults |
65                             kAuthorizationFlagInteractionAllowed |
66                             kAuthorizationFlagExtendRights |
67                             kAuthorizationFlagPreAuthorize;
68
69  status = AuthorizationCopyRights(authorization,
70                                   &rights,
71                                   &environment,
72                                   flags,
73                                   NULL);
74  if (status != errAuthorizationSuccess) {
75    if (status != errAuthorizationCanceled) {
76      LOG(ERROR) << "AuthorizationCopyRights: " << status;
77    }
78    return NULL;
79  }
80
81  return authorization.release();
82}
83
84OSStatus 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
100  // AuthorizationExecuteWithPrivileges wants |char* const*| for |arguments|,
101  // but it doesn't actually modify the arguments, and that type is kind of
102  // silly and callers probably aren't dealing with that.  Put the cast here
103  // to make things a little easier on callers.
104  OSStatus status = AuthorizationExecuteWithPrivileges(authorization,
105                                                       tool_path,
106                                                       options,
107                                                       (char* const*)arguments,
108                                                       pipe_pointer);
109  if (status != errAuthorizationSuccess) {
110    return status;
111  }
112
113  int line_pid = -1;
114  size_t line_length = 0;
115  char* line_c = fgetln(*pipe_pointer, &line_length);
116  if (line_c) {
117    if (line_length > 0 && line_c[line_length - 1] == '\n') {
118      // line_c + line_length is the start of the next line if there is one.
119      // Back up one character.
120      --line_length;
121    }
122    std::string line(line_c, line_length);
123    if (!base::StringToInt(line, &line_pid)) {
124      // StringToInt may have set line_pid to something, but if the conversion
125      // was imperfect, use -1.
126      LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: funny line: " << line;
127      line_pid = -1;
128    }
129  } else {
130    LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: no line";
131  }
132
133  if (!pipe) {
134    fclose(*pipe_pointer);
135  }
136
137  if (pid) {
138    *pid = line_pid;
139  }
140
141  return status;
142}
143
144OSStatus ExecuteWithPrivilegesAndWait(AuthorizationRef authorization,
145                                      const char* tool_path,
146                                      AuthorizationFlags options,
147                                      const char** arguments,
148                                      FILE** pipe,
149                                      int* exit_status) {
150  pid_t pid;
151  OSStatus status = ExecuteWithPrivilegesAndGetPID(authorization,
152                                                   tool_path,
153                                                   options,
154                                                   arguments,
155                                                   pipe,
156                                                   &pid);
157  if (status != errAuthorizationSuccess) {
158    return status;
159  }
160
161  // exit_status may be NULL, but this function needs it.  In that case, use a
162  // local version.
163  int local_exit_status;
164  int* exit_status_pointer;
165  if (exit_status) {
166    exit_status_pointer = exit_status;
167  } else {
168    exit_status_pointer = &local_exit_status;
169  }
170
171  if (pid != -1) {
172    pid_t wait_result = HANDLE_EINTR(waitpid(pid, exit_status_pointer, 0));
173    if (wait_result != pid) {
174      PLOG(ERROR) << "waitpid";
175      *exit_status_pointer = -1;
176    }
177  } else {
178    *exit_status_pointer = -1;
179  }
180
181  return status;
182}
183
184}  // namespace authorization_util
185