Git Product home page Git Product logo

sveinbjornt / stprivilegedtask Goto Github PK

View Code? Open in Web Editor NEW
395.0 20.0 69.0 214 KB

An NSTask-like wrapper around the macOS Security Framework's AuthorizationExecuteWithPrivileges function to run shell commands with root privileges in Objective-C / Cocoa.

License: BSD 3-Clause "New" or "Revised" License

Objective-C 95.71% Ruby 3.42% Shell 0.87%
nstask root-privileges objective-c wrapper authorization osx macos macosx authorizationexecutewithprivileges cocoa

stprivilegedtask's Introduction

License Language Release CocoaPods

STPrivilegedTask

A Cocoa-style NSTask-like wrapper class around the AuthorizationExecuteWithPrivileges() function in the macOS Security API to run shell commands with root privileges. Implemented in Objective-C.

STPrivilegedTask was created a very long time ago (~2005) and has a long history. It has been updated over the years to work with the latest versions of macOS (macOS 13 "Ventura" as of writing) and is available via CocoaPods. It relies on a native system function that has been deprecated since Mac OS X 10.7 "Lion" (2011) and will fail gracefully if the function is not available in a future version of the operating system. That being said, caveat emptor!

Usage

Add the following to your Podfile target's dependencies:

    pod 'STPrivilegedTask', '~> 1.0.8'

...or just download the two STPrivileged.* source files and include them directly in your project.

To import:

#import "STPrivilegedTask.h"

Examples

Create and launch task

// Create task
STPrivilegedTask *privilegedTask = [STPrivilegedTask new];
[privilegedTask setLaunchPath:@"/usr/bin/touch"];
[privilegedTask setArguments:@[@"/etc/my_test_file"]];

// Setting working directory is optional, defaults to /
// NSString *path = [[NSBundle mainBundle] resourcePath];
// [privilegedTask setCurrentDirectoryPath:path];

// Launch it, user is prompted for password (blocking)
OSStatus err = [privilegedTask launch];
if (err == errAuthorizationSuccess) {
    NSLog(@"Task successfully launched");
}
else if (err == errAuthorizationCanceled) {
    NSLog(@"User cancelled");
} 
else {
    NSLog(@"Something went wrong, error %d", err);
}

See Authorization.h for a list of possible error codes.

Launch task one-liner

OSStatus err = [STPrivilegedTask launchedPrivilegedTaskWithLaunchPath:@"/bin/sh" 
                                                            arguments:@[@"/path/to/script.sh"]];

Getting task output

// ... Launch task

[privilegedTask waitUntilExit]; // This is blocking

// Read output file handle for data
NSData *outputData = [[privilegedTask outputFileHandle] readDataToEndOfFile];
NSString *outputString = [[NSString alloc] initWithData:outputData 
                                               encoding:NSUTF8StringEncoding];

Getting output while task runs in background

// ... Launch privilegedTask

NSFileHandle *readHandle = [privilegedTask outputFileHandle];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(getOutputData:)
                                             name:NSFileHandleReadCompletionNotification
                                           object:readHandle];
[readHandle readInBackgroundAndNotify];

// ...

- (void)getOutputData:(NSNotification *)aNotification {
    // Get data from notification
    NSData *data = [[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem];
    
    // Make sure there's actual data
    if ([data length]) {
        // Do something with the data
        NSString *outputString = [[NSString alloc] initWithData:data 
                                                       encoding:NSUTF8StringEncoding];
        NSLog(@"%@", outputString);
        
        // Go read more data in the background
        [[aNotification object] readInBackgroundAndNotify];
    } else {
        // Do something else
    }
}

Task termination

You can observe STPrivilegedTaskDidTerminateNotification:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(privilegedTaskFinished:)
                                             name:STPrivilegedTaskDidTerminateNotification
                                           object:nil];

- (void)privilegedTaskFinished:(NSNotification *)aNotification {
    // Do something
}

Or alternatively set a termination handler block:

privilegedTask.terminationHandler = ^(STPrivilegedTask *privilegedTask) {
    NSLog(@"Terminating task: %@", [privilegedTask description]);
};

Launch using external AuthorizationRef

// ... Create your own AuthorizationRef

