Launchd failure after calling SMJobBless - cocoa

I am working on an application that will update /etc/hosts. For this, I found that the preferred method of evaluating privileges is by installing a helper tool via SMJobBless. The following code sort of works:
BOOL result = NO;
AuthorizationItem authItem = { kSMRightBlessPrivilegedHelper, 0, NULL, 0 };
AuthorizationRights authRights = { 1, &authItem };
AuthorizationFlags flags = kAuthorizationFlagDefaults |
kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagPreAuthorize |
kAuthorizationFlagExtendRights;
AuthorizationRef authRef = NULL;
CFErrorRef error = NULL;
OSStatus status = AuthorizationCreate(&authRights, kAuthorizationEmptyEnvironment, flags, &authRef);
if (status == errAuthorizationSuccess) {
result = SMJobBless(kSMDomainSystemLaunchd, (CFStringRef)#"com.fictitiousnonsense.MeddoHelper", authRef, &error);
} else {
NSLog(#"Failed to authorize");
}
if (error != NULL) {
NSLog(#"Error: %#", error);
} else {
NSLog(#"I think it worked");
}
My helper tool, com.fictitiousnonsense.MeddoHelper is installed to /Library/PrivilegedHelperTools/com.fictitiousnonsense.MeddoHelper and the plist file for it is installed to /Library/LaunchDaemons, all according to the documentation. The problem is that then this appears in my console:
9/24/12 11:04:41.237 PM launchdadd[9082]: Could not open /Library/PrivilegedHelperTools/com.fictitiousnonsense.MeddoHelper (open() error 2: No such file or directory)
9/24/12 11:04:41.237 PM launchdadd[9082]: FAILURE: The path /Library/PrivilegedHelperTools/com.fictitiousnonsense.MeddoHelper does not exist on-disk.
I can manually run the helper from the terminal and it works. Why won't launchd run it? I have run the SMJobBlessApp sample app that Apple provides and it works fine, my won't, and I can't find a different.
For reference, the entire code is here: https://github.com/varikin/meddo.

You could use dtruss on the command line to trace launchd. It'll show you all the syscalls executed by launchd, and one of those should try to access your privileged helper. The trace should show which syscalls fails precisely that makes launchd think the helper is not there. I'm guessing that it's some sort of permission issue, btw.

Related

Authorization by AEDeterminePermissionToAutomateTarget waits infinit time

I use this method to check Apple Event (Automation) permission:
bool checkSIPforAppIdentifier(const QString &appId)
{
OSStatus status = noErr;
if (#available(macOS 10.14, *)) {
NSAppleEventDescriptor *targetAppEventDescriptor;
targetAppEventDescriptor = [NSAppleEventDescriptor descriptorWithBundleIdentifier:appId.toNSString()];
status = AEDeterminePermissionToAutomateTarget(targetAppEventDescriptor.aeDesc, typeWildCard, typeWildCard, true);
}
return status == noErr;
}
The problem is that the execution freezes at API: AEDeterminePermissionToAutomateTarget and the user is not prompted for authorization.
usage example:
checkSIPforAppIdentifier("com.microsoft.Word");
I have inserted necessary key in info.plist:
<key>NSAppleEventsUsageDescription</key>
<string>XXX uses this feature to do do Typography actions.</string>
My App is not sandboxed.
Hardened Runtime open Apple Events
enter image description here

Firefox Native Messaging runtime.LastError not giving any errors in case of no Native application installed on Connectnative

I am trying to check whether the Native app is installed or not , If it is not I have to prompt the user to download it from the webpage. For chrome I used to achieve by checking the error messages from runtime.LastError. However in case of Firefox it gives error only in console No such native application extension_name and not catching it in the runtime.LastError method.
Is there any way that we can identify whether corresponding Native app is installed or not ?
I am facing issue when Native app is not installed and browser.runtime.lastError is not giving any error.
Can you please suggest if there is any way in Firefox Webextension that we can catch such errors and identify it in code whether the corresponding Native app is installed or not on the user machine.
It will really helpful if someone can provide some info on this.
for e.g. :
startNativeApp: function(force){
// note that when the native app is opened and ready, it will call "_ABC_onAgentReady"
ABC.log('Starting native app.');
if (!ABC.appConnected) {
try {
ABC.nativeAppPort = browser.runtime.connectNative(_ABC_native_app_id);
ABC.nativeAppPort.onMessage.addListener(ABC.onNativeMessageReceived);
ABC.nativeAppPort.onDisconnect.addListener(ABC.onNativeAppDisconnected);
ABC.appInstalled = true;
ABC.appConnected = true;
} catch(e) {
ABC.log('Error starting native app: ' + e.message, 'ERR');
}
} else if (force === true) {
ABC.log('Native app is already running; attempting to stop and will restart in 750ms.');
ABC.stopNativeApp();
setTimeout(function() { ABC.startNativeApp(true); }, 750);
}
},
onNativeAppDisconnected: function(message) {
console.log("ABC LastError : "+browser.runtime.lastError);
console.log("ABC LastError : "+ABC.nativeAppPort.error);
console.log("ABC LastError : "+JSON.stringify(message));
ABC.appConnected = false;
ABC.nativeAppPort = null;
ABC.appInstalled = false;
if (browser.runtime.lastError && (browser.runtime.lastError.message.indexOf("No such native application") !== -1 )) {
ABC.appInstalled = false;
}
// cleanup: reset the sig data so that it is re-requested on the next scan
_ABC_sa_data = "";
_ABC_sigs = "";
if (browser.storage && browser.storage.local) {
browser.storage.local.set({ uid: _ABC_be_uid }, null);
}
ABC.log('Send message to page to stop.');
ABC.sendMessageToPage({ onNativeAppDisconnected: '' });
ABC.log('Native app disconnected.');
},
Issue here was that port.error was not giving any error response in Firefox versions less than 52 , Due to which I was facing problem in identifying whether native app is installed or not.
After discussion on Mozilla Community (https://discourse.mozilla-community.org/t/firefox-native-messaging-runtime-lasterror-not-giving-any-errors-in-case-of-no-native-application-installed-on-connectnative/12880/4) , we found that it is actually missed and a bug is already reported : https://bugzilla.mozilla.org/show_bug.cgi?id=12994116
which will be resolved in Firefox 52.
However , I need to support Firefox 50 also , so the alternate I am using is to call native application in starting to find out whether it is installed or not.
If I got back response than it is installed otherwise it is not.
However specific error messages will be available from Firefox52.
Right now at chrome 109 the following approaches won't work after connectNative:
chrome.runtime.lastError. The error is printed because it is visible in the log but right after the call it is undefined.
console.error = function (arg) {/**/}. Is not working to replace the default function.
port.name is "" in both cases (error or no error).
port.onDisconnect is not called if the application is missing.
The only solution left is to call a third checker:
const promise=chrome.runtime.sendNativeMessage("appname", { /*text: ""*/ });//,check_response
promise.then(check_response,check_error);
In Firefox there is no runtime.lastError.
The listener function you pass to runtime.Port.onDisconnect isn't passed the message, it's passed the port itself.
You then want port.error.
See the documentation for onDisconnect here https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/Port

OpenMutex fails in process opened with CreateProcessAsUser

I've been banging my head on this for days, and I must have read every page on the Internet even remotely related, but I still can't find an answer. Help!
Here's the scenario: In Windows 7, I have a process running under an admin user account (not a service). It creates a global named mutex, which is later used in a child process running under a regular user account. No matter what I do, or what ACLs I put on the mutex, the child process keeps returning Access Denied when trying to get the handle.
I've distilled my code down into a test app just to experiment with the process and mutex parts, and I found something surprising: if I call OpenMutex from the user app without first creating the mutex, I would expect a Not Found error but I still get Access Denied. However, if I launch the user app from Explorer instead (shift-right-click, Run as different user...), I get the expected behavior. I also noticed that the user app has a plain blocky window border rather than the normal Windows theme when launched from the admin app.
So my guess is that there's something wrong with how I'm launching the user app, but I just can't see what I'm missing.
Here are the relevant parts:
bool CUserTest::LogInUser()
{
if ((m_hUserToken == NULL) && !LogonUser(TEST_USER_NAME, L".", TEST_USER_PASS, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &m_hUserToken))
{
CloseHandle(m_hUserToken);
m_hUserToken = NULL;
}
return (m_hUserToken != NULL);
}
bool CUserTest::LaunchTestApp()
{
PROCESS_INFORMATION ProcInfo;
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(si);
si.lpDesktop = L"winsta0\\default";
wchar_t wszCmdLine[MAX_PATH + 1] = { 0 };
wcscpy(wszCmdLine, L"UserTestClient.exe");
bool bSuccess = false;
LPVOID pEnv;
PROFILEINFO sProfileInfo;
ZeroMemory(&sProfileInfo, sizeof(PROFILEINFO));
sProfileInfo.dwSize = sizeof(PROFILEINFO);
sProfileInfo.lpUserName = TEST_USER_NAME;
if (LoadUserProfile(m_hUserToken, &sProfileInfo))
{
if (ImpersonateLoggedOnUser(m_hUserToken))
{
if (CreateEnvironmentBlock(&pEnv, m_hUserToken, FALSE))
{
bSuccess = CreateProcessAsUser(
m_hUserToken,
NULL,
wszCmdLine,
NULL, // ProcessAttributes
NULL, // ThreadAttributes
FALSE, // InheritHandles
CREATE_UNICODE_ENVIRONMENT, // CreationFlags
pEnv, // Environment
NULL, // CurrentDirectory
&si,
&ProcInfo); // ProcessInformation
DestroyEnvironmentBlock(pEnv);
}
RevertToSelf();
}
UnloadUserProfile(m_hUserToken, sProfileInfo.hProfile);
}
if (bSuccess)
{
CloseHandle(ProcInfo.hThread);
CloseHandle(ProcInfo.hProcess);
}
return bSuccess;
}
I never could get the CreateProcessAsUser call to work correctly, but I finally got it working using CreateProcessWithLogonW instead. The trick was to set si.lpDesktop to NULL rather than "winsta0\default", contrary to everything I'd read up to this point.

Keychain Access as root user from MAC Application

I am working on MAC Application to create VPN Connection from MY Application.
After a lot of Research i found that i need to run application as ROOT to store User password and sharedSecretKey in SYSTEM keychain.
Application user will not gonna open application as ROOT so that i need to add user password and sharedsecretkey in SYSTEM KEYCHAIN without ROOT Access.
I search on web on this and found that Apple Provide this code : https://developer.apple.com/library/mac/samplecode/SMJobBless/Introduction/Intro.html
https://developer.apple.com/library/mac/samplecode/EvenBetterAuthorizationSample/Introduction/Intro.html
but didn't understand how can i use this 2 code in my application to store user's password and SharedSecretKey in SYSTEM KEYCHAIN WITH OUT ROOT ACCESS.
Any help will be appreciated.
Thanks in advance.
Here is my code to add Password in SYSTEM KEYCHAIN Which is work great if i run my code as ROOT.
// Vendor dependencies
#import <Security/SecKeychain.h>
// Local dependencies
#import "VPNKeychain.h"
// These are the applications which are going to get access to new Keychain items.
// How do we know them? Just create a VPN service manualy and run the following command:
// security dump-keychain -a /Library/Keychains/System.keychain
// Among the results, you will find your VPN service and you can see the paths that have access to it
static const char * trustedAppPaths[] = {
"/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/Helpers/SCHelper", "/System/Library/PreferencePanes/Network.prefPane/Contents/XPCServices/com.apple.preference.network.remoteservice.xpc",
"/System/Library/CoreServices/SystemUIServer.app",
"/usr/sbin/pppd",
"/usr/sbin/racoon",
"/usr/libexec/configd",
};
// This class contains all code we need to handle System Keychain Items
// Exit status codes: 60-79
#implementation VPNKeychain
// This will create a PPP Password Keychain Item
+ (int) createPasswordKeyChainItem:(NSString*)label forService:(NSString*)service withAccount:(NSString*)account andPassword:(NSString*)password {
return [self createItem:label withService:service account:account description:#"PPP Password" andPassword:password];
}
// This will create an IPSec Shared Secret Keychain Item
+ (int) createSharedSecretKeyChainItem:(NSString*)label forService:(NSString*)service withPassword:(NSString*)password {
service = [NSString stringWithFormat:#"%#.SS", service];
return [self createItem:label withService:service account:#"" description:#"IPSec Shared Secret" andPassword:password];
}
// A generic method to create Keychain Items holding Network service passwords
+ (int) createItem:(NSString*)label withService:(NSString*)service account:(NSString*)account description:(NSString*)description andPassword:(NSString*)password {
// This variable will hold all sorts of operation status responses
OSStatus status;
// Converting the NSStrings to char* variables which we will need later
const char *labelUTF8 = [label UTF8String];
const char *serviceUTF8 = [service UTF8String];
const char *accountUTF8 = [account UTF8String];
const char *descriptionUTF8 = [description UTF8String];
const char *passwordUTF8 = [password UTF8String];
// This variable is soon to hold the System Keychain
SecKeychainRef keychain = NULL;
status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &keychain);
if (status == errSecSuccess) {
NSLog(#"Succeeded opening System Keychain");
} else {
NSLog(#"Could not obtain System Keychain: %#", SecCopyErrorMessageString(status, NULL));
return 60;
}
NSLog(#"Unlocking System Keychain");
status = SecKeychainUnlock(keychain, 0, NULL, FALSE);
if (status == errSecSuccess) {
NSLog(#"Succeeded unlocking System Keychain");
} else {
NSLog(#"Could not unlock System Keychain: %#", SecCopyErrorMessageString(status, NULL));
return 61;
}
// This variable is going to hold our new Keychain Item
SecKeychainItemRef item = nil;
SecAccessRef access = nil;
status = SecAccessCreate(CFSTR("Some VPN Test"), (__bridge CFArrayRef)(self.trustedApps), &access);
if(status == noErr) {
NSLog(#"Created empty Keychain access object");
} else {
NSLog(#"Could not unlock System Keychain: %#", SecCopyErrorMessageString(status, NULL));
return 62;
}
// Putting together the configuration options
SecKeychainAttribute attrs[] = {
{kSecLabelItemAttr, (int)strlen(labelUTF8), (char *)labelUTF8},
{kSecAccountItemAttr, (int)strlen(accountUTF8), (char *)accountUTF8},
{kSecServiceItemAttr, (int)strlen(serviceUTF8), (char *)serviceUTF8},
{kSecDescriptionItemAttr, (int)strlen(descriptionUTF8), (char *)descriptionUTF8},
};
SecKeychainAttributeList attributes = {sizeof(attrs) / sizeof(attrs[0]), attrs};
status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &attributes, (int)strlen(passwordUTF8), passwordUTF8, keychain, access, &item);
if(status == noErr) {
NSLog(#"Successfully created Keychain Item");
} else {
NSLog(#"Creating Keychain item failed: %#", SecCopyErrorMessageString(status, NULL));
return 63;
}
return 0;
}
+(NSArray*) trustedApps {
NSMutableArray *apps = [NSMutableArray array];
SecTrustedApplicationRef app;
OSStatus err;
for (int i = 0; i < (sizeof(trustedAppPaths) / sizeof(*trustedAppPaths)); i++) {
err = SecTrustedApplicationCreateFromPath(trustedAppPaths[i], &app);
if (err == errSecSuccess) {
//NSLog(#"SecTrustedApplicationCreateFromPath succeeded: %#", SecCopyErrorMessageString(err, NULL));
} else {
NSLog(#"SecTrustedApplicationCreateFromPath failed: %#", SecCopyErrorMessageString(err, NULL));
}
[apps addObject:(__bridge id)app];
}
return apps;
}
In OS X, applications do not handle users' credentials directly, but instead request the system to do so, via the function call AuthorizationCopyRights, which is documented in Authorization Services.
A Gui application cannot directly perform administrative (root) actions and since Yosemite (10.10), Gui applications cannot run as root. Instead, your application must use a 'helper' application via XPC services, which is what the SMJobBless and BetterAuthorization samples demonstrate. You can read more about XPC here.
In your case, you would need to create such a helper application, which will have the necessary rights to access the system keychain.
Note that if you plan to distribute your application via the Apple Store, the application must be sandboxed and cannot use any security services, such as calling the function AuthorizationCopyRights.
It is explained in the sample code that you link to, see ReadMe.txt:
Once you run the sample you'll be prompted for an admin user name and
password. Enter your admin user name and password and, if all goes
well, the sample's window will show "The Helper Tool is available!"
indicating that everything is OK. If not, you can look in the console
log for information about the failure.
So generally, your application will have to ask for admin credentials at some point.
Update:
This should be done through a privileged helper tool, as demonstrated in cited SMJobBless example. Your helper tool should perform keychain access for your app. Here are main steps to install such helper tool:
Create authorisation object with AuthorizationCreate function.
Perform preauthorisation on the object with given set of rights using
AuthorizationCopyRights function. This will in fact result in asking your user for admin credentials.
Verify, install and register helper tool with launchd using
SMJobBless function.
Once the helper tool is installed and registered you should use NSXPCConnection to talk to your helper tool. See Sandboxing with NSXPCConnection sample code for details on how to achieve it.

Calls to a COM Server succeed under administrator account but fail under SYSTEM account

I am trying to query some data from a COM server (actually the Motorola MeshAPI), but it fails to initialize. Using the code snippet below, I get successful data when running on the command line as Administrator, but my actual program using this code runs as a service under the SYSTEM account. Using "psexec -i -s cmd.exe" I ran the snippet under the SYSTEM account, and I get the same errors I saw with the service.
It is the CreateInstance() call that fails, with an error message "Server execution failed" and error code 2148007941 (0x80080005).
Under the Windows Event Viewer, in the System pane I see errors with source "DCOM", event ID 10010, and description "The server {....GUID} did not register with DCOM within the required timeout."
I've tried the tips from this answer https://stackoverflow.com/a/1157331 , but I haven't found anything helpful in solving this.
What would cause this to work under Administrator but fail under SYSTEM, and how can I get it to work when running under SYSTEM?
HRESULT hr=S_OK;
USHORT nLinkResistance;
if (!m_MeshNetInitialised)
{
hr = m_pApiCardInterface.CreateInstance("MeshAPI.MeshNet");// mea interface smart pointer
if (SUCCEEDED(hr))
{
Sleep(1000);
m_MeshNetInitialised = true;
}
}
if (m_MeshNetInitialised)
{
hr = m_pApiCardInterface->GetIAPLinkResistance(&nLinkResistance);
if( SUCCEEDED(hr) )
{
l_retval = nLinkResistance;
}
else
{
std::wcout << L"Error getting IAPLinkResistance" << std::endl;
}
}
if (FAILED(hr))
{
_com_error err(hr);
std::wcout << err.ErrorMessage();
}

Resources