diff options
author | lambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-21 20:55:05 +0000 |
---|---|---|
committer | lambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-21 20:55:05 +0000 |
commit | ef12d1e6acb871dbd01f330d0b10ada24d209371 (patch) | |
tree | 1b8976cebc4c141acb3e314406d662a7931a731b /base | |
parent | a160baecdd408a80a6861635a398011622f04718 (diff) | |
download | chromium_src-ef12d1e6acb871dbd01f330d0b10ada24d209371.zip chromium_src-ef12d1e6acb871dbd01f330d0b10ada24d209371.tar.gz chromium_src-ef12d1e6acb871dbd01f330d0b10ada24d209371.tar.bz2 |
Move authorization_util files into base/mac.
No logical code changes in this CL. This moves some Mac utilities from
chrome/browser/mac to base/mac, so they can be used by the Remoting Host
plugin code in remoting/host/plugin
BUG=None
TEST=Compiles, unit_tests run
Review URL: https://chromiumcodereview.appspot.com/9764013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@128053 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/base.gypi | 3 | ||||
-rw-r--r-- | base/mac/authorization_util.h | 69 | ||||
-rw-r--r-- | base/mac/authorization_util.mm | 187 | ||||
-rw-r--r-- | base/mac/scoped_authorizationref.h | 86 |
4 files changed, 345 insertions, 0 deletions
diff --git a/base/base.gypi b/base/base.gypi index 0c556f19..00f169e 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -147,6 +147,8 @@ 'logging.h', 'logging_win.cc', 'logging_win.h', + 'mac/authorization_util.h', + 'mac/authorization_util.mm', 'mac/bundle_locations.h', 'mac/bundle_locations.mm', 'mac/cocoa_protocols.h', @@ -163,6 +165,7 @@ 'mac/os_crash_dumps.cc', 'mac/os_crash_dumps.h', 'mac/scoped_aedesc.h', + 'mac/scoped_authorizationref.h', 'mac/scoped_cftyperef.h', 'mac/scoped_nsautorelease_pool.h', 'mac/scoped_nsautorelease_pool.mm', diff --git a/base/mac/authorization_util.h b/base/mac/authorization_util.h new file mode 100644 index 0000000..c6a63c0 --- /dev/null +++ b/base/mac/authorization_util.h @@ -0,0 +1,69 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MAC_AUTHORIZATION_UTIL_H_ +#define BASE_MAC_AUTHORIZATION_UTIL_H_ +#pragma once + +// AuthorizationExecuteWithPrivileges fork()s and exec()s the tool, but it +// does not wait() for it. It also doesn't provide the caller with access to +// the forked pid. If used irresponsibly, zombie processes will accumulate. +// +// Apple's really gotten us between a rock and a hard place, here. +// +// Fortunately, AuthorizationExecuteWithPrivileges does give access to the +// tool's stdout (and stdin) via a FILE* pipe. The tool can output its pid +// to this pipe, and the main program can read it, and then have something +// that it can wait() for. +// +// The contract is that any tool executed by the wrappers declared in this +// file must print its pid to stdout on a line by itself before doing anything +// else. +// +// http://developer.apple.com/library/mac/#samplecode/BetterAuthorizationSample/Listings/BetterAuthorizationSampleLib_c.html +// (Look for "What's This About Zombies?") + +#include <CoreFoundation/CoreFoundation.h> +#include <Security/Authorization.h> +#include <stdio.h> +#include <sys/types.h> + +namespace base { +namespace mac { + +// Obtains an AuthorizationRef that can be used to run commands as root. If +// necessary, prompts the user for authentication. If the user is prompted, +// |prompt| will be used as the prompt string and an icon appropriate for the +// application will be displayed in a prompt dialog. Note that the system +// appends its own text to the prompt string. Returns NULL on failure. +AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt); + +// Calls straight through to AuthorizationExecuteWithPrivileges. If that +// call succeeds, |pid| will be set to the pid of the executed tool. If the +// pid can't be determined, |pid| will be set to -1. |pid| must not be NULL. +// |pipe| may be NULL, but the tool will always be executed with a pipe in +// order to read the pid from its stdout. +OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization, + const char* tool_path, + AuthorizationFlags options, + const char** arguments, + FILE** pipe, + pid_t* pid); + +// Calls ExecuteWithPrivilegesAndGetPID, and if that call succeeds, calls +// waitpid() to wait for the process to exit. If waitpid() succeeds, the +// exit status is placed in |exit_status|, otherwise, -1 is stored. +// |exit_status| may be NULL and this function will still wait for the process +// to exit. +OSStatus ExecuteWithPrivilegesAndWait(AuthorizationRef authorization, + const char* tool_path, + AuthorizationFlags options, + const char** arguments, + FILE** pipe, + int* exit_status); + +} // namespace mac +} // namespace base + +#endif // BASE_MAC_AUTHORIZATION_UTIL_H_ diff --git a/base/mac/authorization_util.mm b/base/mac/authorization_util.mm new file mode 100644 index 0000000..a9fac3e --- /dev/null +++ b/base/mac/authorization_util.mm @@ -0,0 +1,187 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac/authorization_util.h" + +#import <Foundation/Foundation.h> +#include <sys/wait.h> + +#include <string> + +#include "base/basictypes.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/mac/bundle_locations.h" +#include "base/mac/mac_logging.h" +#import "base/mac/mac_util.h" +#include "base/mac/scoped_authorizationref.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" + +namespace base { +namespace mac { + +AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt) { + // Create an empty AuthorizationRef. + ScopedAuthorizationRef authorization; + OSStatus status = AuthorizationCreate(NULL, + kAuthorizationEmptyEnvironment, + kAuthorizationFlagDefaults, + &authorization); + if (status != errAuthorizationSuccess) { + OSSTATUS_LOG(ERROR, status) << "AuthorizationCreate"; + return NULL; + } + + // Specify the "system.privilege.admin" right, which allows + // AuthorizationExecuteWithPrivileges to run commands as root. + AuthorizationItem right_items[] = { + {kAuthorizationRightExecute, 0, NULL, 0} + }; + AuthorizationRights rights = {arraysize(right_items), right_items}; + + // product_logo_32.png is used instead of app.icns because Authorization + // Services can't deal with .icns files. + NSString* icon_path = + [base::mac::FrameworkBundle() pathForResource:@"product_logo_32" + ofType:@"png"]; + const char* icon_path_c = [icon_path fileSystemRepresentation]; + size_t icon_path_length = icon_path_c ? strlen(icon_path_c) : 0; + + // The OS will append " Type an administrator's name and password to allow + // <CFBundleDisplayName> to make changes." + NSString* prompt_ns = base::mac::CFToNSCast(prompt); + const char* prompt_c = [prompt_ns UTF8String]; + size_t prompt_length = prompt_c ? strlen(prompt_c) : 0; + + AuthorizationItem environment_items[] = { + {kAuthorizationEnvironmentIcon, icon_path_length, (void*)icon_path_c, 0}, + {kAuthorizationEnvironmentPrompt, prompt_length, (void*)prompt_c, 0} + }; + + AuthorizationEnvironment environment = {arraysize(environment_items), + environment_items}; + + AuthorizationFlags flags = kAuthorizationFlagDefaults | + kAuthorizationFlagInteractionAllowed | + kAuthorizationFlagExtendRights | + kAuthorizationFlagPreAuthorize; + + status = AuthorizationCopyRights(authorization, + &rights, + &environment, + flags, + NULL); + if (status != errAuthorizationSuccess) { + if (status != errAuthorizationCanceled) { + OSSTATUS_LOG(ERROR, status) << "AuthorizationCopyRights"; + } + return NULL; + } + + return authorization.release(); +} + +OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization, + const char* tool_path, + AuthorizationFlags options, + const char** arguments, + FILE** pipe, + pid_t* pid) { + // pipe may be NULL, but this function needs one. In that case, use a local + // pipe. + FILE* local_pipe; + FILE** pipe_pointer; + if (pipe) { + pipe_pointer = pipe; + } else { + pipe_pointer = &local_pipe; + } + + // AuthorizationExecuteWithPrivileges wants |char* const*| for |arguments|, + // but it doesn't actually modify the arguments, and that type is kind of + // silly and callers probably aren't dealing with that. Put the cast here + // to make things a little easier on callers. + OSStatus status = AuthorizationExecuteWithPrivileges(authorization, + tool_path, + options, + (char* const*)arguments, + pipe_pointer); + if (status != errAuthorizationSuccess) { + return status; + } + + int line_pid = -1; + size_t line_length = 0; + char* line_c = fgetln(*pipe_pointer, &line_length); + if (line_c) { + if (line_length > 0 && line_c[line_length - 1] == '\n') { + // line_c + line_length is the start of the next line if there is one. + // Back up one character. + --line_length; + } + std::string line(line_c, line_length); + if (!base::StringToInt(line, &line_pid)) { + // StringToInt may have set line_pid to something, but if the conversion + // was imperfect, use -1. + LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: funny line: " << line; + line_pid = -1; + } + } else { + LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: no line"; + } + + if (!pipe) { + fclose(*pipe_pointer); + } + + if (pid) { + *pid = line_pid; + } + + return status; +} + +OSStatus ExecuteWithPrivilegesAndWait(AuthorizationRef authorization, + const char* tool_path, + AuthorizationFlags options, + const char** arguments, + FILE** pipe, + int* exit_status) { + pid_t pid; + OSStatus status = ExecuteWithPrivilegesAndGetPID(authorization, + tool_path, + options, + arguments, + pipe, + &pid); + if (status != errAuthorizationSuccess) { + return status; + } + + // exit_status may be NULL, but this function needs it. In that case, use a + // local version. + int local_exit_status; + int* exit_status_pointer; + if (exit_status) { + exit_status_pointer = exit_status; + } else { + exit_status_pointer = &local_exit_status; + } + + if (pid != -1) { + pid_t wait_result = HANDLE_EINTR(waitpid(pid, exit_status_pointer, 0)); + if (wait_result != pid) { + PLOG(ERROR) << "waitpid"; + *exit_status_pointer = -1; + } + } else { + *exit_status_pointer = -1; + } + + return status; +} + +} // namespace mac +} // namespace base diff --git a/base/mac/scoped_authorizationref.h b/base/mac/scoped_authorizationref.h new file mode 100644 index 0000000..28ebce9 --- /dev/null +++ b/base/mac/scoped_authorizationref.h @@ -0,0 +1,86 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MAC_SCOPED_AUTHORIZATIONREF_H_ +#define BASE_MAC_SCOPED_AUTHORIZATIONREF_H_ +#pragma once + +#include <Security/Authorization.h> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" + +// ScopedAuthorizationRef maintains ownership of an AuthorizationRef. It is +// patterned after the scoped_ptr interface. + +namespace base { +namespace mac { + +class ScopedAuthorizationRef { + public: + explicit ScopedAuthorizationRef(AuthorizationRef authorization = NULL) + : authorization_(authorization) { + } + + ~ScopedAuthorizationRef() { + if (authorization_) { + AuthorizationFree(authorization_, kAuthorizationFlagDestroyRights); + } + } + + void reset(AuthorizationRef authorization = NULL) { + if (authorization_ != authorization) { + if (authorization_) { + AuthorizationFree(authorization_, kAuthorizationFlagDestroyRights); + } + authorization_ = authorization; + } + } + + bool operator==(AuthorizationRef that) const { + return authorization_ == that; + } + + bool operator!=(AuthorizationRef that) const { + return authorization_ != that; + } + + operator AuthorizationRef() const { + return authorization_; + } + + AuthorizationRef* operator&() { + return &authorization_; + } + + AuthorizationRef get() const { + return authorization_; + } + + void swap(ScopedAuthorizationRef& that) { + AuthorizationRef temp = that.authorization_; + that.authorization_ = authorization_; + authorization_ = temp; + } + + // ScopedAuthorizationRef::release() is like scoped_ptr<>::release. It is + // NOT a wrapper for AuthorizationFree(). To force a + // ScopedAuthorizationRef object to call AuthorizationFree(), use + // ScopedAuthorizationRef::reset(). + AuthorizationRef release() WARN_UNUSED_RESULT { + AuthorizationRef temp = authorization_; + authorization_ = NULL; + return temp; + } + + private: + AuthorizationRef authorization_; + + DISALLOW_COPY_AND_ASSIGN(ScopedAuthorizationRef); +}; + +} // namespace mac +} // namespace base + +#endif // BASE_MAC_SCOPED_AUTHORIZATIONREF_H_ |