diff options
Diffstat (limited to 'mDNSResponder/mDNSMacOSX/PreferencePane/ddnswriteconfig.m')
-rw-r--r-- | mDNSResponder/mDNSMacOSX/PreferencePane/ddnswriteconfig.m | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/ddnswriteconfig.m b/mDNSResponder/mDNSMacOSX/PreferencePane/ddnswriteconfig.m new file mode 100644 index 00000000..437879bc --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/ddnswriteconfig.m @@ -0,0 +1,442 @@ +/* + File: ddnswriteconfig.m + + Abstract: Setuid root tool invoked by Preference Pane to perform + privileged accesses to system configuration preferences and the system keychain. + Invoked by PrivilegedOperations.c. + + Copyright: (c) Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under Apple's + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "PrivilegedOperations.h" +#import "ConfigurationRights.h" + +#import <stdio.h> +#import <stdint.h> +#import <stdlib.h> +#import <unistd.h> +#import <fcntl.h> +#import <errno.h> +#import <sys/types.h> +#import <sys/stat.h> +#import <sys/mman.h> +#import <mach-o/dyld.h> +#import <dns_sd.h> +#import <AssertMacros.h> +#import <Security/Security.h> +#import <CoreServices/CoreServices.h> +#import <CoreFoundation/CoreFoundation.h> +#import <SystemConfiguration/SystemConfiguration.h> +#import <Foundation/Foundation.h> + + +static AuthorizationRef gAuthRef = 0; + +static OSStatus +WriteArrayToDynDNS(CFStringRef arrayKey, CFArrayRef domainArray) +{ + SCPreferencesRef store; + OSStatus err = noErr; + CFDictionaryRef origDict; + CFMutableDictionaryRef dict = NULL; + Boolean result; + CFStringRef scKey = CFSTR("/System/Network/DynamicDNS"); + + + // Add domain to the array member ("arrayKey") of the DynamicDNS dictionary + // Will replace duplicate, at head of list + // At this point, we only support a single-item list + store = SCPreferencesCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL); + require_action(store != NULL, SysConfigErr, err=paramErr;); + require_action(true == SCPreferencesLock( store, true), LockFailed, err=coreFoundationUnknownErr;); + + origDict = SCPreferencesPathGetValue(store, scKey); + if (origDict) { + dict = CFDictionaryCreateMutableCopy(NULL, 0, origDict); + } + + if (!dict) { + dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + require_action( dict != NULL, NoDict, err=memFullErr;); + + if (CFArrayGetCount(domainArray) > 0) { + CFDictionarySetValue(dict, arrayKey, domainArray); + } else { + CFDictionaryRemoveValue(dict, arrayKey); + } + + result = SCPreferencesPathSetValue(store, scKey, dict); + require_action(result, SCError, err=kernelPrivilegeErr;); + + result = SCPreferencesCommitChanges(store); + require_action(result, SCError, err=kernelPrivilegeErr;); + result = SCPreferencesApplyChanges(store); + require_action(result, SCError, err=kernelPrivilegeErr;); + +SCError: + CFRelease(dict); +NoDict: + SCPreferencesUnlock(store); +LockFailed: + CFRelease(store); +SysConfigErr: + return err; +} + + +static int +readTaggedBlock(int fd, u_int32_t *pTag, u_int32_t *pLen, char **ppBuff) +// Read tag, block len and block data from stream and return. Dealloc *ppBuff via free(). +{ + ssize_t num; + u_int32_t tag, len; // Don't use ssize_t because that's different on 32- vs. 64-bit + int result = 0; + + num = read(fd, &tag, sizeof tag); + require_action(num == sizeof tag, GetTagFailed, result = -1;); + num = read(fd, &len, sizeof len); + require_action(num == sizeof len, GetLenFailed, result = -1;); + + *ppBuff = (char*) malloc( len); + require_action(*ppBuff != NULL, AllocFailed, result = -1;); + + num = read(fd, *ppBuff, len); + if (num == (ssize_t)len) { + *pTag = tag; + *pLen = len; + } else { + free(*ppBuff); + result = -1; + } + +AllocFailed: +GetLenFailed: +GetTagFailed: + return result; +} + + + +static int +SetAuthInfo( int fd) +{ + int result = 0; + u_int32_t tag, len; + char *p; + + result = readTaggedBlock( fd, &tag, &len, &p); + require( result == 0, ReadParamsFailed); + require( len == sizeof(AuthorizationExternalForm), ReadParamsFailed); + require( len == kAuthorizationExternalFormLength, ReadParamsFailed); + + if (gAuthRef != 0) { + (void) AuthorizationFree(gAuthRef, kAuthorizationFlagDefaults); + gAuthRef = 0; + } + + result = AuthorizationCreateFromExternalForm((AuthorizationExternalForm*) p, &gAuthRef); + + free( p); +ReadParamsFailed: + return result; +} + + +static int +HandleWriteDomain(int fd, int domainType) +{ + CFArrayRef domainArray; + CFDataRef domainData; + int result = 0; + u_int32_t tag, len; + char *p; + + AuthorizationItem scAuth = { UPDATE_SC_RIGHT, 0, NULL, 0 }; + AuthorizationRights authSet = { 1, &scAuth }; + + if (noErr != (result = AuthorizationCopyRights(gAuthRef, &authSet, NULL, (AuthorizationFlags)0, NULL))) + return result; + + result = readTaggedBlock(fd, &tag, &len, &p); + require(result == 0, ReadParamsFailed); + + domainData = CFDataCreate(NULL, (UInt8 *)p, len); + domainArray = (CFArrayRef)[NSUnarchiver unarchiveObjectWithData:(NSData *)domainData]; + + if (domainType) { + result = WriteArrayToDynDNS(SC_DYNDNS_REGDOMAINS_KEY, domainArray); + } else { + result = WriteArrayToDynDNS(SC_DYNDNS_BROWSEDOMAINS_KEY, domainArray); + } + +ReadParamsFailed: + return result; +} + + +static int +HandleWriteHostname(int fd) +{ + CFArrayRef domainArray; + CFDataRef domainData; + int result = 0; + u_int32_t tag, len; + char *p; + + AuthorizationItem scAuth = { UPDATE_SC_RIGHT, 0, NULL, 0 }; + AuthorizationRights authSet = { 1, &scAuth }; + + if (noErr != (result = AuthorizationCopyRights(gAuthRef, &authSet, NULL, (AuthorizationFlags) 0, NULL))) + return result; + + result = readTaggedBlock(fd, &tag, &len, &p); + require(result == 0, ReadParamsFailed); + + domainData = CFDataCreate(NULL, (const UInt8 *)p, len); + domainArray = (CFArrayRef)[NSUnarchiver unarchiveObjectWithData:(NSData *)domainData]; + result = WriteArrayToDynDNS(SC_DYNDNS_HOSTNAMES_KEY, domainArray); + +ReadParamsFailed: + return result; +} + + +static SecAccessRef +MyMakeUidAccess(uid_t uid) +{ + // make the "uid/gid" ACL subject + // this is a CSSM_LIST_ELEMENT chain + CSSM_ACL_PROCESS_SUBJECT_SELECTOR selector = { + CSSM_ACL_PROCESS_SELECTOR_CURRENT_VERSION, // selector version + CSSM_ACL_MATCH_UID, // set mask: match uids (only) + uid, // uid to match + 0 // gid (not matched here) + }; + CSSM_LIST_ELEMENT subject2 = { NULL, 0, 0, {{0,0,0}} }; + subject2.Element.Word.Data = (UInt8 *)&selector; + subject2.Element.Word.Length = sizeof(selector); + CSSM_LIST_ELEMENT subject1 = { &subject2, CSSM_ACL_SUBJECT_TYPE_PROCESS, CSSM_LIST_ELEMENT_WORDID, {{0,0,0}} }; + + + // rights granted (replace with individual list if desired) + CSSM_ACL_AUTHORIZATION_TAG rights[] = { + CSSM_ACL_AUTHORIZATION_ANY // everything + }; + // owner component (right to change ACL) + CSSM_ACL_OWNER_PROTOTYPE owner = { + // TypedSubject + { CSSM_LIST_TYPE_UNKNOWN, &subject1, &subject2 }, + // Delegate + false + }; + // ACL entries (any number, just one here) + CSSM_ACL_ENTRY_INFO acls = + { + // CSSM_ACL_ENTRY_PROTOTYPE + { + { CSSM_LIST_TYPE_UNKNOWN, &subject1, &subject2 }, // TypedSubject + false, // Delegate + { sizeof(rights) / sizeof(rights[0]), rights }, // Authorization rights for this entry + { { 0, 0 }, { 0, 0 } }, // CSSM_ACL_VALIDITY_PERIOD + "" // CSSM_STRING EntryTag + }, + // CSSM_ACL_HANDLE + 0 + }; + + SecAccessRef a = NULL; + (void) SecAccessCreateFromOwnerAndACL(&owner, 1, &acls, &a); + return a; +} + + +static OSStatus +MyAddDynamicDNSPassword(SecKeychainRef keychain, SecAccessRef a, UInt32 serviceNameLength, const char *serviceName, + UInt32 accountNameLength, const char *accountName, UInt32 passwordLength, const void *passwordData) +{ + char * description = DYNDNS_KEYCHAIN_DESCRIPTION; + UInt32 descriptionLength = strlen(DYNDNS_KEYCHAIN_DESCRIPTION); + UInt32 type = 'ddns'; + UInt32 creator = 'ddns'; + UInt32 typeLength = sizeof(type); + UInt32 creatorLength = sizeof(creator); + OSStatus err; + + // set up attribute vector (each attribute consists of {tag, length, pointer}) + SecKeychainAttribute attrs[] = { { kSecLabelItemAttr, serviceNameLength, (char *)serviceName }, + { kSecAccountItemAttr, accountNameLength, (char *)accountName }, + { kSecServiceItemAttr, serviceNameLength, (char *)serviceName }, + { kSecDescriptionItemAttr, descriptionLength, (char *)description }, + { kSecTypeItemAttr, typeLength, (UInt32 *)&type }, + { kSecCreatorItemAttr, creatorLength, (UInt32 *)&creator } }; + SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs }; + + err = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &attributes, passwordLength, passwordData, keychain, a, NULL); + return err; +} + + +static int +SetKeychainEntry(int fd) +// Create a new entry in system keychain, or replace existing +{ + CFDataRef secretData; + CFDictionaryRef secretDictionary; + CFStringRef keyNameString; + CFStringRef domainString; + CFStringRef secretString; + SecKeychainItemRef item = NULL; + int result = 0; + u_int32_t tag, len; + char *p; + char keyname[kDNSServiceMaxDomainName]; + char domain[kDNSServiceMaxDomainName]; + char secret[kDNSServiceMaxDomainName]; + + AuthorizationItem kcAuth = { EDIT_SYS_KEYCHAIN_RIGHT, 0, NULL, 0 }; + AuthorizationRights authSet = { 1, &kcAuth }; + + if (noErr != (result = AuthorizationCopyRights(gAuthRef, &authSet, NULL, (AuthorizationFlags)0, NULL))) + return result; + + result = readTaggedBlock(fd, &tag, &len, &p); + require_noerr(result, ReadParamsFailed); + + secretData = CFDataCreate(NULL, (UInt8 *)p, len); + secretDictionary = (CFDictionaryRef)[NSUnarchiver unarchiveObjectWithData:(NSData *)secretData]; + + keyNameString = (CFStringRef)CFDictionaryGetValue(secretDictionary, SC_DYNDNS_KEYNAME_KEY); + assert(keyNameString != NULL); + + domainString = (CFStringRef)CFDictionaryGetValue(secretDictionary, SC_DYNDNS_DOMAIN_KEY); + assert(domainString != NULL); + + secretString = (CFStringRef)CFDictionaryGetValue(secretDictionary, SC_DYNDNS_SECRET_KEY); + assert(secretString != NULL); + + CFStringGetCString(keyNameString, keyname, kDNSServiceMaxDomainName, kCFStringEncodingUTF8); + CFStringGetCString(domainString, domain, kDNSServiceMaxDomainName, kCFStringEncodingUTF8); + CFStringGetCString(secretString, secret, kDNSServiceMaxDomainName, kCFStringEncodingUTF8); + + result = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); + if (result == noErr) { + result = SecKeychainFindGenericPassword(NULL, strlen(domain), domain, 0, NULL, 0, NULL, &item); + if (result == noErr) { + result = SecKeychainItemDelete(item); + if (result != noErr) fprintf(stderr, "SecKeychainItemDelete returned %d\n", result); + } + + result = MyAddDynamicDNSPassword(NULL, MyMakeUidAccess(0), strlen(domain), domain, strlen(keyname)+1, keyname, strlen(secret)+1, secret); + if (result != noErr) fprintf(stderr, "MyAddDynamicDNSPassword returned %d\n", result); + if (item) CFRelease(item); + } + +ReadParamsFailed: + return result; +} + + +int main( int argc, char **argv) +/* argv[0] is the exec path; argv[1] is a fd for input data; argv[2]... are operation codes. + The tool supports the following operations: + V -- exit with status PRIV_OP_TOOL_VERS + A -- read AuthInfo from input pipe + Wd -- write registration domain to dynamic store + Wb -- write browse domain to dynamic store + Wh -- write hostname to dynamic store + Wk -- write keychain entry for given account name +*/ +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + int commFD = -1, iArg, result = 0; + + if ( 0 != seteuid( 0)) + return -1; + + if ( argc == 3 && 0 == strcmp( argv[2], "V")) + return PRIV_OP_TOOL_VERS; + + if ( argc > 1) + { + commFD = strtol( argv[1], NULL, 0); + lseek( commFD, 0, SEEK_SET); + } + for ( iArg = 2; iArg < argc && result == 0; iArg++) + { + if ( 0 == strcmp( "A", argv[ iArg])) // get auth info + { + result = SetAuthInfo( commFD); + } + else if ( 0 == strcmp( "Wd", argv[ iArg])) // Write registration domain + { + result = HandleWriteDomain( commFD, 1); + } + else if ( 0 == strcmp( "Wb", argv[ iArg])) // Write browse domain + { + result = HandleWriteDomain( commFD, 0); + } + else if ( 0 == strcmp( "Wh", argv[ iArg])) // Write hostname + { + result = HandleWriteHostname( commFD); + } + else if ( 0 == strcmp( "Wk", argv[ iArg])) // Write keychain entry + { + result = SetKeychainEntry( commFD); + } + } + [pool release]; + return result; +} + +// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion +// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" +// To expand "version" to its value before making the string, use STRINGIFY(version) instead +#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s +#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) + +// NOT static -- otherwise the compiler may optimize it out +// The "@(#) " pattern is a special prefix the "what" command looks for +const char VersionString_SCCS[] = "@(#) ddnswriteconfig " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; + +#if _BUILDING_XCODE_PROJECT_ +// If the process crashes, then this string will be magically included in the automatically-generated crash log +const char *__crashreporter_info__ = VersionString_SCCS + 5; +asm(".desc ___crashreporter_info__, 0x10"); +#endif |