xamarin.mac multiple do shell script with administrator privilege with one password request

andyeduandyedu CHMember

Dear all of you, I'm trying to convert an old Applescript that used different commands "do shell script... with administrator privilege" so that you can run packages and other similar commands in the MacOS system with high privileges.

With Applescript, the password was requested only once during the first command "... with administrator privilege" while trying a similar code in C# with the function NSApplescript and Xamain.Mac I do not find the way to allow the execution of other commands using the same credentials previously entered by the user.

I've read that normally for 5 minutes these should be valid, but in this case it's as if they were reset after the execution of the first command "do shell script... with administrator privilege".

Does anyone have an idea of how it is the right way to proceed or an alternative to use a similar procedure in this new program created with Xamarin.Mac?

Thank you very much to everyone who can help me in some way.
Greetings.

Answers

  • ChrisHamonsChrisHamons USXamarin Team Xamurai

    Xamarin.Mac can invoke apple scripts via NSAppleScript. We don't have a sample for those, but Apple has a write up here:

    https://developer.apple.com/library/content/technotes/tn2084/_index.html

    You might need a refresher on obj-c syntax to follow it:

    https://developer.xamarin.com/guides/mac/application_fundamentals/mac-apis/
    https://developer.xamarin.com/guides/mac/application_fundamentals/patterns/

    The following stack overflow post may also help:

    https://stackoverflow.com/questions/3541654/how-to-give-permission-using-nstask-objective-c

  • andyeduandyedu CHMember
    edited September 13

    Dear Chris, thank you very much for your help and for answering my question.

    I found an example of code that uses NSAppleScript, but I still have to try to modify it by analyzing the information that is indicated on the websites you have indicated to me.

    In the meantime I have also tried to use another function called "ExecuteWithPrivileges" that seems to work, even if I think I understood that it should not be used anymore because it was deprecated by Apple.

    http://monobook.org/wiki/Xamarin.Mac/AppleScriptを実行する

    using System;
    
    using AppKit;
    using Foundation;
    
    namespace PlayiTunes
    {
        static class MainClass
        {
            static void Main(string[] args)
            {
                // Cocoa初期化(NS〜系クラスを使うためのおまじない)
                NSApplication.Init();
    
                // iTunesを起動し再生するAppleScript
                // 使い終わったら必ずDispose()すること。
                using (var script = new NSAppleScript("tell application \"iTunes\" to play"))
                {
                    // スクリプトを実行する
                    var errors = new NSDictionary();
                    var result = script.ExecuteAndReturnError(out errors);
    
                    if (result != null)
                    {//たぶん正常終了
    
                        // 戻り値はNSAppleEventDescriptor
                        Console.WriteLine(result.Description);
                    }
                    else
                    {//たぶん異常終了
    
                    }
    
                }//dispose script
            }
        }
    }
    
    
    
        string[] nomiFiles = { "pippo.txt", "pluto.txt", "pappa.txt" };
                var defaults = Security.AuthorizationFlags.Defaults;
                using (var auth = Security.Authorization.Create(defaults))
    
                {
    
                    foreach (string value in nomiFiles)
    
                    {
                        var args = new[] { "-c", "\"\"" + "whoami" + " " + ">> /Users/Andrea/Desktop/" + value + "\"\"" };
                        var result = auth.ExecuteWithPrivileges("/bin/sh", defaults, args);
    
                        Console.WriteLine(result);
    
                        args.Initialize();
                    }
    
  • ChrisHamonsChrisHamons USXamarin Team Xamurai

    If you select code blocks and use the "code" formatting option, it will make your posts much more readable. I've taken the liberty to fix the post above.

    ExecuteWithPrivileges works, but Apple wants you to use this API:

    https://developer.apple.com/library/content/samplecode/SMJobBless/Introduction/Intro.html

    It is a total nightmare to use however, and they haven't removed ExecuteWithPrivileges for many releases at this point.

  • andyeduandyedu CHMember

    Dear Chris, thank you for settling the formatting of the code in the previous commentary.

    For the problem mentioned in my question, I was thinking about using the "ExecuteWithPrivileges" function for now and then trying to run that "NSAppleScript" to get an alternative if the first one no longer works in the future.

    Maybe as MacOS 10.13 will come out in about ten days, I can check if the "deprecated" function will also work with this version and then proceed in this way if everything is OK.

    Thank you again.

  • AndreaDellaviaAndreaDellavia USMember

    Good morning to everyone, I write to confirm that the "ExecuteWithPrivileges" function also works well with the new macOS 10.13.

    I would then need to ask another question about this function, as I found an example of an Objective C code that I slightly modified in C# to try to get the standard out by running a process with high privileges.

    I tried to find out if this code is really usable with Xamain. Mac but unfortunately after several attempts I didn't get a result.

    Please let me know if it is really possible to add the communication channel to the basic function "ExecuteWithPrivileges" to read the standard out during execution.

    Thank you very much for your help.
    Regards.

    #include <stdio.h>
    #include <stdlib.h>
    #include <CoreFoundation/CoreFoundation.h>
    #include <Security/Authorization.h>
    #include <Security/AuthorizationTags.h>
    
    int main (int argc, const char[] argv)
    {
        AuthorizationRef authRef;
        AuthorizationItem authItem;
        AuthorizationRights authRights;
        AuthorizationFlags authFlags;
        OSStatus authStatus;
        printf("awaiting: AuthorizationCreate\n");
        authStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, authRef);
        if (authStatus != errAuthorizationSuccess)
        {
            printf("AuthorizationCreate // authStatus == %d\n", authStatus);
            return 1;
        }
        else
        {
            printf("success: AuthorizationCreate // authStatus == %d\n", authStatus);
        }
    
        authItem.Name = "ntp.server.startStopService";
        authItem.ValueLength = 0;
        authItem.Value = NULL;
        authItem.Flags = 0;
        authRights.Count = 1;
        authRights.Items = authItem;
        authFlags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights;
        printf("awaiting: AuthorizationCopyRights\n");
        authStatus = AuthorizationCopyRights(authRef, authRights, kAuthorizationEmptyEnvironment, authFlags, NULL);
        if (authStatus != errAuthorizationSuccess)
        {
            printf("AuthorizationCreate // authStatus == %d\n", authStatus);
            return 1;
        }
        else
        {
            printf("success: AuthorizationCopyRights // authStatus == %d\n", authStatus);
        }
    
        FILE pipe = NULL;
        int inbytes;
        char[] buffer = new char[1024];
        char[] toolpath = "/sbin/SystemStarter";
        char[] tooloptions = {"-d", "stop", "\"Network Time\"", NULL};
        printf("awaiting: AuthorizationExecuteWithPrivileges\n");
        authStatus = AuthorizationExecuteWithPrivileges(authRef, toolpath, kAuthorizationFlagDefaults, tooloptions, pipe);
        if (authStatus == errAuthorizationSuccess)
        {
            printf("success: AuthorizationExecuteWithPrivileges // authStatus == %d\n", authStatus);
            for (;;)
            {
                inbytes = read(fileno(pipe), buffer, sizeof (buffer));
                if (inbytes < 1)
                {
                    break;
                }
    
                write(stdout, buffer, inbytes);
            }
    
        }
        else
        {
            printf("AuthorizationExecuteWithPrivileges // authStatus == %d\n", authStatus);
            return 1;
        }
    
        return 0;
    }
    
  • ChrisHamonsChrisHamons USXamarin Team Xamurai

    There is no reason that shouldn't work, given enough work, from C#. You are literally just invoking a C function, getting a pipe, and reading it.

    The "tricky" part, if there is one, is binding the relevant C functions in p/invokes.

    What do you have so far?

  • AndreaDellaviaAndreaDellavia USMember

    Dear Chris, thank you very much for your reply.

    In truth I don't have any additional code compared to the basic code I found on the monobook site.

    I tried in various ways, following the guidelines of the APIs on the Xamarin website, to modify it to add the communication part through the pipe, but every time I got an error of some kind.

    I think that this is probably a limitation due to my too limited knowledge of this programming language and I do not doubt that it is easy to solve for anyone who has been using it for a long time.

    Thank you for having confirmed that this is possible and I think that in one way or another I will find a solution :)

    Best regards.

    developer.xamarin.com/api/type/MonoMac.Security.Authorization/

    monobook.org/wiki/Xamarin.Mac/管理者権限に昇格して実行する

            using System;
            using AppKit;
            using Security;
    
            namespace Auth
            {
                static class MainClass
                {
                    static void Main(string[] args)
                    {
                        NSApplication.Init();
    
                        var ret = Auth();
                        Console.WriteLine(ret);
                    }
    
                    static AuthorizationStatus Auth()
                    {
                        var defaults = AuthorizationFlags.Defaults;
    
                        using (var auth = Authorization.Create(defaults))
                        {
                            // ExecuteWithPrivilegesメソッドを呼ぶと認証ダイアログが表示される。
                            var args = new[]{"-c", "\"\"mkdir -p /usr/share/MyFolder\"\""};
                            var ret = (AuthorizationStatus)auth.ExecuteWithPrivileges("/bin/sh", defaults, args);
                            return ret;
                        }  
                    }
                }
            }
    
Sign In or Register to comment.