Service Management

RSS for tag

The Service Management framework provides facilities to load and unload launchd services and read and modify launchd dictionaries from within an application.

Service Management Documentation

Pinned Posts

Posts under Service Management tag

20 Posts
Sort by:
Post not yet marked as solved
5 Replies
1k Views
My app has a helper application to register with launched using- SMLoginItemSetEnabled When both the main app and helper app are signed with dev cert, the helper app is launched/called as soon as you login, but a prod singed apps do not work as expected. In this case, the helper app is not auto launchd upon login. This behavior is not seen in macOS 11.0/11.1. Here the prod signed helper app is launched as expected upon login. Upon further investigation, I found the following log entries in system.log file Feb 11 00:38:33 MacBook-Pro com.apple.xpc.launchd[1] (com.company.HelperApp[2121]): LaunchServices returned a bundle URL that does not match with the LoginItem's known association. Feb 11 00:38:33 MacBook-Pro com.apple.xpc.launchd[1] (com.company.HelperApp[2121]): Service exited with abnormal code: 78 Feb 11 00:38:33 MacBook-Pro com.apple.xpc.launchd[1] (com.company.HelperApp): Service only ran for 0 seconds. Pushing respawn out by 10 seconds. What does the log mean and how can I get the prod signed builds to work as expected?
Posted
by
Post marked as solved
2 Replies
490 Views
How to call SMLoginItemSetEnabled in Mac Catalyst project ? Our application needs to run automatically at Macos system login. We developed the MacOS app using Mac Catalyst. Normally, MAC projects do this by calling SMLoginItemSetEnabled, but SMLoginItemSetEnabled cannot be called in the MAC Catalyst project. In the <ServiceManagement/ServiceManagement.h> are defined as follows: #if !TARGET_OS_IPHONE #include <Security/Authorization.h> #include <ServiceManagement/SMLoginItem.h> #else // !TARGET_OS_IPHONE The macro “TARGET_OS_IPHONE” is 1 in Mac Catalyst project. Does anyone know how to resove this problem? Thank you!
Posted
by
Post marked as solved
2 Replies
439 Views
I have small integration test that is confirming behavior in XPC communications (leveraging NSXPCConnection) between a dummy XPC service and an XPC client. My test is flaky, which is indicating to me that I don't fully understand the nature of connections between services and clients, and I was hoping to get clarity as to what's happening in my test. My test involves the following steps. XCode Pre-Action: Load the plist for a dummy XPC service into launchctl. Create 2 XPC client objects (in the same client process), each with their own NSXPCConnection to the dummy service, and connect. Tell client 1 to disconnect, which calls NSXPCConnection.invalidate() Using client 2, send a message to the same dummy XPC service over it's own NSXPCConnection object. Wait for the echo response from the dummy XPC service XCode Post-Action: Unload the plist for the dummy XPC service from launchctl    func testMultipleConnections() {     let delegate1 = MockClientDelegate()     let delegate2 = MockClientDelegate()     let client1 = XPCMessagingClientFacade(withServiceName: serviceName, andXPCErrorHandler: {error in })     let client2 = XPCMessagingClientFacade(withServiceName: serviceName, andXPCErrorHandler: {error in })     client1.processMessageDelegate = delegate1     client2.processMessageDelegate = delegate2           _ = client1.connect()     _ = client2.connect()           _ = client1.disconnect()           delegate2.expectation = XCTestExpectation(description: "Message received from echo service")     _ = client2.sendMessage(ofMessageType: eMTAction_Uninstall, withData: ["dummy": "data"])     wait(for: [delegate2.expectation!], timeout: timeout)   } This test sometimes succeeds and sometimes fails. Sometimes, the test expectation at the bottom is fulfilled, and sometimes the timeout is hit. I have tested with excessively long timeouts to rule-out processing-time as as factor. I am suspecting that calling invalidate() on one NSXPCConnection object is somehow causing a separate connection between the same client and service process to also be invalidated. Is it even a valid use-case to have multiple NSXPCConnection objects between a single XPC Service and XPC Client? When NSXPCConnection.invalidate() is called, does it inherently mean nothing can connect and communicate anymore, or is it just for that specific connection? When invalidate() is called, what does launchctl do to manage that service? Does it shut it down and allow subsequent connection attempts to spool the service up again? Does it prevent from any connections from ever being made again?
Posted
by
Post marked as solved
1 Replies
425 Views
After an extensive research, I haven’t found a canonical answer to what seems a fairly common task — placing a launch agent to the LaunchAgents folder. I would like to copy a propriety list file to ~/Library/LaunchAgents/com.mycompany.MyAgent.plist from my sandboxed app. Can this be achieved with app sandbox enabled? Which entitlement should I use (if any)? Will it pass the Mac App Store app review if I enable it? Is there a best practice that I’m missing? I know we’re not supposed to access a path outside of the app sandbox without the user’s consent, but I have a justified and legitimate case to copy a file with a particular com.mycompany.MyAgent.plist name to a very specific folder. ——— Note: I don’t need to manually start the launch agent. macOS will see my .plist file and load it automatically the next time it restarts. (Launch agents are regular user processes so none of this requires any special privileges.) Of course, if I try to copy the .plist file, it’s placed in a folder relative to my app’s container rather than the user’s real home folder. If I disable the sandbox, I get the desired result. Any help is greatly appreciated and good ideas are welcome. Thank you.
Posted
by
Post not yet marked as solved
4 Replies
553 Views
Hey there, I'm trying to employ the same pattern as demonstrated in the EvenBetterAuthorizationSample: an unsandboxed XPC service calls SMJobBless to install a privileged helper service on behalf a sandboxed main app (which isn't allowed to call SMJobBless). It then starts an XPC connection to the Mach service hosted by the privileged service, and hands over the connection back to the main app, along with the XPC service's connection to the security server (AuthorizationRef). When I try to do call SMJobBless from my XPC service, I get these messages: info authd Process /usr/libexec/smd (PID 28881) evaluates 1 rights with flags 00000003 (engine 629): ( "com.apple.ServiceManagement.blesshelper" ) error authd Fatal: interaction not allowed (session has no ui access) (engine 629) default authd Failed to authorize right 'com.apple.ServiceManagement.blesshelper' by client '/usr/libexec/smd' [28881] for authorization created by '/MyApp.app/Contents/XPCServices/IntermediatorXPCService.xpc' [29325] (3,0) (-60007) (engine 629) error authd copy_rights: authorization failed This seems reasonable to me, because I wouldn't expect an XPC service to be capable of running graphics. However, this works just fine in the "App-Sandboxed" app in the EvenBetterAuthorizationSample project. I poked around the available open source code, and found out that this message is logged when the processes' audit session doesn't have AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS set. if (!(session_get_attributes(auth_token_get_session(engine->auth)) & AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS)) { os_log_error(AUTHD_LOG, "Fatal: interaction not allowed (session has no ui access) (engine %lld)", engine->engine_index); return errAuthorizationInteractionNotAllowed; } Out of curiosity, I compared the audit sessions of my XPC service to the one in EBAS using this code: auditinfo_addr_t auditInfo; int result = getaudit_addr(&auditInfo, sizeof(auditInfo)); assert(result == 0 ); if (auditInfo.ai_flags & AU_SESSION_FLAG_IS_INITIAL) { NSLog(@"AU_SESSION_FLAG_IS_INITIAL"); } if (auditInfo.ai_flags & AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS) { NSLog(@"AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS"); } if (auditInfo.ai_flags & AU_SESSION_FLAG_HAS_TTY) { NSLog(@"AU_SESSION_FLAG_HAS_TTY"); } if (auditInfo.ai_flags & AU_SESSION_FLAG_IS_REMOTE) { NSLog(@"AU_SESSION_FLAG_IS_REMOTE"); } if (auditInfo.ai_flags & AU_SESSION_FLAG_HAS_CONSOLE_ACCESS) { NSLog(@"AU_SESSION_FLAG_HAS_CONSOLE_ACCESS"); } if (auditInfo.ai_flags & AU_SESSION_FLAG_HAS_AUTHENTICATED) { NSLog(@"AU_SESSION_FLAG_HAS_AUTHENTICATED"); } Sure enough, I got different results. EBAS: 2021-11-20 18:45:52.792512-0500 com.example.apple-samplecode.EBAS.XPCService[25296:592874] result: 0 2021-11-20 18:45:52.792527-0500 com.example.apple-samplecode.EBAS.XPCService[25296:592874] AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS 2021-11-20 18:45:52.792539-0500 com.example.apple-samplecode.EBAS.XPCService[25296:592874] AU_SESSION_FLAG_HAS_TTY 2021-11-20 18:45:52.792549-0500 com.example.apple-samplecode.EBAS.XPCService[25296:592874] AU_SESSION_FLAG_HAS_CONSOLE_ACCESS (lldb) p auditInfo (auditinfo_addr_t) $0 = { ai_auid = 501 ai_mask = (am_success = 4294967295, am_failure = 4294967295) ai_termid = { at_port = 50331650 at_type = 4 at_addr = ([0] = 0, [1] = 0, [2] = 0, [3] = 0) } ai_asid = 100019 ai_flags = 8240 } My XPC service: 2021-11-20 21:33:44.355007-0500 IntermediatorXPCService[29325:698278] result: 0 (lldb) p auditInfo ▿ __C.auditinfo_addr - ai_auid: 4294967295 ▿ ai_mask: __C.au_mask - am_success: 4294967295 - am_failure: 4294967295 ▿ ai_termid: __C.au_tid_addr - at_port: 0 - at_type: 4 ▿ at_addr: (4 elements) - .0: 0 - .1: 0 - .2: 0 - .3: 0 - ai_asid: 102293 - ai_flags: 0 It looks like ai_flags is all 0. Any ideas why that might be? What is making EBAS special? And also, how can AU_SESSION_FLAG_HAS_TTY and AU_SESSION_FLAG_HAS_CONSOLE_ACCESS be false? I'm reading these logs from the console?! (Another curious observation: audit_session_flags is imported into Swift as RawRepresentable, but not as an OptionSet)
Posted
by
Post not yet marked as solved
1 Replies
273 Views
The documentaion states that SMJobBless returns success if the service is already installed. However, if it's installed and running, SMJobBless fails and returns a code 2 (kSMErrorInternalFailure). I managed to evade the problem by forcing the helper to quit whenever a connection is invalidated or interrupted by stopping the main runloop. It's acceptable since in our case there shouldn't be two clients accessing the helper at the same time, but I still feel there's something wrong when calling SMJobBless and I can't put my finger on it... Any advice on what could go wrong and what I could try to find more about it?
Posted
by
Post not yet marked as solved
6 Replies
515 Views
I had assumed the answer was "copy or create a plist in /Library/LaunchDaemons," but after poking around here and google a bit, I'm more confused. (Which seems to be a normal thing for me, so I'll hold off deciding I'm stupid for a while.) The options that I seem to see are: Copy/create a plist in /Library/LaunchDaemons Have Foo.app/Contents/Library/LaunchAgents, which will, I presume, run something as long as the app is running? Use SMJobBless to install the daemon. This is the preferred way, and requires an embedded launchd plist, which I presume is what will be installedinto /Library/LaunchDaemons? And "embedded" means "pushed into the binary because MachO is infinitely versatile so we can do this if we want to"? This requires user interaction to get an authorization? And... if the app is distributed via MDM, then that can install the launchd plist file without the app needing to run, just like it can install a system extension without the app needing to run?
Posted
by
Post marked as solved
3 Replies
433 Views
Does anyone know if there are any limitations for installing a privileged helper via SMJobBless from a Mac Catalyst application? The ServiceManagement framework is not available on iOS, but I can create a Mac bundle and talk to that from the iOS layer. What I’m not sure about is if the bundle is allowed to install and communicate with a privileged helper? Any one tried this?
Posted
by
Post marked as solved
4 Replies
510 Views
We have a simple sandboxed app with important checkbox, and toggling it enables a system-wide setting. In order for this setting to be applied at login, we use a Service Management login item installed via SMLoginItemSetEnabled, located in in the main app bundle’s Contents/Library/LoginItems folder. A couple of questions: General — does the helper tool need to launch the main app, or can it apply the setting itself? All we have to do is apply a setting at login without showing UI, then terminate the app. Technical — does the helper tool need to be a full application bundle, or can it be just a simple sandboxed command line tool, since it has no UI? Any suggestions are welcome on how to elegantly solve this and still fly in Mac App Store.
Posted
by
Post not yet marked as solved
8 Replies
597 Views
This question came from https://developer.apple.com/forums/thread/695826, where I saw crashes in AppKit if called without a GUI session. What troubles me from there is that our code is registered as a LaunchAgent (under /Library/LaunchAgents), and I was under the impression that a LaunchAgent only runs if a user logs into a GUI session. I tried at least ssh-only sessions and didn't see it launch automatically (I had to manually launch it through ssh to reproduce the crash). But the fact that we see thousands of crash reports coming from a few devices means somehow our LaunchAgent is trying to launch itself automatically &amp; repeatedly on these devices, while there is no GUI session so it keeps crashing. So, maybe there is a legit way to reproduce the scenario, to launch a LaunchAgent without a GUI session that I'm not aware of?
Posted
by
Post not yet marked as solved
7 Replies
438 Views
Hi, I realize that there was a similar question asked about a year ago, but I wasn't sure if it was appropriate to "piggy back" onto that thread, and it didn't seem to have a definitive solution. The thread I'm referring to is: https://developer.apple.com/forums/thread/131568?answerId=415860022 My problem is similar but perhaps not quite the same as I just have one app that 'owns' the helper tool. I am currently on xcode 13 and Monterey. I implemented an app and privileged helper according to the EvenBetterAuthorizationSample, using SMJobBless and everything. The Privileged helper is a console tool, with the sole purpose of running the installer command to install a downloaded installer package. The app and tool are codesigned, and the app has 'Privacy - Downloads Folder Usage Description' in the plist. When I launch the app it should install a specified package via the helper tool. And... it works great, which I was pleasantly surprised with given the complexity of this thing. There is just one problem: The helper tool cannot access the Downloads folder. If the package is located in /users/me, no problem, but if it's /users/me/Downloads then installer can't access the file. This is not unique to '/usr/sbin/installer' command- I tested other commands like '/bin/ls' If I look in Security &amp; Privacy --&gt; Privacy --&gt; Full Disk Access, I can see the helper listed there as com.myCompany.myApp.helper but it is not checked. If I check it manually, then.. it works - Great! Only problem is, for this to work for a user it would have to fail once, and then I would have to explain to the user how to fix it and try again, which is very awkward. While I'm doing testing, between tests I'm using launchctl to unload the helper, and removing it from LaunchDaemons and PrivilegedHelperTools. I'm also using 'tccutil reset SystemPolicyAllFiles com.myCompany.myApp' Another observation: The app itself can somehow write files to the Downloads folder. And it doesn't show a dialog to ask for permission when I launch the app. Obviously one solution is to not even use the Downloads folder these downloads. I could create a folder like /users/me/myDownloads, and download the packages there. That seems a bit silly... or maybe not? Thanks for any help with this.
Posted
by
Post not yet marked as solved
5 Replies
385 Views
We are noticing a strange behavior with our app and launchctl, not sure yet if it is only in M1 Monterey Macos. We have a launchctl plist that has not changed for a while that allows the app to restart after a crash via the Keepalive key &amp;lt;key&amp;gt;KeepAlive&amp;lt;/key&amp;gt; &amp;lt;dict&amp;gt; &amp;lt;key&amp;gt;SuccessfulExit&amp;lt;/key&amp;gt; &amp;lt;false/&amp;gt; What we are noticing is that the app crashes and instead of the app reopening via launchctl , an Apple crash report window opens. The app is used in several headless environments and we are trying to understand asap how to deal with this change of behavior. Any input will be helpful. Is there an option in xcode to modify this kind of behavior ? For example set the app to show a crash report instead of launchctl executing. Is there a way to trace why launchctl did not reopen the app ? Does it make a difference if the app is a debug version ? We have noticed no difference between Release and Debug versions, same behavior.
Posted
by
Post not yet marked as solved
6 Replies
301 Views
My LaunchAgent is launched when a user logs in. However, it is launched AFTER all Applications that were still open at logout have been restored. Is it possible to increase the priority of a LaunchAgent so that it runs before Applications are restored? I tried setting the Nice value in the plist to -20 but that had no effect. This problem can easily be reproduced by sudo reboot in Terminal when there are multiple applications open. At login all Applications are restored.
Posted
by
Post not yet marked as solved
16 Replies
1k Views
I've got an app that is sandboxed, and it requires a privileged helper. I've worked through the EBAS sample app with various updates to conform with current systems. After a lot of work, I've got to a point where I'm stumped. The Python script SMJobBlessUtil.py returns this error, and I don't know what to do to correct it: &lt;path to helper tool&gt;: tool __TEXT / __info_plist section dump malformed (2) I've gone over the various settings numerous times. It doesn't fail for the EBAS sample, but does for my app. Looking at the binary, the __info_plist sections look identical apart from identifiers. This is what mine looks like (identifiers deleted): &lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt; &lt;plist version="1.0"&gt; &lt;dict&gt; &lt;key&gt;CFBundleIdentifier&lt;/key&gt; &lt;string&gt;***&lt;/string&gt; &lt;key&gt;CFBundleInfoDictionaryVersion&lt;/key&gt; &lt;string&gt;6.0&lt;/string&gt; &lt;key&gt;CFBundleName&lt;/key&gt; &lt;string&gt;***&lt;/string&gt; &lt;key&gt;CFBundleVersion&lt;/key&gt; &lt;string&gt;1.0&lt;/string&gt; &lt;key&gt;SMAuthorizedClients&lt;/key&gt; &lt;array&gt; &lt;string&gt;anchor apple generic and identifier "***" 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] = "***")&lt;/string&gt; &lt;/array&gt; &lt;/dict&gt; &lt;/plist&gt; I must be missing something, but I've run out of ideas on where to find it. Anybody got a pointer?
Posted
by
Post marked as solved
7 Replies
509 Views
I was trying out SMAppService on macOS Ventura to register LaunchDaemons and LaunchAgents. Apparently I didn't get it quite right 🙈 So now I am in a situation where I registered a LaunchDaemons (it showed up in System Settings > General > Login Items) and did not throw an error on registering, but it doesn't really work and I am not able to unregister it. SMAppService.unregister() returns SMAppServiceErrorDomain Code 113 "Could not find the specified service". The SMAppService instance I am trying to use for unregistering was created the same way that I registered, using SMAppService.daemon(plistName: "some.plist"). Is there any way to reset the state or remove the broken daemon config manually? I tried removing all copies of my app from the system and rebooting, but that didn't clean it out.
Posted
by
Post marked as solved
1 Replies
72 Views
I put a cromulent plist file in /Library/LaunchAgents; I load it for the current user using launchctl bootstrap gui/501 $plistfile. Great! But if I then log in as a different user, without rebooting, it doesn't run. I can't do a bootstrap for a user who isn't there; I can't do a launchctl load for an agent. This seems like I'm missing something, but googling hasn't helped me a lot. (On top of all that, I am pretty positive this used to work, but I may be thinking back to MacOS not macOS.)
Posted
by
Post not yet marked as solved
0 Replies
78 Views
This week I’m handling a DTS incident from a developer who wants to escalate privileges in their app. This is a tricky problem. Over the years I’ve explained aspects of this both here on DevForums and in numerous DTS incidents. Rather than do that again, I figured I’d collect my thoughts into one place and share them here. If you have questions or comments, please start a new thread with an appropriate tag (Service Management or XPC are the most likely candidates here). Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" BSD Privilege Escalation on macOS macOS has multiple privilege models. Some of these were inherited from its ancestor platforms. For example, Mach messages has a capability-based privilege model. Others were introduced by Apple to address specific user scenarios. For example, macOS 10.14 and later have mandatory access control (MAC), as discussed in On File System Permissions. One of the most important privilege models is the one inherited from BSD. This is the classic users and groups model. Many subsystems within macOS, especially those with a BSD heritage, use this model. For example, a packet tracing tool must open a BPF device, /dev/bpf*, and that requires root privileges. Specifically, the process that calls open must have an effective user ID of 0, that is, the root user. That process is said to be running as root, and escalating BSD privileges is the act of getting code to run as root. IMPORTANT Escalating privileges does not bypass all privilege restrictions. For example, MAC applies to all processes, including those running as root. Indeed, running as root can make things harder because TCC will not display UI when a launchd daemon trips over a MAC restriction. Escalating privileges on macOS is not straightforward. There are many different ways to do this, each with its own pros and cons. The best approach depends on your specific circumstances. Note If you find operations where a root privilege restriction doesn’t make sense, feel free to file a bug requesting that it be lifted. This is not without precedent. For example, in macOS 10.2 (yes, back in 2002!) we made it possible to implement ICMP (ping) without root privileges. And in macOS 10.14 we removed the restriction on binding to low-number ports (r. 17427890). Nice! Decide on One-Shot vs Ongoing Privileges To start, decide whether you want one-shot or ongoing privileges. For one-shot privileges, the user authorises the operation, you perform it, and that’s that. For example, if you’re creating an un-installer for your product, one-shot privileges make sense because, once it’s done, your code is no longer present on the user’s system. In contrast, for ongoing privileges the user authorises the installation of a launchd daemon. This code always runs as root and thus can perform privileged operations at any time. Folks often ask for one-shot privileges but really need ongoing privileges. A classic example of this is a custom installer. In many cases installation isn’t a one-shot operation. Rather, the installer includes a software update mechanism that needs ongoing privileges. If that’s the case, there’s no point dealing with one-shot privileges at all. Just get ongoing privileges and treat your initial operation as a special case within that. Keep in mind that you can convert one-shot privileges to ongoing privileges by installing a launchd daemon. Just Because You Can, Doesn’t Mean You Should Ongoing privileges represent an obvious security risk. Your daemon can perform an operation, but how does it know whether it should perform that operation? There are two common ways to authorise operations: Authorise the user Authorise the client To authorise the user, use Authorization Services. For a specific example of this, look at the EvenBetterAuthorizationSample sample code. Note This sample hasn’t been updated in a while (sorry!) and it’s ironic that one of the things it demonstrates, opening a low-number port, no longer requires root privileges. However, the core concepts demonstrated by the sample are still valid. The packet trace example from above is a situation where authorising the user with Authorization Services makes perfect sense. By default you might want your privileged helper tool to allow any user to run a packet trace. However, your code might be running on a Mac in a managed environment, where the site admin wants to restrict this to just admin users, or just a specific group of users. A custom authorisation right gives the site admin the flexibility to configure authorisation exactly as they want. Authorising the client is a relatively new idea. It assumes that some process is using XPC to request that the daemon perform a privileged operation. In that case, the daemon can use XPC facilities to ensure that only certain processes can make such a request. Doing this securely is a challenge. For specific API advice, see this post. WARNING This authorisation is based on the code signature of the process’s main executable. If the process loads plug-ins [1], the daemon can’t tell the difference between a request coming from the main executable and a request coming from a plug-in. [1] I’m talking in-process plug-ins here. Plug-ins that run in their own process, such as those managed by ExtensionKit, aren’t a concern. Choose an Approach There are (at least) seven different ways to run with root privileges on macOS: A setuid-root executable The sudo command AppleScript’s do shell script command, passing true to the administrator privileges parameter The long-deprecated AuthorizationExecuteWithPrivileges function, in Security framework The SMJobBless function, in Service Management framework An installer package (.pkg) The new-in-macOS 13 (currently in beta) SMAppService facility, a much-needed enhancement to the Service Management framework To choose between them: Do not use a setuid-root executable. Ever. It’s that simple! Doing that is creating a security vulnerability looking for an attacker to exploit it. If you’re working interactively on the command line, use sudo. IMPORTANT sudo is not appropriate to use as an API. While it may be possible to make this work under some circumstances, by the time you’re done you’ll have code that’s way more complicated than the alternatives. If you’re building an ad hoc solution to distribute to a limited audience, and you need one-shot privileges, use either AuthorizationExecuteWithPrivileges or AppleScript. While AuthorizationExecuteWithPrivileges still works, it’s been deprecated for many years. Do not use it in a widely distributed product. The AppleScript approach works great from AppleScript, but you can also use it from native code using NSAppleScript. See the code snippet later in this post. If you only need escalated privileges to install your product, consider using an installer package. That’s by far the easiest solution to this problem. Keep in mind that an installer package can install a launchd daemon and thereby gain ongoing privileges. If you need ongoing privileges but don’t want to ship an installer package, use SMAppService. If you need to deploy to older systems, use SMJobBless. For instructions on using SMAppService, see Updating helper executables from earlier versions of macOS. For a comprehensive example of how to use SMJobBless, see the EvenBetterAuthorizationSample sample code. For the simplest possible example, see the SMJobBless sample code. That has a Python script to help you debug your setup. Unfortunately this hasn’t been updated in a while; see this thread for more. Hints and Tips I’m sure I’ll think of more of these as time goes by but, for the moment, let’s start with the big one… Do not run GUI code as root. In some cases you can make this work but it’s not supported. Moreover, it’s not safe. The GUI frameworks are huge, and thus have a huge attack surface. If you run GUI code as root, you are opening yourself up to security vulnerabilities. Appendix: Running an AppleScript from Native Code Below is an example of running a shell script with elevated privileges using NSAppleScript. WARNING This is not meant to be the final word in privilege escalation. Before using this, work through the steps above to see if it’s the right option for you. Hint It probably isn’t! let url: URL = … file URL for the script to execute … let script = NSAppleScript(source: """ on open (filePath) if class of filePath is not text then error "Expected a single file path argument." end if set shellScript to "exec " & quoted form of filePath do shell script shellScript with administrator privileges end open """)! // Create the Apple event. let event = NSAppleEventDescriptor( eventClass: AEEventClass(kCoreEventClass), eventID: AEEventID(kAEOpenDocuments), targetDescriptor: nil, returnID: AEReturnID(kAutoGenerateReturnID), transactionID: AETransactionID(kAnyTransactionID) ) // Set up the direct object parameter to be a single string holding the // path to our script. let parameters = NSAppleEventDescriptor(string: url.path) event.setDescriptor(parameters, forKeyword: AEKeyword(keyDirectObject)) // The `as NSAppleEventDescriptor?` is required due to a bug in the // nullability annotation on this method’s result (r. 38702068). var error: NSDictionary? = nil guard let result = script.executeAppleEvent(event, error: &error) as NSAppleEventDescriptor? else { let code = (error?[NSAppleScript.errorNumber] as? Int) ?? 1 let message = (error?[NSAppleScript.errorMessage] as? String) ?? "-" throw NSError(domain: "ShellScript", code: code, userInfo: nil) } let scriptResult = result.stringValue ?? ""
Posted
by
Post not yet marked as solved
0 Replies
59 Views
Service Management framework supports installing and uninstalling services, including Service Management login items, launchd agents, and launchd daemons. General: DevForums tag: Service Management Service Management framework documentation Daemons and Services Programming Guide archived documentation Technote 2083 Daemons and Agents — It hasn’t been updated in… well… decades, but it’s still remarkably relevant. BSD Privilege Escalation on macOS DevForums post EvenBetterAuthorizationSample sample code SMJobBless sample code Sandboxing with NSXPCConnection sample code Related tags include: XPC, Apple’s preferred inter-process communication (IPC) mechanism Inter-process communication, for other IPC mechanisms Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com"
Posted
by
Post not yet marked as solved
0 Replies
79 Views
In macOS 13 there's now the SMAppService object which can be retrieved for a login item via its loginItem(identifier:) function. However, the documentation for this function doesn't make sense, it reads in part: The property list name must correspond to a property list in the calling app’s Contents/Library/LoginItems directory This function doesn't take a property list name, it takes a (presumably bundle) identifier. Is it safe to assume despite the documentation that the officially designed behavior is actually the same as the now deprecated SMLoginItemSetEnabled(_:_:) function and that actually there must be a bundle in the calling app'sContents/Library/LoginItems directory with that bundle identifier? I know I can try this out myself and see what happens, but I've found with Apple it's never a good idea to make assumptions which don't match documented behavior.
Posted
by