#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include <Security/Security.h>
#include "SCPreferencesInternal.h"
#include "SCHelper_client.h"
#include "helper_comm.h"
static AuthorizationRef authorization = NULL;
static SCPreferencesRef prefs = NULL;
static Boolean
do_Exit(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
return FALSE;
}
static Boolean
do_Auth(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
if (authorization != NULL) {
AuthorizationFree(authorization, kAuthorizationFlagDefaults);
authorization = NULL;
}
if (data != NULL) {
AuthorizationExternalForm extForm;
if (CFDataGetLength(data) == sizeof(extForm.bytes)) {
OSStatus err;
bcopy(CFDataGetBytePtr(data), extForm.bytes, sizeof(extForm.bytes));
err = AuthorizationCreateFromExternalForm(&extForm, &authorization);
if (err != errAuthorizationSuccess) {
SCLog(TRUE, LOG_ERR,
CFSTR("AuthorizationCreateFromExternalForm() failed: status = %d"),
(int)err);
}
}
CFRelease(data);
}
*status = (authorization != NULL) ? 0 : 1;
return TRUE;
}
static Boolean
do_keychain_copy(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
CFStringRef unique_id = NULL;
if ((data != NULL) && !_SCUnserializeString(&unique_id, data, NULL, 0)) {
return FALSE;
}
if (!isA_CFString(unique_id)) {
return FALSE;
}
*reply = _SCPreferencesSystemKeychainPasswordItemCopy(prefs, unique_id);
CFRelease(unique_id);
if (*reply == NULL) {
*status = SCError();
}
return TRUE;
}
static Boolean
do_keychain_exists(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
Boolean ok;
CFStringRef unique_id = NULL;
if ((data != NULL) && !_SCUnserializeString(&unique_id, data, NULL, 0)) {
return FALSE;
}
if (!isA_CFString(unique_id)) {
return FALSE;
}
ok = _SCPreferencesSystemKeychainPasswordItemExists(prefs, unique_id);
CFRelease(unique_id);
if (!ok) {
*status = SCError();
}
return TRUE;
}
static Boolean
do_keychain_remove(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
Boolean ok;
CFStringRef unique_id = NULL;
if ((data != NULL) && !_SCUnserializeString(&unique_id, data, NULL, 0)) {
return FALSE;
}
if (!isA_CFString(unique_id)) {
return FALSE;
}
ok = _SCPreferencesSystemKeychainPasswordItemRemove(prefs, unique_id);
CFRelease(unique_id);
if (!ok) {
*status = SCError();
}
return TRUE;
}
static Boolean
do_keychain_set(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
CFStringRef account;
CFStringRef description;
CFArrayRef executablePaths = NULL;
CFStringRef label;
Boolean ok;
CFDictionaryRef options = NULL;
CFDataRef password;
CFStringRef unique_id;
if ((data != NULL) && !_SCUnserialize((CFPropertyListRef *)&options, data, NULL, 0)) {
return FALSE;
}
if (!isA_CFDictionary(options)) {
return FALSE;
}
if (CFDictionaryGetValueIfPresent(options,
kSCKeychainOptionsAllowedExecutables,
(const void **)&executablePaths)) {
CFMutableArrayRef executableURLs;
CFIndex i;
CFIndex n;
CFMutableDictionaryRef newOptions;
executableURLs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
n = CFArrayGetCount(executablePaths);
for (i = 0; i < n; i++) {
CFDataRef path;
CFURLRef url;
path = CFArrayGetValueAtIndex(executablePaths, i);
url = CFURLCreateFromFileSystemRepresentation(NULL,
CFDataGetBytePtr(path),
CFDataGetLength(path),
FALSE);
if (url != NULL) {
CFArrayAppendValue(executableURLs, url);
CFRelease(url);
}
}
newOptions = CFDictionaryCreateMutableCopy(NULL, 0, options);
CFDictionarySetValue(newOptions, kSCKeychainOptionsAllowedExecutables, executableURLs);
CFRelease(executableURLs);
CFRelease(options);
options = newOptions;
}
unique_id = CFDictionaryGetValue(options, kSCKeychainOptionsUniqueID);
label = CFDictionaryGetValue(options, kSCKeychainOptionsLabel);
description = CFDictionaryGetValue(options, kSCKeychainOptionsDescription);
account = CFDictionaryGetValue(options, kSCKeychainOptionsAccount);
password = CFDictionaryGetValue(options, kSCKeychainOptionsPassword);
ok = _SCPreferencesSystemKeychainPasswordItemSet(prefs,
unique_id,
label,
description,
account,
password,
options);
CFRelease(options);
if (!ok) {
*status = SCError();
}
return TRUE;
}
static Boolean
do_interface_refresh(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
CFStringRef ifName = NULL;
Boolean ok;
if ((data != NULL) && !_SCUnserializeString(&ifName, data, NULL, 0)) {
SCLog(TRUE, LOG_ERR, CFSTR("interface name not valid"));
return FALSE;
}
if (!isA_CFString(ifName)) {
SCLog(TRUE, LOG_ERR, CFSTR("interface name not valid"));
return FALSE;
}
ok = _SCNetworkInterfaceForceConfigurationRefresh(ifName);
CFRelease(ifName);
if (!ok) {
*status = SCError();
}
return TRUE;
}
static Boolean
do_prefs_Open(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
CFStringRef prefsID = NULL;
if (prefs != NULL) {
return FALSE;
}
if ((data != NULL) && !_SCUnserializeString(&prefsID, data, NULL, 0)) {
SCLog(TRUE, LOG_ERR, CFSTR("prefsID not valid"));
return FALSE;
}
prefs = SCPreferencesCreate(NULL, CFSTR("SCHelper"), prefsID);
if (prefsID != NULL) CFRelease(prefsID);
if (prefs == NULL) {
*status = SCError();
}
return TRUE;
}
static Boolean
do_prefs_Access(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
Boolean ok;
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
CFDataRef signature;
if (prefs == NULL) {
return FALSE;
}
signature = SCPreferencesGetSignature(prefs);
if (signature != NULL) {
const void * dictKeys[2];
const void * dictVals[2];
CFDictionaryRef replyDict;
dictKeys[0] = CFSTR("signature");
dictVals[0] = signature;
dictKeys[1] = CFSTR("preferences");
dictVals[1] = prefsPrivate->prefs;
replyDict = CFDictionaryCreate(NULL,
(const void **)&dictKeys,
(const void **)&dictVals,
sizeof(dictKeys)/sizeof(dictKeys[0]),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
ok = _SCSerialize(replyDict, reply, NULL, NULL);
CFRelease(replyDict);
if (!ok) {
return FALSE;
}
} else {
*status = SCError();
}
return TRUE;
}
static Boolean
do_prefs_Lock(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
CFDataRef clientSignature = (CFDataRef)data;
Boolean ok;
Boolean wait = (info == (void *)FALSE) ? FALSE : TRUE;
if (prefs == NULL) {
return FALSE;
}
ok = SCPreferencesLock(prefs, wait);
if (!ok) {
*status = SCError();
return TRUE;
}
if (clientSignature != NULL) {
CFDataRef serverSignature;
serverSignature = SCPreferencesGetSignature(prefs);
if (!CFEqual(clientSignature, serverSignature)) {
(void)SCPreferencesUnlock(prefs);
*status = kSCStatusStale;
}
}
return TRUE;
}
static Boolean
do_prefs_Commit(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
Boolean ok;
SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
if (prefs == NULL) {
return FALSE;
}
if (data != NULL) {
if (prefsPrivate->prefs != NULL) {
CFRelease(prefsPrivate->prefs);
}
ok = _SCUnserialize((CFPropertyListRef *)&prefsPrivate->prefs, data, NULL, 0);
if (!ok) {
return FALSE;
}
prefsPrivate->accessed = TRUE;
prefsPrivate->changed = TRUE;
}
ok = SCPreferencesCommitChanges(prefs);
if (ok) {
*reply = SCPreferencesGetSignature(prefs);
CFRetain(*reply);
} else {
*status = SCError();
}
return TRUE;
}
static Boolean
do_prefs_Apply(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
Boolean ok;
if (prefs == NULL) {
return FALSE;
}
ok = SCPreferencesApplyChanges(prefs);
if (!ok) {
*status = SCError();
}
return TRUE;
}
static Boolean
do_prefs_Unlock(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
Boolean ok;
if (prefs == NULL) {
return FALSE;
}
ok = SCPreferencesUnlock(prefs);
if (!ok) {
*status = SCError();
}
return TRUE;
}
static Boolean
do_prefs_Close(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
if (prefs == NULL) {
return FALSE;
}
CFRelease(prefs);
prefs = NULL;
return TRUE;
}
static Boolean
do_prefs_Synchronize(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
{
if (prefs == NULL) {
return FALSE;
}
SCPreferencesSynchronize(prefs);
*status = kSCStatusOK;
return TRUE;
}
static Boolean
hasAuthorization()
{
AuthorizationFlags flags;
AuthorizationItem items[1];
AuthorizationRights rights;
OSStatus status;
if (authorization == NULL) {
return FALSE;
}
items[0].name = "system.preferences";
items[0].value = NULL;
items[0].valueLength = 0;
items[0].flags = 0;
rights.count = sizeof(items) / sizeof(items[0]);
rights.items = items;
flags = kAuthorizationFlagDefaults;
flags |= kAuthorizationFlagExtendRights;
flags |= kAuthorizationFlagInteractionAllowed;
status = AuthorizationCopyRights(authorization,
&rights,
kAuthorizationEmptyEnvironment,
flags,
NULL);
if (status != errAuthorizationSuccess) {
return FALSE;
}
if (items[0].flags != 0) SCLog(TRUE, LOG_DEBUG, CFSTR("***** success w/flags (%u) != 0"), items[0].flags);
return TRUE;
}
typedef Boolean (*helperFunction) (void *info,
CFDataRef data,
uint32_t *status,
CFDataRef *reply);
static const struct helper {
int command;
const char *commandName;
Boolean needsAuthorization;
helperFunction func;
void *info;
} helpers[] = {
{ SCHELPER_MSG_AUTH, "AUTH", FALSE, do_Auth , NULL },
{ SCHELPER_MSG_PREFS_OPEN, "PREFS open", FALSE, do_prefs_Open , NULL },
{ SCHELPER_MSG_PREFS_ACCESS, "PREFS access", TRUE, do_prefs_Access , NULL },
{ SCHELPER_MSG_PREFS_LOCK, "PREFS lock", TRUE, do_prefs_Lock , (void *)FALSE },
{ SCHELPER_MSG_PREFS_LOCKWAIT, "PREFS lock/wait", TRUE, do_prefs_Lock , (void *)TRUE },
{ SCHELPER_MSG_PREFS_COMMIT, "PREFS commit", TRUE, do_prefs_Commit , NULL },
{ SCHELPER_MSG_PREFS_APPLY, "PREFS apply", TRUE, do_prefs_Apply , NULL },
{ SCHELPER_MSG_PREFS_UNLOCK, "PREFS unlock", FALSE, do_prefs_Unlock , NULL },
{ SCHELPER_MSG_PREFS_CLOSE, "PREFS close", FALSE, do_prefs_Close , NULL },
{ SCHELPER_MSG_PREFS_SYNCHRONIZE, "PREFS synchronize", FALSE, do_prefs_Synchronize , NULL },
{ SCHELPER_MSG_INTERFACE_REFRESH, "INTERFACE refresh", TRUE, do_interface_refresh , NULL },
{ SCHELPER_MSG_KEYCHAIN_COPY, "KEYCHAIN copy", TRUE, do_keychain_copy , NULL },
{ SCHELPER_MSG_KEYCHAIN_EXISTS, "KEYCHAIN exists", TRUE, do_keychain_exists , NULL },
{ SCHELPER_MSG_KEYCHAIN_REMOVE, "KEYCHAIN remove", TRUE, do_keychain_remove , NULL },
{ SCHELPER_MSG_KEYCHAIN_SET, "KEYCHAIN set", TRUE, do_keychain_set , NULL },
{ SCHELPER_MSG_EXIT, "EXIT", FALSE, do_Exit , NULL }
};
#define nHELPERS (sizeof(helpers)/sizeof(struct helper))
static int
findHelper(command)
{
int i;
for (i = 0; i < (int)nHELPERS; i++) {
if (helpers[i].command == command) {
return i;
}
}
return -1;
}
int
main(int argc, char **argv)
{
int err = 0;
Boolean ok = TRUE;
openlog("SCHelper", LOG_CONS|LOG_PID, LOG_DAEMON);
if (geteuid() != 0) {
(void)__SCHelper_txMessage(STDOUT_FILENO, EACCES, NULL);
exit(EACCES);
}
if (!__SCHelper_txMessage(STDOUT_FILENO, 0, NULL)) {
exit(EIO);
}
while (ok) {
uint32_t command;
CFDataRef data;
int i;
CFDataRef reply;
uint32_t status;
command = 0;
data = NULL;
if (!__SCHelper_rxMessage(STDIN_FILENO, &command, &data)) {
SCLog(TRUE, LOG_ERR, CFSTR("no command"));
err = EIO;
break;
}
i = findHelper(command);
if (i == -1) {
SCLog(TRUE, LOG_ERR, CFSTR("received unknown command : %u"), command);
err = EINVAL;
break;
}
SCLog(TRUE, LOG_DEBUG,
CFSTR("processing command \"%s\"%s"),
helpers[i].commandName,
(data != NULL) ? " w/data" : "");
status = kSCStatusOK;
reply = NULL;
if (helpers[i].needsAuthorization && !hasAuthorization()) {
SCLog(TRUE, LOG_DEBUG,
CFSTR("command \"%s\" : not authorized"),
helpers[i].commandName);
status = kSCStatusAccessError;
}
if (status == kSCStatusOK) {
ok = (*helpers[i].func)(helpers[i].info, data, &status, &reply);
}
SCLog(TRUE, LOG_DEBUG,
CFSTR("sending status %u%s"),
status,
(reply != NULL) ? " w/reply" : "");
if (!__SCHelper_txMessage(STDOUT_FILENO, status, reply)) {
err = EIO;
break;
}
if (reply != NULL) {
CFRelease(reply);
}
}
if (prefs != NULL) {
CFRelease(prefs);
}
if (authorization != NULL) {
AuthorizationFree(authorization, kAuthorizationFlagDefaults);
}
exit(err);
}