]> git.saurik.com Git - apple/configd.git/blobdiff - SystemConfiguration.fproj/helper/SCHelper_server.c
configd-204.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / helper / SCHelper_server.c
diff --git a/SystemConfiguration.fproj/helper/SCHelper_server.c b/SystemConfiguration.fproj/helper/SCHelper_server.c
new file mode 100644 (file)
index 0000000..9231e08
--- /dev/null
@@ -0,0 +1,704 @@
+/*
+ * Copyright (c) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#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;
+
+
+/*
+ * EXIT
+ *   (in)  data   = N/A
+ *   (out) status = SCError()
+ *   (out) reply  = N/A
+ */
+static Boolean
+do_Exit(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
+{
+       return FALSE;
+}
+
+
+/*
+ * AUTHORIZE
+ *   (in)  data   = AuthorizationExternalForm
+ *   (out) status = OSStatus
+ *   (out) reply  = N/A
+ */
+static Boolean
+do_Auth(void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
+{
+       if (authorization != NULL) {
+               AuthorizationFree(authorization, kAuthorizationFlagDefaults);
+//             AuthorizationFree(authorization, kAuthorizationFlagDestroyRights);
+               authorization = NULL;
+       }
+
+       if (data != NULL) {
+               AuthorizationExternalForm       extForm;
+
+               if (CFDataGetLength(data) == sizeof(extForm.bytes)) {
+                       OSStatus        err;
+
+                       memcpy(extForm.bytes, CFDataGetBytePtr(data), 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;
+}
+
+
+/*
+ * SCHELPER_MSG_KEYCHAIN_COPY
+ *   (in)  data   = unique_id
+ *   (out) status = SCError()
+ *   (out) reply  = password
+ */
+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;
+}
+
+
+/*
+ * SCHELPER_MSG_KEYCHAIN_EXISTS
+ *   (in)  data   = unique_id
+ *   (out) status = SCError()
+ *   (out) reply  = N/A
+ */
+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;
+}
+
+
+/*
+ * SCHELPER_MSG_KEYCHAIN_REMOVE
+ *   (in)  data   = unique_id
+ *   (out) status = SCError()
+ *   (out) reply  = N/A
+ */
+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;
+}
+
+
+/*
+ * SCHELPER_MSG_KEYCHAIN_SET
+ *   (in)  data   = options dictionary
+ *   (out) status = SCError()
+ *   (out) reply  = N/A
+ */
+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;
+}
+
+
+/*
+ * SCHELPER_MSG_INTERFACE_REFRESH
+ *   (in)  data   = ifName
+ *   (out) status = SCError()
+ *   (out) reply  = N/A
+ */
+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;
+}
+
+
+/*
+ * OPEN
+ *   (in)  data   = prefsID
+ *   (out) status = SCError()
+ *   (out) reply  = N/A
+ */
+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;
+}
+
+
+/*
+ * ACCESS
+ *   (in)  data   = N/A
+ *   (out) status = SCError()
+ *   (out) reply  = current signature + current preferences
+ */
+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();
+       }
+
+       SCPreferencesSynchronize(prefs);
+       return TRUE;
+}
+
+
+/*
+ * LOCK
+ *   (in)  data   = client prefs signature (NULL if check not needed)
+ *   (out) status = SCError()
+ *   (out) reply  = N/A
+ */
+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;
+}
+
+
+/*
+ * COMMIT
+ *   (in)  data   = new preferences (NULL if commit w/no changes)
+ *   (out) status = SCError()
+ *   (out) reply  = new signature
+ */
+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();
+       }
+
+       SCPreferencesSynchronize(prefs);
+       return TRUE;
+}
+
+
+/*
+ * APPLY
+ *   (in)  data   = N/A
+ *   (out) status = SCError()
+ *   (out) reply  = N/A
+ */
+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;
+}
+
+
+/*
+ * UNLOCK
+ *   (in)  data   = N/A
+ *   (out) status = SCError()
+ *   (out) reply  = N/A
+ */
+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;
+}
+
+
+/*
+ * CLOSE
+ *   (in)  data   = N/A
+ *   (out) status = SCError()
+ *   (out) reply  = N/A
+ */
+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
+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;
+//     flags |= kAuthorizationFlagPartialRights;
+//     flags |= kAuthorizationFlagPreAuthorize;
+
+       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_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);
+       }
+
+       // send "we are here" message
+       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);
+//             AuthorizationFree(authorization, kAuthorizationFlagDestroyRights);
+       }
+
+       exit(err);
+}