• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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      [base::mac::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 = base::mac::CFToNSCast(prompt);
52  const char* prompt_c = [prompt_ns UTF8String];
53  size_t prompt_length = prompt_c ? strlen(prompt_c) : 0;
54
55  AuthorizationItem environment_items[] = {
56    {kAuthorizationEnvironmentIcon, icon_path_length, (void*)icon_path_c, 0},
57    {kAuthorizationEnvironmentPrompt, prompt_length, (void*)prompt_c, 0}
58  };
59
60  AuthorizationEnvironment environment = {arraysize(environment_items),
61                                          environment_items};
62
63  AuthorizationFlags flags = kAuthorizationFlagDefaults |
64                             kAuthorizationFlagInteractionAllowed |
65                             kAuthorizationFlagExtendRights |
66                             kAuthorizationFlagPreAuthorize;
67
68  status = AuthorizationCopyRights(authorization,
69                                   &rights,
70                                   &environment,
71                                   flags,
72                                   NULL);
73  if (status != errAuthorizationSuccess) {
74    if (status != errAuthorizationCanceled) {
75      LOG(ERROR) << "AuthorizationCopyRights: " << status;
76    }
77    return NULL;
78  }
79
80  return authorization.release();
81}
82
83OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization,
84                                        const char* tool_path,
85                                        AuthorizationFlags options,
86                                        const char** arguments,
87                                        FILE** pipe,
88                                        pid_t* pid) {
89  // pipe may be NULL, but this function needs one.  In that case, use a local
90  // pipe.
91  FILE* local_pipe;
92  FILE** pipe_pointer;
93  if (pipe) {
94    pipe_pointer = pipe;
95  } else {
96    pipe_pointer = &local_pipe;
97  }
98
99  // AuthorizationExecuteWithPrivileges wants |char* const*| for |arguments|,
100  // but it doesn't actually modify the arguments, and that type is kind of
101  // silly and callers probably aren't dealing with that.  Put the cast here
102  // to make things a little easier on callers.
103  OSStatus status = AuthorizationExecuteWithPrivileges(authorization,
104                                                       tool_path,
105                                                       options,
106                                                       (char* const*)arguments,
107                                                       pipe_pointer);
108  if (status != errAuthorizationSuccess) {
109    return status;
110  }
111
112  int line_pid = -1;
113  size_t line_length = 0;
114  char* line_c = fgetln(*pipe_pointer, &line_length);
115  if (line_c) {
116    if (line_length > 0 && line_c[line_length - 1] == '\n') {
117      // line_c + line_length is the start of the next line if there is one.
118      // Back up one character.
119      --line_length;
120    }
121    std::string line(line_c, line_length);
122    if (!base::StringToInt(line, &line_pid)) {
123      // StringToInt may have set line_pid to something, but if the conversion
124      // was imperfect, use -1.
125      LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: funny line: " << line;
126      line_pid = -1;
127    }
128  } else {
129    LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: no line";
130  }
131
132  if (!pipe) {
133    fclose(*pipe_pointer);
134  }
135
136  if (pid) {
137    *pid = line_pid;
138  }
139
140  return status;
141}
142
143OSStatus 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
160  // exit_status may be NULL, but this function needs it.  In that case, use a
161  // local version.
162  int local_exit_status;
163  int* exit_status_pointer;
164  if (exit_status) {
165    exit_status_pointer = exit_status;
166  } else {
167    exit_status_pointer = &local_exit_status;
168  }
169
170  if (pid != -1) {
171    pid_t wait_result = HANDLE_EINTR(waitpid(pid, exit_status_pointer, 0));
172    if (wait_result != pid) {
173      PLOG(ERROR) << "waitpid";
174      *exit_status_pointer = -1;
175    }
176  } else {
177    *exit_status_pointer = -1;
178  }
179
180  return status;
181}
182
183}  // namespace authorization_util
184