1/* 2 * Copyright (c) 2002-2004,2011,2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * simpleprefs.cpp - plist support for a bare bones Preferences implementation, 26 * using only Darwin-avaialble CoreFoundation classes. 27 */ 28 29#include "simpleprefs.h" 30#include "errors.h" 31#include <sys/param.h> 32#include <stdlib.h> 33#include <assert.h> 34#include <stdexcept> 35#include <security_utilities/debugging.h> 36#include <CoreFoundation/CFData.h> 37#include <CoreFoundation/CFNumber.h> 38#include <CoreFoundation/CFURLAccess.h> 39#include <CoreFoundation/CFPropertyList.h> 40#include <sys/stat.h> 41 42#define prefsDebug(args...) secdebug("simpleprefs", ## args) 43 44#define kSecUserPrefsDir "Library/Preferences" /* relative to $HOME */ 45#define kSecSystemPrefsDir "/Library/Preferences" 46 47#pragma mark ----- (immutable) Dictionary ----- 48 49static void pathForDomain(const char *domain, Dictionary::UserOrSystem userSys, std::string &path) 50{ 51 path.clear(); 52 if(userSys == Dictionary::US_User) { 53 const char *home = getenv("HOME"); 54 if(home == NULL) { 55 home = ""; 56 } 57 path = std::string(home) + "/" + kSecUserPrefsDir + "/" + domain + ".plist"; 58 } 59 else { 60 path = std::string(kSecSystemPrefsDir) + "/" + domain + ".plist"; 61 } 62} 63 64static bool FileExists(const char* s) 65{ 66 // this isn't very efficient, either, but orders are to get rid of exceptions... 67 struct stat st; 68 int result = stat(s, &st); 69 return result == 0; 70} 71 72// use factory functions to create the dictionaries so that we can test for the presence of the dictionaries 73// without throwing 74Dictionary* Dictionary::CreateDictionary(const char* path) 75{ 76 if (!FileExists(path)) 77 { 78 return NULL; 79 } 80 else 81 { 82 return new Dictionary(path); 83 } 84} 85 86Dictionary* Dictionary::CreateDictionary(const char* domain, UserOrSystem userSys, bool loose) 87{ 88 std::string path; 89 pathForDomain(domain, userSys, path); 90 bool exists = FileExists(path.c_str()); 91 if (!loose && !exists) 92 { 93 return NULL; 94 } 95 96 if (!exists) 97 { 98 return new Dictionary(); 99 } 100 101 return new Dictionary(path.c_str()); 102} 103 104Dictionary::Dictionary() : mDict(NULL) 105{ 106} 107 108Dictionary::Dictionary( 109 const char *path) 110 : mDict(NULL) 111{ 112 initFromFile(path); 113} 114 115Dictionary::Dictionary( 116 CFDictionaryRef dict) 117 : mDict(dict) 118{ 119 if (mDict) 120 CFRetain(mDict); 121} 122 123Dictionary::~Dictionary() 124{ 125 if(mDict) { 126 CFRelease(mDict); 127 } 128} 129 130/* basic lookup */ 131const void *Dictionary::getValue( 132 CFStringRef key) 133{ 134 return CFDictionaryGetValue(dict(), key); 135} 136 137/* lookup, value must be CFString (we check) */ 138CFStringRef Dictionary::getStringValue( 139 CFStringRef key) 140{ 141 CFStringRef val = (CFStringRef)CFDictionaryGetValue(dict(), key); 142 if(val == NULL) { 143 return NULL; 144 } 145 if(CFGetTypeID(val) != CFStringGetTypeID()) { 146 return NULL; 147 } 148 return val; 149} 150 151/* lookup, value must be CFData (we check) */ 152CFDataRef Dictionary::getDataValue( 153 CFStringRef key) 154{ 155 CFDataRef val = (CFDataRef)CFDictionaryGetValue(dict(), key); 156 if(val == NULL) { 157 return NULL; 158 } 159 if(CFGetTypeID(val) != CFDataGetTypeID()) { 160 return NULL; 161 } 162 return val; 163} 164 165/* lookup, value must be CFDictionary (we check) */ 166CFDictionaryRef Dictionary::getDictValue( 167 CFStringRef key) 168{ 169 CFDictionaryRef val = (CFDictionaryRef)CFDictionaryGetValue(dict(), key); 170 if(val == NULL) { 171 return NULL; 172 } 173 if(CFGetTypeID(val) != CFDictionaryGetTypeID()) { 174 return NULL; 175 } 176 return val; 177} 178 179/* 180 * Lookup, value is a dictionary, we return value as Dictionary 181 * if found, else return NULL. 182 */ 183Dictionary *Dictionary::copyDictValue( 184 CFStringRef key) 185{ 186 CFDictionaryRef cfDict = getDictValue(key); 187 if(cfDict == NULL) { 188 return NULL; 189 } 190 Dictionary *rtnDict = new Dictionary(cfDict); 191 /* 192 * mDict has one ref count 193 * cfDict has one ref count 194 */ 195 return rtnDict; 196} 197 198/* 199 * boolean lookup, tolerate many different forms of value. 200 * Default if value not present is false. 201 */ 202bool Dictionary::getBoolValue( 203 CFStringRef key) 204{ 205 CFTypeRef val = CFDictionaryGetValue(dict(), key); 206 if(val == NULL) { 207 return false; 208 } 209 CFComparisonResult res; 210 if(CFGetTypeID(val) == CFStringGetTypeID()) { 211 res = CFStringCompare((CFStringRef)val, CFSTR("YES"), 212 kCFCompareCaseInsensitive); 213 if(res == kCFCompareEqualTo) { 214 return true; 215 } 216 else { 217 return false; 218 } 219 } 220 if(CFGetTypeID(val) == CFBooleanGetTypeID()) { 221 return CFBooleanGetValue((CFBooleanRef)val) ? true : false; 222 } 223 if(CFGetTypeID(val) == CFNumberGetTypeID()) { 224 char cval = 0; 225 CFNumberGetValue((CFNumberRef)val, kCFNumberCharType, &cval); 226 return (cval == 0) ? false : true; 227 } 228 return false; 229} 230 231CFIndex Dictionary::count() 232{ 233 return CFDictionaryGetCount(dict()); 234} 235 236void Dictionary::setDict( 237 CFDictionaryRef newDict) 238{ 239 if(mDict != NULL) 240 CFRelease(mDict); 241 mDict = newDict; 242 CFRetain(mDict); 243} 244 245/* fundamental routine to init from a plist file; throws a UnixError on error */ 246void Dictionary::initFromFile( 247 const char *path, 248 bool loose /* = false */) 249{ 250 if(mDict != NULL) { 251 CFRelease(mDict); 252 mDict = NULL; 253 } 254 CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)path, 255 strlen(path), false); 256 if(url == NULL) { 257 UnixError::throwMe(EIO); 258 } 259 260 CFDataRef fileData = NULL; 261 CFPropertyListRef propList = NULL; 262 CFStringRef errorString = NULL; 263 SInt32 errorCode; 264 265 Boolean success = CFURLCreateDataAndPropertiesFromResource( 266 NULL, 267 url, 268 &fileData, 269 NULL, // properties 270 NULL, // desiredProperties 271 &errorCode); 272 CFRelease(url); 273 if(success) { 274 propList = CFPropertyListCreateFromXMLData( 275 NULL, 276 fileData, 277 kCFPropertyListImmutable, 278 &errorString); 279 if(propList != NULL) { 280 /* 281 * Note don't use setDict() here to avoid the extra 282 * refcount that would entail. We own the dictionary now. 283 */ 284 mDict = (CFDictionaryRef)propList; 285 } 286 else { 287 success = false; 288 } 289 } 290 if(fileData != NULL) { 291 CFRelease(fileData); 292 } 293 if(errorString != NULL) { 294 CFRelease(errorString); 295 } 296 if(!success) { 297 if (loose) 298 return; 299 else 300 UnixError::throwMe(EIO); 301 } 302} 303 304#pragma mark ----- Mutable Dictionary ----- 305 306// factory functions 307MutableDictionary* MutableDictionary::CreateMutableDictionary(const char* fileName) 308{ 309 std::string path; 310 311 if (!FileExists(path.c_str())) 312 { 313 return NULL; 314 } 315 else 316 { 317 return new MutableDictionary(path.c_str()); 318 } 319} 320 321MutableDictionary* MutableDictionary::CreateMutableDictionary(const char *domain, UserOrSystem userSys) 322{ 323 std::string path; 324 pathForDomain(domain, userSys, path); 325 326 if (!FileExists(path.c_str())) 327 { 328 return NULL; 329 } 330 331 return new MutableDictionary(path.c_str()); 332} 333 334/* Create an empty mutable dictionary */ 335MutableDictionary::MutableDictionary() 336 : Dictionary((CFDictionaryRef)CFDictionaryCreateMutable(NULL, 0, 337 &kCFCopyStringDictionaryKeyCallBacks, 338 &kCFTypeDictionaryValueCallBacks)) 339{ 340 /* lose one of those two retain counts.... */ 341 CFRelease(mDict); 342} 343 344MutableDictionary::MutableDictionary( 345 const char *filename) 346 : Dictionary(filename) 347{ 348 /* 349 * Dictionary's contructor read the plist from disk. Now 350 * replace that dictionary with a mutable copy. 351 */ 352 makeMutable(); 353} 354 355/* 356 * Create from existing CFDictionary (OR CFMutableDictionary). 357 * I don't see any way the CF runtime will let us differentiate an 358 * immutable from a mutable dictionary here, so caller has to tell us. 359 */ 360MutableDictionary::MutableDictionary( 361 CFDictionaryRef dict, 362 bool isMutable) 363 : Dictionary(dict) 364{ 365 if(!isMutable) { 366 makeMutable(); 367 } 368} 369 370MutableDictionary::~MutableDictionary() 371{ 372 /* nothing for now */ 373} 374 375/* 376 * Lookup, value must be CFDictionary (we check). We return a 377 * mutable copy, or if key not found, we return a new mutable dictionary 378 * with a ref count of one. 379 * If you want a NULL return if it's not there, use getDictValue(). 380 */ 381CFMutableDictionaryRef MutableDictionary::getMutableDictValue( 382 CFStringRef key) 383{ 384 CFDictionaryRef dict = getDictValue(key); 385 if(dict == NULL) { 386 prefsDebug("getMutableDictValue returning new empty dict; this %p", this); 387 return CFDictionaryCreateMutable(NULL, 0, 388 &kCFCopyStringDictionaryKeyCallBacks, 389 &kCFTypeDictionaryValueCallBacks); 390 } 391 else { 392 prefsDebug("getMutableDictValue returning copy; this %p", this); 393 return CFDictionaryCreateMutableCopy(NULL, 0, dict); 394 } 395} 396 397/* 398 * Lookup, value is a dictionary, we return a MutableDictionary, even if 399 * no value found. 400 */ 401MutableDictionary *MutableDictionary::copyMutableDictValue( 402 CFStringRef key) 403{ 404 CFMutableDictionaryRef cfDict = getMutableDictValue(key); 405 assert(CFGetRetainCount(cfDict) == 1); 406 MutableDictionary *rtnDict = new MutableDictionary(cfDict, true); 407 CFRelease(cfDict); 408 /* rtnDict->mDict now holds the only ref count */ 409 return rtnDict; 410} 411 412/* 413 * Basic setter. Does a replace if present, add if not present op. 414 */ 415void MutableDictionary::setValue( 416 CFStringRef key, 417 CFTypeRef val) 418{ 419 CFDictionarySetValue(mutableDict(), key, val); 420} 421 422/* 423 * Set key/value pair, data as CFData in the dictionary but passed to us as CSSM_DATA. 424 */ 425void MutableDictionary::setDataValue( 426 CFStringRef key, 427 const void *valData, CFIndex valLength) 428{ 429 CFDataRef cfVal = CFDataCreate(NULL, reinterpret_cast<const UInt8 *>(valData), valLength); 430 setValue(key, cfVal); 431 CFRelease(cfVal); 432} 433 434/* remove key/value, if present; not an error if it's not */ 435void MutableDictionary::removeValue( 436 CFStringRef key) 437{ 438 CFDictionaryRemoveValue(mutableDict(), key); 439} 440 441/* write as XML property list, both return true on success */ 442bool MutableDictionary::writePlistToFile( 443 const char *path) 444{ 445 assert(mDict != NULL); 446 CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)path, 447 strlen(path), false); 448 if(url == NULL) { 449 UnixError::throwMe(EIO); 450 } 451 452 CFDataRef xmlData = CFPropertyListCreateXMLData(NULL, dict()); 453 bool ourRtn = false; 454 SInt32 errorCode; 455 if(xmlData == NULL) { 456 goto errOut; 457 } 458 if(CFURLWriteDataAndPropertiesToResource(url, xmlData, NULL, &errorCode)) { 459 ourRtn = true; 460 } 461errOut: 462 if(url) { 463 CFRelease(url); 464 } 465 if(xmlData) { 466 CFRelease(xmlData); 467 } 468 return ourRtn; 469} 470 471/* write XML property list to preferences file */ 472bool MutableDictionary::writePlistToPrefs( 473 const char *domain, // e.g., com.apple.security 474 UserOrSystem userSys) // US_User : ~/Library/Preferences/domain.plist 475 // US_System: /Library/Preferences/domain.plist 476{ 477 std::string path; 478 pathForDomain(domain, userSys, path); 479 return writePlistToFile(path.c_str()); 480} 481 482/* 483 * Called after Dictionary reads plist from file, resulting in an immutable 484 * mDict. We replace that with a mutable copy. 485 */ 486void MutableDictionary::makeMutable() 487{ 488 CFMutableDictionaryRef mutDict = CFDictionaryCreateMutableCopy(NULL, 0, dict()); 489 if(mutDict == NULL) { 490 throw std::bad_alloc(); 491 } 492 setDict(mutDict); 493 /* we own the dictionary now */ 494 CFRelease(mutDict); 495} 496 497