1/*
2 * Copyright (c) 2004-2011 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 * trustSettings.cpp - routines called by MIG for Trust Settings r/w
26 *
27 * Created 9 May 2006 by dmitch
28 */
29
30#include <security_ocspd/ocspd.h>	/* created by MIG */
31#include <Security/Authorization.h>
32#include <Security/AuthorizationDB.h>
33#include <security_utilities/threading.h>
34#include <security_utilities/globalizer.h>
35#include <security_cdsa_utils/cuFileIo.h>
36#include <sys/param.h>
37#include <sys/stat.h>
38#include <bsm/libbsm.h>				/* for audit_token_to_au32() */
39#include <security_ocspd/ocspdDebug.h>
40#include <Security/SecBase.h>
41#include <Security/SecTrustSettings.h>
42#include <Security/TrustSettingsSchema.h>
43#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
44#include "ocspdServer.h"
45#include <membership.h>				/* for mbr_uid_to_uuid() */
46
47/* One lock for all accesses to TrustSettings files */
48static ModuleNexus<Mutex> gTrustSettingsLock;
49
50/*
51 * The auth rights we use.
52 * This probably will get tweaked: what I'd like to have, I think, for
53 * the admin settings is "OK if root, else authenticate as admin". For
54 * now just require root.
55 */
56#define TRUST_SETTINGS_RIGHT_USER	"com.apple.trust-settings.user"
57#define TRUST_SETTINGS_RIGHT_ADMIN	"com.apple.trust-settings.admin"
58
59#define TRUST_SETTINGS_RULE_USER	CFSTR("authenticate-session-owner");
60#define TRUST_SETTINGS_RULE_ADMIN	CFSTR("is-root");
61
62/*
63 * Everyone can look in the settings directory, and hence stat the admin
64 * file (as a quick optimization to avoid an RPC to us if it's not there),
65 * but the individual files are readable only by root.
66 */
67#define TRUST_SETTINGS_PATH_MODE	0755
68#define TRUST_SETTINGS_FILES_MODE	0600
69
70/*
71 * Create a stringified UUID. Caller allocs result, length UUID_STR_LEN + 1.
72 * String has two characters for each UUID byte, plus 4 '-' chars, plus NULL.
73 */
74#define UUID_STR_LEN	((2 * sizeof(uuid_t)) + 5)
75
76static void uuidString(
77	uuid_t uuid,
78	char *cp)
79{
80	unsigned dex;
81	unsigned char *uuidCp = (unsigned char *)uuid;
82
83	for(dex=0; dex<sizeof(uuid_t); dex++) {
84		sprintf(cp, "%02X", *uuidCp++);
85		cp += 2;
86		switch(dex) {
87			case 3:
88			case 5:
89			case 7:
90			case 9:
91				*cp++ = '-';
92				break;
93			default:
94				break;
95		}
96	}
97	*cp = '\0';
98}
99
100/*
101 * Given the audit_token from an incoming message, cook up the path to the
102 * appropriate settings file.
103 *
104 * We use UUID here to form the file name, not uid, since uids can be reused.
105 * (User Joe, uid 600, has some settings, then quits to company. User Mary joins,
106 * is assigned to the currently unused uid 600. User Mary unwittingly inherits
107 * Joe's Trust Settings. Bad.)
108 *
109 * I have no idea under which circumstances the mbr_uid_to_uuid() function could
110 * fail, but we fall back to uid if it does.
111 *
112 * The UUID-based filename is of the same form as UUID are generally
113 * presented: FA36D0A2-D91A-4E36-8156-1A22E8982652.plist,
114 */
115static void trustSettingsPath(
116	audit_token_t auditToken,
117	SecTrustSettingsDomain domain,
118	char *path)				/* caller allocated, (MAXPATHLEN + 1) bytes */
119{
120	switch(domain) {
121		case kSecTrustSettingsDomainUser:
122		{
123			uid_t euid;
124			uuid_t uuid;
125
126			/* uid from the incoming message's audit_token */
127			audit_token_to_au32(auditToken, NULL, &euid, NULL, NULL, NULL, NULL, NULL, NULL);
128			/* convert that to uuid */
129			if(!mbr_uid_to_uuid(euid, uuid)) {
130				char uuidStr[UUID_STR_LEN];
131				uuidString(uuid, uuidStr);
132				snprintf(path, MAXPATHLEN + 1, "%s/%s.plist", TRUST_SETTINGS_PATH, uuidStr);
133			}
134			else {
135				/* can't get UUID - use the uid */
136				snprintf(path, MAXPATHLEN + 1, "%s/%lu.plist", TRUST_SETTINGS_PATH,
137					(unsigned long)euid);
138			}
139			break;
140		}
141		case kSecTrustSettingsDomainAdmin:
142			snprintf(path, MAXPATHLEN + 1, "%s/%s", TRUST_SETTINGS_PATH, ADMIN_TRUST_SETTINGS);
143			break;
144		case kSecTrustSettingsDomainSystem:
145			/*
146			 * The client really should just read this file themselves since it's
147			 * immutable. But, we'll do it if asked.
148			 */
149			strcpy(path, SYSTEM_TRUST_SETTINGS_PATH);
150			break;
151		default:
152			break;
153	}
154}
155
156static mach_port_t	gBootstrapPort=MACH_PORT_NULL;
157static mach_port_t	gAuditSessionPort=MACH_PORT_NULL;
158
159void switchToContext(mach_port_t clientBootstrapPort, audit_token_t auditToken)
160{
161	au_asid_t 		asid, tmp_asid;
162	mach_port_t		clientAuditSessionPort=MACH_PORT_NULL;
163	kern_return_t	kr;
164
165	/* get the audit session identifier and port from the audit_token */
166	audit_token_to_au32(auditToken, NULL, NULL, NULL, NULL, NULL, NULL, &asid, NULL);
167	audit_session_port(asid, &clientAuditSessionPort);
168
169	if (gBootstrapPort == MACH_PORT_NULL) {
170		/* save our own bootstrap port the first time through (to restore later) */
171		task_get_bootstrap_port(mach_task_self(), &gBootstrapPort);
172	}
173	kr = task_set_bootstrap_port(mach_task_self(), clientBootstrapPort);
174	if (kr != KERN_SUCCESS) {
175		ocspdErrorLog("Unable to set client bootstrap port\n");
176	}
177	if (gAuditSessionPort == MACH_PORT_NULL) {
178		/* save our own audit session port the first time through (to restore later) */
179		gAuditSessionPort = audit_session_self();
180		mach_port_mod_refs(mach_task_self(), gAuditSessionPort, MACH_PORT_RIGHT_SEND, +1);
181	}
182	tmp_asid = audit_session_join(clientAuditSessionPort);
183	if (tmp_asid == AU_DEFAUDITSID) {
184		ocspdErrorLog("Unable to join client security session\n");
185	}
186}
187
188void restoreContext()
189{
190	if (gBootstrapPort != MACH_PORT_NULL)
191	{
192		kern_return_t kr = task_set_bootstrap_port(mach_task_self(), gBootstrapPort);
193		if (kr != KERN_SUCCESS) {
194			ocspdErrorLog("Unable to restore server bootstrap port");
195		}
196	}
197
198	if (gAuditSessionPort != MACH_PORT_NULL)
199	{
200		au_asid_t asid = audit_session_join(gAuditSessionPort);
201		if (asid == AU_DEFAUDITSID) {
202			ocspdErrorLog("Unable to rejoin original security session");
203		}
204	}
205}
206
207
208kern_return_t ocsp_server_trustSettingsRead(
209	mach_port_t serverport,
210	audit_token_t auditToken,
211	uint32_t domain,
212	Data *trustSettings,
213	mach_msg_type_number_t *trustSettingsCnt,
214	OSStatus *rcode)
215{
216	ocspdDebug("Processing trustSettingsRead request");
217	ServerActivity();
218
219	StLock<Mutex> _(gTrustSettingsLock());
220	char path[MAXPATHLEN + 1];
221
222	trustSettingsPath(auditToken, domain, path);
223	unsigned char *fileData = NULL;
224	unsigned fileDataLen;
225	if(readFile(path, &fileData, &fileDataLen)) {
226		ocspdTrustDebug("trustSettingsRead: no file at %s", path);
227		*rcode = errSecNoTrustSettings;
228		*trustSettings = NULL;
229		*trustSettingsCnt = 0;
230		return 0;
231	}
232
233	/* realloc using our server's allocator for later free */
234	CSSM_DATA cdata;
235	Allocator &alloc = OcspdServer::active().alloc();
236	cdata.Data = (uint8 *)alloc.malloc(fileDataLen);
237	cdata.Length = fileDataLen;
238	memmove(cdata.Data, fileData, fileDataLen);
239	free(fileData);
240	passDataToCaller(cdata, trustSettings, trustSettingsCnt);
241	ocspdTrustDebug("trustSettingsRead: read %lu bytes from %s",
242			(unsigned long)cdata.Length, path);
243	*rcode = noErr;
244	return 0;
245}
246
247kern_return_t ocsp_server_trustSettingsWrite(
248	mach_port_t serverport,
249	audit_token_t auditToken,
250	mach_port_t clientport,
251	uint32_t domain,
252	Data authBlob,
253	mach_msg_type_number_t authBlobCnt,
254	Data trustSettings,
255	mach_msg_type_number_t trustSettingsCnt,
256	OSStatus *rcode)
257{
258	ocspdDebug("Processing trustSettingsWrite request");
259	ServerActivity();
260
261	const char *authRight = NULL;
262	CFStringRef authRule = NULL;
263
264	switch(domain) {
265		case kSecTrustSettingsDomainUser:
266			authRight = TRUST_SETTINGS_RIGHT_USER;
267			authRule  = TRUST_SETTINGS_RULE_USER;
268			break;
269		case kSecTrustSettingsDomainAdmin:
270			authRight = TRUST_SETTINGS_RIGHT_ADMIN;
271			authRule  = TRUST_SETTINGS_RULE_ADMIN;
272			break;
273		case kSecTrustSettingsDomainSystem:
274			/* this TrustSetting is immutable */
275			*rcode = errSecDataNotModifiable;
276			return 0;
277		default:
278			*rcode = errSecInvalidPrefsDomain;
279			return 0;
280	}
281
282	AuthorizationExternalForm extForm;
283	if(authBlobCnt > sizeof(extForm)) {
284		/* not sure how this could legitimately happen.... */
285		ocspdErrorLog("trustSettingsWrite: authBlob too big\n");
286		*rcode = paramErr;
287		return 0;
288	}
289
290	/*
291	 * Lazily create auth rights we (and we alone) use
292	 */
293	AuthorizationRef authRef;
294	OSStatus ortn;
295	/*
296	 * WARNING: Authorization APIs may cause ocspd to be re-entered!
297	 */
298	if(AuthorizationRightGet(authRight, NULL)) {
299		ortn = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
300					0, &authRef);
301		if(ortn) {
302			/* should never happen */
303			ocspdErrorLog("trustSettingsWrite: AuthorizationCreate failure\n");
304			*rcode = internalComponentErr;
305			return 0;
306		}
307		ortn = AuthorizationRightSet(authRef, authRight, authRule,
308			NULL, NULL, NULL);
309		if(ortn) {
310			ocspdErrorLog("trustSettingsWrite: AuthorizationRightSet failure\n");
311			*rcode = internalComponentErr;
312			return 0;
313		}
314		AuthorizationFree(authRef, 0);
315	}
316
317	/*
318 	 * Cook up an auth object from the client's blob
319	 */
320	memmove(&extForm, authBlob, authBlobCnt);
321	ortn = AuthorizationCreateFromExternalForm(&extForm, &authRef);
322	if(ortn) {
323		ocspdErrorLog("trustSettingsWrite: AuthorizationCreateFromExternalForm failure\n");
324		*rcode = paramErr;
325		return 0;
326	}
327
328	/* now, see if we're authorized to do this thing */
329	AuthorizationItem authItem     = {authRight, 0, NULL, 0};
330	AuthorizationRights authRights = { 1, &authItem };
331	AuthorizationFlags authFlags   = kAuthorizationFlagInteractionAllowed |
332								     kAuthorizationFlagExtendRights;
333	/* save and restore context around call which can put up UI */
334	switchToContext(clientport, auditToken);
335	ortn = AuthorizationCopyRights(authRef, &authRights, NULL,
336		authFlags, NULL);
337	restoreContext();
338	if(ortn) {
339		ocspdErrorLog("trustSettingsWrite: AuthorizationCopyRights failure\n");
340	}
341	/* fixme - destroy rights? Really? */
342	AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
343	if(ortn) {
344		*rcode = ortn;
345		return 0;
346	}
347
348	/*
349	 * Take the trust settings lock only after we've confirmed our authorization
350	 */
351	StLock<Mutex> _(gTrustSettingsLock());
352
353	char path[MAXPATHLEN + 1];
354
355	trustSettingsPath(auditToken, domain, path);
356
357	/*
358	 * Looks like we're good to go.
359	 * First, handle easy case of deleting a Trust Settings file (indicated
360	 * by an empty trustSettings).
361	 */
362	if(trustSettingsCnt == 0) {
363		ocspdTrustDebug("trustSettingsWrite: DELETING %s", path);
364		if(unlink(path)) {
365			/* FIXME maybe we should log this to the console */
366			*rcode = errno;
367			ocspdErrorLog("trustSettingsWrite: unlink error %d\n", errno);
368		}
369		else {
370			*rcode = noErr;
371		}
372		return 0;
373	}
374
375
376	/*
377	 * Create TRUST_SETTINGS_PATH if necessary.
378	 */
379	struct stat sb;
380	if(stat(TRUST_SETTINGS_PATH, &sb)) {
381		ocspdTrustDebug("trustSettingsWrite: creating %s", TRUST_SETTINGS_PATH);
382		int errcode = mkpath_np(TRUST_SETTINGS_PATH, TRUST_SETTINGS_PATH_MODE);
383		if(errcode) {
384			ocspdErrorLog("trustSettingsWrite: mkpath_np() returned %d\n", errcode);
385			*rcode = internalComponentErr;
386			return 0;
387		}
388
389		/* override the probable umask that made this directory unreadable by others */
390		chmod(TRUST_SETTINGS_PATH, TRUST_SETTINGS_PATH_MODE);
391	}
392
393	/* And, finally.... */
394	if(writeFile(path, (const unsigned char *)trustSettings, trustSettingsCnt)) {
395		ocspdErrorLog("trustSettingsWrite: writeFile() error\n");
396		*rcode = internalComponentErr;
397	}
398	else {
399		ocspdTrustDebug("trustSettingsWrite: wrote %lu bytes to %s",
400			(unsigned long)trustSettingsCnt, path);
401		chmod(path, TRUST_SETTINGS_FILES_MODE);
402		*rcode = noErr;
403	}
404	return 0;
405}
406