Question

I am writing an Installer / Autoupdater application for MacOSX (using Qt and C++). I need escalated privileges in order to overwrite the old files in the app folder.

My escalation code is based on the following example: http://www.michaelvobrien.com/blog/2009/07/authorizationexecutewithprivileges-a-simple-example/ I try to relaunch my existing app with admin privileges like this:

void MainDialog::EscalatePrivileges()
{
    AuthorizationRef authorizationRef;
    OSStatus status;

    status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef);

    char* tool = QApplication::instance()->applicationFilePath().toLocal8Bit().data();
    char* args[] = { "STARTUPDATE", NULL };
    FILE* pipe = NULL;

    status = AuthorizationExecuteWithPrivileges(authorizationRef, tool, AuthorizationFlagDefaults, args, &pipe);
    QApplication::instance()->quit();
}

However, I am getting error -60031 (failed to launch tool). Questions:

a) Why does this fail? I suspect its because the working folder is not set correctly...? (Can I set the working folder of the tool somehow?)

EDIT: Ok, figured that one out: The args[] - array itself needs to be NULL-terminated. Already fixed in above code.

b) Other internet sources say the function AuthorizationExecuteWithPrivileges is deprecated and should not be used because of security concerns. Can someone give an example how to do this in a better way?

Était-ce utile?

La solution 2

Yes, AuthorizationExecuteWithPrivileges is deprecated.

Apple now provides a more secure method of handling elevated applications and that is to factor out the code that requires elevation into another, helper application which is given special permission and launched with the elevated rights. That way, if your application was abused due to malware or code injection, it wouldn't be able to gain any elevated access either. The helper app is also signed with your application, so only your application can request it to be launched.

If you take a look at Apple's SMJobBless example, it shows how you go about doing this. Start by taking a look at the ReadMe.txt and I would also suggest doing a Google search for SMJobBless as there is quite a lot of discussion about it.

Autres conseils

Perhaps my answer is late for you, but I hope it can helps anothers Qt Developers. I have create a project in Qt to show how to sign, install an execute a privileged helper tool by using SMJobBless; you can see the code here: https://github.com/mbsanchez/QtPrivilegedHelperExample

I have created it, because there is not documentation on how to install a privileged helper tool, what was developed with C++ on QtCreator.

Edit: I'll explain the process that I was followed to do this.

The problem: You have an application "AppA" what you want to execute with administrative rights from another application "AppB" developed on C++ using QtCreator .

Solution: Since Mac Os X 10.7 the AuthorizationExecuteWithPrivileges function, is deprecated, so you will use SMJobBless instead. But all the documentation and examples about SMJobBless is on Xcode, and there was nothing on C++ until now.

To use SMJobBless you will develop a third application "AppC", this application is commonly named a helper tool, and will be installed as a privileged helper tool using SMJobBless, and executed as a daemon by launchd. Then, like the AppC is executed with admin rights, whatever application executed by AppC, will gain admin rights. So, if you executed the AppA from AppC, AppA will runs with admin rihts. There are 3 things importants here:

  1. AppB will install the privileged helper tool (AppC), using SMJobBless, so, AppB needs a Info.plist file, that has a list of the privileged helper tool that it can install (AppC in this case).

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>CFBundleIdentifier</key>
        <string>com.example.AppB</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        ...
        <key>SMPrivilegedExecutables</key>
        <dict>
            <key>com.example.AppC</key>
            <string>anchor apple generic and identifier &quot;com.example.AppC&quot; and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = XXXXXXXXXX)</string>
        </dict>
    </dict>
    </plist>
    

    Info.plist

  2. AppC needs two plist files, the first one contains information about the launchd task, what is used by the system to launch AppC as a daemon (AppC will not be executed by AppB, once that AppC is installed, it will be launched by the system); the second one has information about what app could install it as a helper tool (AppB in this case).

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>Label</key>
        <string>com.example.AppC</string>
        <key>StandardErrorPath</key>
        <string>/var/log/com.example.appc.log</string>
        <key>Sockets</key>
        <dict>
            <key>com.example.AppC</key>
            <dict>
                 <key>SockFamily</key>
                 <string>Unix</string>
                 <key>SockPathMode</key>
                 <integer>438</integer>
                 <key>SockPathName</key>
                 <string>/var/run/com.example.AppC.socket</string>
                 <key>SockType</key>
                 <string>Stream</string>
            </dict>
        </dict>
    </dict>
    </plist>
    

    AppC-Launchd.plist

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>CFBundleIdentifier</key>
        <string>com.example.AppC</string>
        ...
        <key>SMAuthorizedClients</key>
        <array>
            <string>anchor apple generic and identifier com.example.AppB and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = XXXXXXXXXX)</string>
        </array>
    </dict>
    </plist>
    

    AppC-Info.plist

  3. AppC will be linked with both plist files by using "-sectcreate __TEXT __info_plist myinfo.plist -sectcreate __TEXT __launchd_plist mylaunchd.plist" in the compiler's link flag (QMAKE_LFLAGS in qmake). This will embedded the two plist files into the __TEXT section of the privileged helper application (AppC).

    QMAKE_LFLAGS += -sectcreate __TEXT __info_plist $$PWD/AppC-Info.plist -sectcreate __TEXT __launchd_plist $$PWD/AppC-Launchd.plist
    

    This flag was set in AppC.pro

  4. each occurence of XXXXXXXXXX in plist files, will be changed for the Organization Unit of your apple developer ceritifcate.

  5. AppB and AppC will be signed using a valid apple developer certificate by using codesign tool.
  6. AppC will be copied into the bundle of the AppB in Contents/Library/LaunchServices
  7. AppA will be copied into the bundle of the AppB in Contents/Resources
  8. AppB Info.plist will be copied into the App Bundle.
  9. Next you will sign the AppB Bundle by using codesing tool.
  10. This sample uses unix socket to communicate AppB with AppC launchd daemon, then AppC will start a server connection and wait for commands from AppB, when AppC receives the command it will executes the AppA (you can use execvp C function or whatever you want).
  11. AppB will start a client connection and send the command to the AppC when it needs to run the AppA.

I have tried to explain all the details here, but i think you will have a better view of the solution by examining my code.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top