1/*
2 * Copyright (c) 2006-2007 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// clientid - track and manage identity of securityd clients
25//
26#include "clientid.h"
27#include "server.h"
28#include <Security/SecCodePriv.h>
29
30
31//
32// Constructing a ClientIdentification doesn't do much.
33// We're waiting for setup(), which should be called by the child class's
34// constructor.
35//
36ClientIdentification::ClientIdentification()
37{
38}
39
40
41//
42// Initialize the ClientIdentification.
43// This creates a process-level code object for the client.
44//
45void ClientIdentification::setup(pid_t pid)
46{
47	StLock<Mutex> _(mLock);
48	if (OSStatus rc = SecCodeCreateWithPID(pid, kSecCSDefaultFlags,
49			&mClientProcess.aref()))
50		secdebug("clientid", "could not get code for process %d: OSStatus=%d",
51			pid, int32_t(rc));
52	mGuests.erase(mGuests.begin(), mGuests.end());
53}
54
55
56//
57// Return a SecCodeRef for the client process itself, regardless of
58// which guest within it is currently active.
59// Think twice before using this.
60//
61SecCodeRef ClientIdentification::processCode() const
62{
63	return mClientProcess;
64}
65
66
67//
68// Return a SecCodeRef for the currently active guest within the client
69// process.
70//
71// We make a fair effort to cache client guest identities without over-growing
72// the cache. Note that there's currently no protocol for being notified of
73// a guest's death or disappearance (independent from the host process's death),
74// so we couldn't track guests live even if we tried.
75//
76// Note that this consults Server::connection for the currently serviced
77// Connection object, so this is not entirely a function of ClientIdentification state.
78//
79SecCodeRef ClientIdentification::currentGuest() const
80{
81	if (GuestState *guest = current())
82		return guest->code;
83	else
84		return mClientProcess;
85}
86
87ClientIdentification::GuestState *ClientIdentification::current() const
88{
89	// if we have no client identification, we can't find a current guest either
90	if (!processCode())
91		return NULL;
92
93	SecGuestRef guestRef = Server::connection().guestRef();
94
95	// try to deliver an already-cached entry
96	{
97		StLock<Mutex> _(mLock);
98		GuestMap::iterator it = mGuests.find(guestRef);
99		if (it != mGuests.end())
100			return &it->second;
101	}
102
103	// okay, make a new one (this may take a while)
104	CFRef<CFDictionaryRef> attributes = (guestRef == kSecNoGuest)
105		? NULL
106		: makeCFDictionary(1, kSecGuestAttributeCanonical, CFTempNumber(guestRef).get());
107	Server::active().longTermActivity();
108	CFRef<SecCodeRef> code;
109	switch (OSStatus rc = SecCodeCopyGuestWithAttributes(processCode(),
110		attributes, kSecCSDefaultFlags, &code.aref())) {
111	case noErr:
112		break;
113	case errSecCSUnsigned:			// not signed; clearly not a host
114	case errSecCSNotAHost:			// signed but not marked as a (potential) host
115		code = mClientProcess;
116		break;
117	case errSecCSNoSuchCode:		// potential host, but...
118		if (guestRef == kSecNoGuest) {	//  ... no guests (yet), so return the process
119			code = mClientProcess;
120			break;
121		}
122		// else fall through		//  ... the guest we expected to be there isn't
123	default:
124		MacOSError::throwMe(rc);
125	}
126	StLock<Mutex> _(mLock);
127	GuestState &slot = mGuests[guestRef];
128	if (!slot.code)	// if another thread didn't get here first...
129		slot.code = code;
130	return &slot;
131}
132
133
134//
135// Support for the legacy hash identification mechanism.
136// The legacy machinery deals exclusively in terms of processes.
137// It knows nothing about guests and their identities.
138//
139string ClientIdentification::getPath() const
140{
141	assert(mClientProcess);
142	return codePath(currentGuest());
143}
144
145const CssmData ClientIdentification::getHash() const
146{
147	if (GuestState *guest = current()) {
148		if (!guest->gotHash) {
149			RefPointer<OSXCode> clientCode = new OSXCodeWrap(guest->code);
150			OSXVerifier::makeLegacyHash(clientCode, guest->legacyHash);
151			guest->gotHash = true;
152		}
153		return CssmData::wrap(guest->legacyHash, SHA1::digestLength);
154	} else
155		return CssmData();
156}
157
158const bool ClientIdentification::checkAppleSigned() const
159{
160	if (GuestState *guest = current()) {
161		if (!guest->checkedSignature) {
162            // This is the clownfish supported way to check for a Mac App Store or B&I signed build
163            CFStringRef requirementString = CFSTR("(anchor apple) or (anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9])");
164            SecRequirementRef  secRequirementRef = NULL;
165            OSStatus status = SecRequirementCreateWithString(requirementString, kSecCSDefaultFlags, &secRequirementRef);
166            if (status == errSecSuccess) {
167                OSStatus status = SecCodeCheckValidity(guest->code, kSecCSDefaultFlags, secRequirementRef);
168                if (status != errSecSuccess) {
169                    secdebug("SecurityAgentXPCQuery", "code requirement check failed (%d)", (int32_t)status);
170                } else {
171                    guest->appleSigned = true;
172                }
173                guest->checkedSignature = true;
174            }
175            CFRelease(secRequirementRef);
176		}
177		return guest->appleSigned;
178	} else
179		return false;
180}
181
182
183//
184// Bonus function: get the path out of a SecCodeRef
185//
186std::string codePath(SecStaticCodeRef code)
187{
188	CFRef<CFURLRef> path;
189	MacOSError::check(SecCodeCopyPath(code, kSecCSDefaultFlags, &path.aref()));
190	return cfString(path);
191}
192
193
194//
195// Debug dump support
196//
197#if defined(DEBUGDUMP)
198
199static void dumpCode(SecCodeRef code)
200{
201	CFRef<CFURLRef> path;
202	if (OSStatus rc = SecCodeCopyPath(code, kSecCSDefaultFlags, &path.aref()))
203		Debug::dump("unknown(rc=%d)", int32_t(rc));
204	else
205		Debug::dump("%s", cfString(path).c_str());
206}
207
208void ClientIdentification::dump()
209{
210	Debug::dump(" client=");
211	dumpCode(mClientProcess);
212	for (GuestMap::const_iterator it = mGuests.begin(); it != mGuests.end(); ++it) {
213		Debug::dump(" guest(0x%x)=", it->first);
214		dumpCode(it->second.code);
215		if (it->second.gotHash)
216			Debug::dump(" [got hash]");
217	}
218}
219
220#endif //DEBUGDUMP
221