]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/simpleprefs.cpp
Security-58286.200.222.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / simpleprefs.cpp
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...) secinfo("simpleprefs", ## args)
43
44 #define kSecUserPrefsDir "Library/Preferences" /* relative to $HOME */
45 #define kSecSystemPrefsDir "/Library/Preferences"
46
47 #pragma mark ----- (immutable) Dictionary -----
48
49 static 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
64 static 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
74 Dictionary* 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
86 Dictionary* 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
104 Dictionary::Dictionary() : mDict(NULL)
105 {
106 }
107
108 Dictionary::Dictionary(
109 const char *path)
110 : mDict(NULL)
111 {
112 initFromFile(path);
113 }
114
115 Dictionary::Dictionary(
116 CFDictionaryRef dict)
117 : mDict(dict)
118 {
119 if (mDict)
120 CFRetain(mDict);
121 }
122
123 Dictionary::~Dictionary()
124 {
125 if(mDict) {
126 CFRelease(mDict);
127 }
128 }
129
130 /* basic lookup */
131 const void *Dictionary::getValue(
132 CFStringRef key)
133 {
134 return CFDictionaryGetValue(dict(), key);
135 }
136
137 /* lookup, value must be CFString (we check) */
138 CFStringRef 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) */
152 CFDataRef 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) */
166 CFDictionaryRef 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 */
183 Dictionary *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 */
202 bool 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
231 CFIndex Dictionary::count()
232 {
233 return CFDictionaryGetCount(dict());
234 }
235
236 void 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 */
246 void 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
307 MutableDictionary* 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
321 MutableDictionary* 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 */
335 MutableDictionary::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
344 MutableDictionary::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 */
360 MutableDictionary::MutableDictionary(
361 CFDictionaryRef dict,
362 bool isMutable)
363 : Dictionary(dict)
364 {
365 if(!isMutable) {
366 makeMutable();
367 }
368 }
369
370 MutableDictionary::~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 */
381 CFMutableDictionaryRef 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 */
401 MutableDictionary *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 */
415 void 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 */
425 void 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 */
435 void MutableDictionary::removeValue(
436 CFStringRef key)
437 {
438 CFDictionaryRemoveValue(mutableDict(), key);
439 }
440
441 /* write as XML property list, both return true on success */
442 bool 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 }
461 errOut:
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 */
472 bool 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 */
486 void 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