[STPrivilegedTask launchedPrivilegedTaskWithLaunchPath:@"/bin/sh"
                                             arguments:@"/path/to/script"
                                      currentDirectory:@"/"
                                         authorization:authRef]

AuthorizationExecuteWithPrivileges() is deprecated

AuthorizationExecuteWithPrivileges() is deprecated as of macOS 10.7 but still remains available in macOS 13 "Ventura". If you want to be future-proof, here's how you check if STPrivilegedTask works in the running version of macOS:

// ...create privilegedTask object

OSStatus err = [privilegedTask launch];
if (err == errAuthorizationFnNoLongerExists) {
    NSLog(@"AuthorizationExecuteWithPrivileges not available");
    // Do something to handle the situation
}

If you need to check whether STPrivilegedTask works before you launch the task:

BOOL works = [STPrivilegedTask authorizationFunctionAvailable];

Sample app

A sample app which makes use of STPrivilegedTask is included in the project. This app runs the following script:

#!/bin/sh

echo "/usr/bin/whoami:"
whoami
echo ""
echo "Real User ID:"
echo $UID \($USER\)
echo ""
echo "Effective User ID:"
/usr/bin/id -u
echo ""
echo "Current working directory:"
echo "$PWD"

exit 5

It waits for the script to finish and then shows the output in a window, along with the exit code.

BSD License

Copyright (c) 2008-2023 Sveinbjorn Thordarson <[email protected]> All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

stprivilegedtask's People

Contributors

antonsimakov avatar magervalp avatar marloe avatar nyteshade avatar sveinbjornt avatar watr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stprivilegedtask's Issues

Using STPrivilegedTask to run shell script

I'm trying to execute a series of commands to move a file & change permissions in a shell script saved as a .command file. If I launch the command file directly from terminal then it succeeds however using STPrivilegedTask it seems to fail. Any directions?

Linker error

Im getting a linker error as soon as instatiating your class:

Undefined symbols for architecture x86_64:
"OBJC_CLASS$_STPrivilegedTask", referenced from:
objc-class-ref in ROKAppDelegate.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I have included the Security framework...

Wrong terminationStatus

I noticed that my task reports a wrong terminationStatus. I think the problem is in this code waitpid(_processIdentifier, &_terminationStatus, 0); To get the status information it is necessary to use WEXITSTATUS macro.

Task <STPrivilegedTask: 0x600002117390> is not running

Hi:
I used STPrivilegedTask to kill a process with a pid in my own Demo, but it didnot work with a output "Task <STPrivilegedTask: 0x600002117390> is not running". Why did this happen? How evr, it really works well in your Demo. I just confused...
Here is my code:

    STPrivilegedTask *task= [[STPrivilegedTask alloc] init];

    NSString *cmd = [NSString stringWithFormat:@"/bin/kill -9 %ld", (long)pid.integerValue];
    
    NSMutableArray *components = [[cmd componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] mutableCopy];
    task.launchPath = components[0];


    [components removeObjectAtIndex:0];
    task.arguments = components;

    task.currentDirectoryPath = [[NSBundle  mainBundle] resourcePath];
    [task launch];
    [task waitUntilExit];

fcntl() returns 0

So this may not be as one expects, but fcntl() always returns 0 here (on 10.11), thus it does not report back a desired child pid to wait for.

isExecutableFileAtPath always return no

When I call the method "- (BOOL)isValidShellCommand:(NSString *)cmd
" the shell command(/usr/local/bin/ideviceinfo) always return NO, but use the example project can return YES. This command has been installed in (/usr/local/bin/) directory, but my project can not found the executable file. Compared my project file settings and info.plist file, I can not find the different with the example project. How can I setup the project, make the executable file can be found. Thanks.

"Operation not permitted" output string in new app

I created a new gui-app, When I run it, it present "Operation not permitted" error.
But when I created a new command-line-tool, it works.

Now I need gui-app instead of a command-line-tool. What should I do?

Trying to create a Platypus-Like App that runs as launchd startup.

Catalina blocks any Platypus-built apps I try to use as a LaunchDaemon/LaunchAgent (at the login window). Something just stops them from firing. See here.

I 'wrote' a simple app to just launch a 'script' file in resources. It works and will be adequate, but I'd very much like to double-click, authenticate as an admin and have it run as root ...

