]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPCommit.c
configd-1061.141.1.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCPCommit.c
1 /*
2 * Copyright (c) 2000-2008, 2010-2013, 2015-2020 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 * Modification History
26 *
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
29 *
30 * November 9, 2000 Allan Nathanson <ajn@apple.com>
31 * - initial revision
32 */
33
34 #include <TargetConditionals.h>
35 #include "SCPreferencesInternal.h"
36 #include "SCHelper_client.h"
37
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <sys/errno.h>
41
42 static Boolean
43 __SCPreferencesCommitChanges_helper(SCPreferencesRef prefs)
44 {
45 CFDataRef data = NULL;
46 Boolean ok;
47 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
48 uint32_t status = kSCStatusOK;
49 CFDataRef reply = NULL;
50
51 if (prefsPrivate->helper_port == MACH_PORT_NULL) {
52 // if no helper
53 status = kSCStatusAccessError;
54 goto fail;
55 }
56
57 if (prefsPrivate->changed) {
58 ok = _SCSerialize(prefsPrivate->prefs, &data, NULL, NULL);
59 if (!ok) {
60 status = kSCStatusFailed;
61 if (_sc_verbose) {
62 SC_log(LOG_NOTICE, "_SCSerialize() failed");
63 SC_log(LOG_NOTICE, " prefs = %s",
64 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path);
65 }
66 goto error;
67 }
68 }
69
70 // have the helper "commit" the prefs
71 // status = kSCStatusOK;
72 // reply = NULL;
73 ok = _SCHelperExec(prefsPrivate->helper_port,
74 SCHELPER_MSG_PREFS_COMMIT,
75 data,
76 &status,
77 &reply);
78 if (data != NULL) CFRelease(data);
79 if (!ok) {
80 goto fail;
81 }
82
83 if (status != kSCStatusOK) {
84 goto error;
85 }
86
87 if (prefsPrivate->changed) {
88 if (prefsPrivate->signature != NULL) CFRelease(prefsPrivate->signature);
89 prefsPrivate->signature = reply;
90 } else {
91 if (reply != NULL) CFRelease(reply);
92 }
93
94 prefsPrivate->changed = FALSE;
95 return TRUE;
96
97 fail :
98
99 // close helper
100 if (prefsPrivate->helper_port != MACH_PORT_NULL) {
101 _SCHelperClose(&prefsPrivate->helper_port);
102 }
103
104 error :
105
106 // return error
107 if (reply != NULL) CFRelease(reply);
108 _SCErrorSet(status);
109 return FALSE;
110 }
111
112
113 static ssize_t
114 writen(int ref, const void *data, size_t len)
115 {
116 size_t left = len;
117 ssize_t n;
118 const void *p = data;
119
120 while (left > 0) {
121 if ((n = write(ref, p, left)) == -1) {
122 if (errno != EINTR) {
123 return -1;
124 }
125 n = 0;
126 }
127 left -= n;
128 p += n;
129 }
130 return len;
131 }
132
133
134 Boolean
135 SCPreferencesCommitChanges(SCPreferencesRef prefs)
136 {
137 Boolean ok = FALSE;
138 char * path;
139 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
140 Boolean save = TRUE;
141 struct stat statBuf;
142 Boolean wasLocked;
143
144 if (prefs == NULL) {
145 /* sorry, you must provide a session */
146 _SCErrorSet(kSCStatusNoPrefsSession);
147 return FALSE;
148 }
149
150 /*
151 * Determine if the we have exclusive access to the preferences
152 * and acquire the lock if necessary.
153 */
154 wasLocked = prefsPrivate->locked;
155 if (!wasLocked) {
156 if (!SCPreferencesLock(prefs, TRUE)) {
157 SC_log(LOG_INFO, "SCPreferencesLock() failed");
158 return FALSE;
159 }
160 }
161
162 if (prefsPrivate->authorizationData != NULL) {
163 ok = __SCPreferencesCommitChanges_helper(prefs);
164 if (ok) {
165 prefsPrivate->changed = FALSE;
166 }
167 goto done;
168 }
169
170 /*
171 * if necessary, apply changes
172 */
173 if (!prefsPrivate->changed) {
174 goto committed;
175 }
176
177 /*
178 * check if the preferences should be removed
179 */
180 if (__SCPreferencesIsEmpty(prefs)) {
181 CFBooleanRef val;
182
183 /* if empty */
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 */
191 save = FALSE;
192 }
193 }
194
195 path = prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path;
196 if (save) {
197 int fd;
198 CFDataRef newPrefs;
199 CFIndex pathLen;
200 CFStringRef protectionClass;
201 char * thePath;
202
203 if (stat(prefsPrivate->path, &statBuf) == -1) {
204 if (errno == ENOENT) {
205 memset(&statBuf, 0, sizeof(statBuf));
206 statBuf.st_mode = 0644;
207 statBuf.st_uid = geteuid();
208 statBuf.st_gid = getegid();
209 } else {
210 _SCErrorSet(errno);
211 SC_log(LOG_INFO, "stat() failed: %s", strerror(errno));
212 goto done;
213 }
214 }
215
216 /* create the (new) preferences file */
217 pathLen = strlen(path) + sizeof("-new");
218 thePath = CFAllocatorAllocate(NULL, pathLen, 0);
219 snprintf(thePath, pathLen, "%s-new", path);
220
221 if ((prefsPrivate->options != NULL) &&
222 CFDictionaryGetValueIfPresent(prefsPrivate->options,
223 kSCPreferencesOptionProtectionClass,
224 (const void **)&protectionClass)) {
225 int pc;
226 const char *str;
227
228 if (!isA_CFString(protectionClass) ||
229 (CFStringGetLength(protectionClass) != 1) ||
230 ((str = CFStringGetCStringPtr(protectionClass, kCFStringEncodingASCII)) == NULL) ||
231 (str[0] < 'A') || (str[0] > 'F')
232 ) {
233 _SCErrorSet(kSCStatusInvalidArgument);
234 goto done;
235 }
236
237 pc = str[0] - 'A' + 1; // PROTECTION_CLASS_[ABCDEF]
238 fd = open_dprotected_np(thePath, O_WRONLY|O_CREAT, pc, 0, statBuf.st_mode);
239 } else {
240 fd = open(thePath, O_WRONLY|O_CREAT, statBuf.st_mode);
241 }
242
243 if (fd == -1) {
244 _SCErrorSet(errno);
245 SC_log(LOG_NOTICE, "open() failed: %s", strerror(errno));
246 CFAllocatorDeallocate(NULL, thePath);
247 goto done;
248 }
249
250 /* preserve permissions */
251 (void) fchown(fd, statBuf.st_uid, statBuf.st_gid);
252 (void) fchmod(fd, statBuf.st_mode);
253
254 /* write the new preferences */
255 newPrefs = CFPropertyListCreateData(NULL,
256 prefsPrivate->prefs,
257 #if TARGET_OS_IPHONE
258 kCFPropertyListBinaryFormat_v1_0,
259 #else // TARGET_OS_IPHONE
260 kCFPropertyListXMLFormat_v1_0,
261 #endif // TARGET_OS_IPHONE
262 0,
263 NULL);
264 if (!newPrefs) {
265 _SCErrorSet(kSCStatusFailed);
266 SC_log(LOG_INFO, "CFPropertyListCreateData() failed");
267 SC_log(LOG_INFO, " prefs = %s", path);
268 CFAllocatorDeallocate(NULL, thePath);
269 (void) close(fd);
270 goto done;
271 }
272 if (writen(fd, (const void *)CFDataGetBytePtr(newPrefs), CFDataGetLength(newPrefs)) == -1) {
273 _SCErrorSet(errno);
274 SC_log(LOG_INFO, "writen() failed: %s", strerror(errno));
275 SC_log(LOG_INFO, " path = %s", thePath);
276 (void) unlink(thePath);
277 CFAllocatorDeallocate(NULL, thePath);
278 (void) close(fd);
279 CFRelease(newPrefs);
280 goto done;
281 }
282
283 /* new preferences have been written */
284 if (close(fd) == -1) {
285 _SCErrorSet(errno);
286 SC_log(LOG_INFO, "close() failed: %s", strerror(errno));
287 SC_log(LOG_INFO, " path = %s", thePath);
288 (void) unlink(thePath);
289 CFAllocatorDeallocate(NULL, thePath);
290 CFRelease(newPrefs);
291 goto done;
292 }
293 CFRelease(newPrefs);
294
295 /* rename new->old */
296 if (rename(thePath, path) == -1) {
297 _SCErrorSet(errno);
298 SC_log(LOG_INFO, "rename() failed: %s", strerror(errno));
299 SC_log(LOG_INFO, " path = %s --> %s", thePath, path);
300 CFAllocatorDeallocate(NULL, thePath);
301 goto done;
302 }
303 CFAllocatorDeallocate(NULL, thePath);
304
305 if (prefsPrivate->newPath) {
306 /* prefs file saved in "new" directory */
307 (void) unlink(prefsPrivate->path);
308 (void) symlink(prefsPrivate->newPath, prefsPrivate->path);
309 CFAllocatorDeallocate(NULL, prefsPrivate->path);
310 prefsPrivate->path = path;
311 prefsPrivate->newPath = NULL;
312 }
313
314 /* grab the new signature */
315 if (stat(path, &statBuf) == -1) {
316 _SCErrorSet(errno);
317 SC_log(LOG_INFO, "stat() failed: %s", strerror(errno));
318 SC_log(LOG_INFO, " path = %s", thePath);
319 goto done;
320 }
321 } else {
322 /* remove the empty .plist */
323 unlink(path);
324
325 /* init the new signature */
326 memset(&statBuf, 0, sizeof(statBuf));
327 }
328
329 /* update signature */
330 if (prefsPrivate->signature != NULL) CFRelease(prefsPrivate->signature);
331 prefsPrivate->signature = __SCPSignatureFromStatbuf(&statBuf);
332
333 committed :
334
335 if (prefsPrivate->changed) {
336 SC_log(LOG_INFO, "SCPreferences() commit: %s, size=%lld",
337 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path,
338 __SCPreferencesPrefsSize(prefs));
339 } else {
340 SC_log(LOG_INFO, "SCPreferences() commit: %s, w/no changes",
341 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path);
342 }
343
344 /* post notification */
345 ok = SCDynamicStoreNotifyValue(NULL, prefsPrivate->sessionKeyCommit);
346 if (!ok) {
347 SC_log(LOG_INFO, "SCDynamicStoreNotifyValue() failed");
348 _SCErrorSet(kSCStatusFailed);
349 goto done;
350 }
351
352 prefsPrivate->changed = FALSE;
353
354 done :
355
356 if (!wasLocked) {
357 uint32_t status;
358
359 status = SCError(); // preserve status across unlock
360 (void) SCPreferencesUnlock(prefs);
361 _SCErrorSet(status);
362 }
363 return ok;
364 }