2  * Copyright (c) 2000-2008, 2010-2013, 2015-2017 Apple Inc. All rights reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   6  * This file contains Original Code and/or Modifications of Original Code 
   7  * as defined in and that are subject to the Apple Public Source License 
   8  * Version 2.0 (the 'License'). You may not use this file except in 
   9  * compliance with the License. Please obtain a copy of the License at 
  10  * http://www.opensource.apple.com/apsl/ and read it before using this 
  13  * The Original Code and all software distributed under the License are 
  14  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  15  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  16  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  17  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  18  * Please see the License for the specific language governing rights and 
  19  * limitations under the License. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  25  * Modification History 
  27  * June 1, 2001                 Allan Nathanson <ajn@apple.com> 
  28  * - public API conversion 
  30  * November 9, 2000             Allan Nathanson <ajn@apple.com> 
  34 #include <TargetConditionals.h> 
  35 #include "SCPreferencesInternal.h" 
  36 #include "SCHelper_client.h" 
  40 #include <sys/errno.h> 
  43 __SCPreferencesCommitChanges_helper(SCPreferencesRef prefs
) 
  45         CFDataRef               data            
= NULL
; 
  47         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
  48         uint32_t                status          
= kSCStatusOK
; 
  49         CFDataRef               reply           
= NULL
; 
  51         if (prefsPrivate
->helper_port 
== MACH_PORT_NULL
) { 
  53                 status 
= kSCStatusAccessError
; 
  57         if (prefsPrivate
->changed
) { 
  58                 ok 
= _SCSerialize(prefsPrivate
->prefs
, &data
, NULL
, NULL
); 
  60                         status 
= kSCStatusFailed
; 
  62                                 SC_log(LOG_NOTICE
, "_SCSerialize() failed"); 
  63                                 SC_log(LOG_NOTICE
, "  prefs = %s", 
  64                                       prefsPrivate
->newPath 
? prefsPrivate
->newPath 
: prefsPrivate
->path
); 
  70         // have the helper "commit" the prefs 
  71 //      status = kSCStatusOK; 
  73         ok 
= _SCHelperExec(prefsPrivate
->helper_port
, 
  74                            SCHELPER_MSG_PREFS_COMMIT
, 
  78         if (data 
!= NULL
) CFRelease(data
); 
  83         if (status 
!= kSCStatusOK
) { 
  87         if (prefsPrivate
->changed
) { 
  88                 if (prefsPrivate
->signature 
!= NULL
) CFRelease(prefsPrivate
->signature
); 
  89                 prefsPrivate
->signature 
= reply
; 
  91                 if (reply 
!= NULL
) CFRelease(reply
); 
  94         prefsPrivate
->changed 
= FALSE
; 
 100         if (prefsPrivate
->helper_port 
!= MACH_PORT_NULL
) { 
 101                 _SCHelperClose(&prefsPrivate
->helper_port
); 
 107         if (reply 
!= NULL
) CFRelease(reply
); 
 114 writen(int ref
, const void *data
, size_t len
) 
 118         const void      *p      
= data
; 
 121                 if ((n 
= write(ref
, p
, left
)) == -1) { 
 122                         if (errno 
!= EINTR
) { 
 135 SCPreferencesCommitChanges(SCPreferencesRef prefs
) 
 139         SCPreferencesPrivateRef prefsPrivate    
= (SCPreferencesPrivateRef
)prefs
; 
 145                 /* sorry, you must provide a session */ 
 146                 _SCErrorSet(kSCStatusNoPrefsSession
); 
 151          * Determine if the we have exclusive access to the preferences 
 152          * and acquire the lock if necessary. 
 154         wasLocked 
= prefsPrivate
->locked
; 
 156                 if (!SCPreferencesLock(prefs
, TRUE
)) { 
 157                         SC_log(LOG_INFO
, "SCPreferencesLock() failed"); 
 162         if (prefsPrivate
->authorizationData 
!= NULL
) { 
 163                 ok 
= __SCPreferencesCommitChanges_helper(prefs
); 
 165                         prefsPrivate
->changed 
= FALSE
; 
 171          * if necessary, apply changes 
 173         if (!prefsPrivate
->changed
) { 
 178          * check if the preferences should be removed 
 180         if (CFDictionaryGetCount(prefsPrivate
->prefs
) == 0) { 
 184                 if ((prefsPrivate
->options 
!= NULL
) && 
 185                     CFDictionaryGetValueIfPresent(prefsPrivate
->options
, 
 186                                                   kSCPreferencesOptionRemoveWhenEmpty
, 
 187                                                   (const void **)&val
) && 
 188                     isA_CFBoolean(val
) && 
 189                     CFBooleanGetValue(val
)) { 
 190                         /* if we've been asked to remove empty .plists */ 
 195         path 
= prefsPrivate
->newPath 
? prefsPrivate
->newPath 
: prefsPrivate
->path
; 
 200 #if     TARGET_OS_EMBEDDED 
 201                 CFStringRef     protectionClass
; 
 202 #endif  // TARGET_OS_EMBEDDED 
 205                 if (stat(prefsPrivate
->path
, &statBuf
) == -1) { 
 206                         if (errno 
== ENOENT
) { 
 207                                 bzero(&statBuf
, sizeof(statBuf
)); 
 208                                 statBuf
.st_mode 
= 0644; 
 209                                 statBuf
.st_uid  
= geteuid(); 
 210                                 statBuf
.st_gid  
= getegid(); 
 213                                 SC_log(LOG_INFO
, "stat() failed: %s", strerror(errno
)); 
 218                 /* create the (new) preferences file */ 
 219                 pathLen 
= strlen(path
) + sizeof("-new"); 
 220                 thePath 
= CFAllocatorAllocate(NULL
, pathLen
, 0); 
 221                 snprintf(thePath
, pathLen
, "%s-new", path
); 
 223 #if     TARGET_OS_EMBEDDED 
 224                 if ((prefsPrivate
->options 
!= NULL
) && 
 225                     CFDictionaryGetValueIfPresent(prefsPrivate
->options
, 
 226                                                   kSCPreferencesOptionProtectionClass
, 
 227                                                   (const void **)&protectionClass
)) { 
 231                         if (!isA_CFString(protectionClass
) || 
 232                             (CFStringGetLength(protectionClass
) != 1) || 
 233                             ((str 
= CFStringGetCStringPtr(protectionClass
, kCFStringEncodingASCII
)) == NULL
) || 
 234                             (str
[0] < 'A') || (str
[0] > 'F') 
 236                                 _SCErrorSet(kSCStatusInvalidArgument
); 
 240                         pc 
= str
[0] - 'A' + 1;  // PROTECTION_CLASS_[ABCDEF] 
 241                         fd 
= open_dprotected_np(thePath
, O_WRONLY
|O_CREAT
, pc
, 0, statBuf
.st_mode
); 
 243 #endif  // TARGET_OS_EMBEDDED 
 244                 fd 
= open(thePath
, O_WRONLY
|O_CREAT
, statBuf
.st_mode
); 
 248                         SC_log(LOG_INFO
, "open() failed: %s", strerror(errno
)); 
 249                         CFAllocatorDeallocate(NULL
, thePath
); 
 253                 /* preserve permissions */ 
 254                 (void) fchown(fd
, statBuf
.st_uid
, statBuf
.st_gid
); 
 255                 (void) fchmod(fd
, statBuf
.st_mode
); 
 257                 /* write the new preferences */ 
 258                 newPrefs 
= CFPropertyListCreateData(NULL
, 
 261                                                     kCFPropertyListBinaryFormat_v1_0
, 
 262 #else   // TARGET_OS_IPHONE 
 263                                                     kCFPropertyListXMLFormat_v1_0
, 
 264 #endif  // TARGET_OS_IPHONE 
 268                         _SCErrorSet(kSCStatusFailed
); 
 269                         SC_log(LOG_INFO
, "CFPropertyListCreateData() failed"); 
 270                         SC_log(LOG_INFO
, "  prefs = %s", path
); 
 271                         CFAllocatorDeallocate(NULL
, thePath
); 
 275                 if (writen(fd
, (const void *)CFDataGetBytePtr(newPrefs
), CFDataGetLength(newPrefs
)) == -1) { 
 277                         SC_log(LOG_INFO
, "writen() failed: %s", strerror(errno
)); 
 278                         SC_log(LOG_INFO
, "  path = %s", thePath
); 
 279                         (void) unlink(thePath
); 
 280                         CFAllocatorDeallocate(NULL
, thePath
); 
 286                 /* new preferences have been written */ 
 287                 if (close(fd
) == -1) { 
 289                         SC_log(LOG_INFO
, "close() failed: %s", strerror(errno
)); 
 290                         SC_log(LOG_INFO
, "  path = %s", thePath
); 
 291                         (void) unlink(thePath
); 
 292                         CFAllocatorDeallocate(NULL
, thePath
); 
 298                 /* rename new->old */ 
 299                 if (rename(thePath
, path
) == -1) { 
 301                         SC_log(LOG_INFO
, "rename() failed: %s", strerror(errno
)); 
 302                         SC_log(LOG_INFO
, "  path = %s --> %s", thePath
, path
); 
 303                         CFAllocatorDeallocate(NULL
, thePath
); 
 306                 CFAllocatorDeallocate(NULL
, thePath
); 
 308                 if (prefsPrivate
->newPath
) { 
 309                         /* prefs file saved in "new" directory */ 
 310                         (void) unlink(prefsPrivate
->path
); 
 311                         (void) symlink(prefsPrivate
->newPath
, prefsPrivate
->path
); 
 312                         CFAllocatorDeallocate(NULL
, prefsPrivate
->path
); 
 313                         prefsPrivate
->path 
= path
; 
 314                         prefsPrivate
->newPath 
= NULL
; 
 317                 /* grab the new signature */ 
 318                 if (stat(path
, &statBuf
) == -1) { 
 320                         SC_log(LOG_INFO
, "stat() failed: %s", strerror(errno
)); 
 321                         SC_log(LOG_INFO
, "  path = %s", thePath
); 
 325                 /* remove the empty .plist */ 
 328                 /* init the new signature */ 
 329                 bzero(&statBuf
, sizeof(statBuf
)); 
 332         /* update signature */ 
 333         if (prefsPrivate
->signature 
!= NULL
) CFRelease(prefsPrivate
->signature
); 
 334         prefsPrivate
->signature 
= __SCPSignatureFromStatbuf(&statBuf
); 
 338         SC_log(LOG_INFO
, "SCPreferences() commit: %s", 
 339                prefsPrivate
->newPath 
? prefsPrivate
->newPath 
: prefsPrivate
->path
); 
 341         /* post notification */ 
 342         ok 
= SCDynamicStoreNotifyValue(NULL
, prefsPrivate
->sessionKeyCommit
); 
 344                 SC_log(LOG_INFO
, "SCDynamicStoreNotifyValue() failed"); 
 345                 _SCErrorSet(kSCStatusFailed
); 
 349         prefsPrivate
->changed 
= FALSE
; 
 356                 status 
= SCError();     // preserve status across unlock 
 357                 (void) SCPreferencesUnlock(prefs
);