#import <Cocoa/Cocoa.h>
#import "STPrivilegedTask.h"

int main(int argc, const char * argv[]) {
@autoreleasepool {
    NSTask *script = [[NSTask alloc] init];
    [script setLaunchPath:@"/bin/zsh"];
    [script setArguments:@[ [[NSBundle mainBundle] pathForResource:@"script" ofType:nil] ]];
    [script launch];
}
   return 0;
}

I have virtually no Objective-C or coding experience beyond shell scripting, but I'm trying. My hope was to have something like this,

#import <Cocoa/Cocoa.h>
#import "STPrivilegedTask.h"

int main(int argc, const char * argv[]) {
@autoreleasepool {
    
   STPrivilegedTask *privilegedTask = [[STPrivilegedTask alloc] init];
   [privilegedTask setLaunchPath:@"/bin/zsh"];
   [privilegedTask setArguments:@[@"script"]];
  
   // Setting working directory is optional, defaults to /
   NSString *path = [[NSBundle mainBundle] resourcePath];
   [privilegedTask setCurrentDirectoryPath:path];
  
   // Launch it, user is prompted for password
   OSStatus err = [privilegedTask launch];
   if (err == errAuthorizationSuccess) {
       NSLog(@"Task successfully launched");
   }
   else if (err == errAuthorizationCanceled) {
       NSLog(@"User cancelled");
   }
   else {
       NSLog(@"Something went wrong");
   }
  
}
   return 0;
}

But this will not pop an authorization for the end user and always returns

2020-02-25 18:05:45.569310-0700 Remote Acccess[33288:288320] Something went wrong

I'm sure I'm missing something that is blatantly obvious to anyone who knows what they are doing.

Thanks for any help.

Doesn't seem to permit root access

I'm using your class and have changed it to work with ARC and NSLog the process output. I'm trying to start /usr/sbin/httpd and your class seems to work as far as asking for the root password, but after that I still get "root privileges needed" ... seems like it doesn't apply the privileges really. I'm in Mountain Lion, latest Xcode.

Something went wrong: -60005

I am running this code on 10.11.6. but it is showing error.
error:

Something went wrong: -60005
Task <STPrivilegedTask: 0x60000009cc50> is not running
outputString=

Code:
STPrivilegedTask *task = [[STPrivilegedTask alloc] init];
[task setLaunchPath:@"/usr/bin/pluginkit"];
[task setArguments: @[@"-vmA"]];
[task setCurrentDirectoryPath:[[NSBundle mainBundle] resourcePath]];

OSStatus err = [task launch];
if (err != errAuthorizationSuccess) {
    if (err == errAuthorizationCanceled) {
        NSLog(@"User cancelled");
        return;
    }  else {
        NSLog(@"Something went wrong: %d", (int)err);
        // For error codes, see http://www.opensource.apple.com/source/libsecurity_authorization/libsecurity_authorization-36329/lib/Authorization.h
    }
}

[task waitUntilExit];

NSFileHandle *readHandle = [task outputFileHandle];
NSData *outputData = [readHandle readDataToEndOfFile];
NSString *outputString = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding];
NSLog(@"outputString=%@",outputString);

Please any suggestion or comment on it.

Thanks & Regards

Remove Authorization PopUp

Hello ,

I am using your Code to run my ShellScript.
This is how I used the code.

STPrivilegedTask *taskToRemoveHelperTool = [STPrivilegedTask launchedPrivilegedTaskWithLaunchPath:pathForUninstallFile arguments:[NSArray arrayWithObjects:@"Uninstall.sh", nil]];

But it ask user to enter password. If this is running as root then why it needs user's password?

My main purpose is to execute this shell script without Authorization PopUp.

Thanks

Running /bin/bash fails to escalate privileges

For some reason, running /bin/bash in an STPrivilegedTask fails to escalate privileges. This only happens with the /bin/bash binary, not the other shell binaries in /bin/. Works fine in zsh, csh, tcsh etc.

screen shot 2017-05-10 at 16 10 13

screen shot 2017-05-10 at 16 10 20

How to catch stderr log?

When I use stdout everything works fine, but currently it doesn't seem to be able to capture the stderr output? I see the commented out code AuthorizationExecuteWithPrivilegesStdErrAndPid, does it work?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.