Home | History | Annotate | Download | only in cocoa
      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 
     20 namespace authorization_util {
     21 
     22 AuthorizationRef 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 
     83 OSStatus 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 
    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 
    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