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//
25// csgeneric - generic Code representative
26//
27#include "csgeneric.h"
28#include "cs.h"
29#include "StaticCode.h"
30#include <securityd_client/cshosting.h>
31#include <sys/param.h>
32
33namespace Security {
34namespace CodeSigning {
35
36using MachPlusPlus::Port;
37
38
39//
40// Common call-out code for cshosting RPC service
41//
42#define CALL(host, name, args...) \
43	OSStatus result; \
44	if (cshosting_client_ ## name (host, mig_get_reply_port(), &result, args)) \
45		MacOSError::throwMe(errSecCSNotAHost); \
46	MacOSError::check(result);
47
48
49//
50// Construct a running process representation
51//
52GenericCode::GenericCode(SecCode *host, SecGuestRef guestRef)
53	: SecCode(host), mGuestRef(guestRef)
54{
55}
56
57
58//
59// Identify a guest by attribute set, and return a new GenericCode representing it.
60// This uses cshosting RPCs to ask the host (or its proxy).
61//
62SecCode *GenericCode::locateGuest(CFDictionaryRef attributes)
63{
64	if (Port host = hostingPort()) {
65		CFRef<CFDataRef> attrData;
66		void *attrPtr = NULL; size_t attrLength = 0;
67		if (attributes) {
68			attrData.take(CFPropertyListCreateXMLData(NULL, attributes));
69			attrPtr = (void *)CFDataGetBytePtr(attrData);
70			attrLength = CFDataGetLength(attrData);
71		}
72		GuestChain guestPath;
73		mach_msg_type_number_t guestPathLength;
74		mach_port_t subport;
75		CALL(host, findGuest, guestRef(), attrPtr, (mach_msg_type_number_t)attrLength,
76			&guestPath, &guestPathLength, &subport);
77		CODESIGN_GUEST_LOCATE_GENERIC(this, guestPath, guestPathLength, subport);
78		SecPointer<SecCode> code = this;
79		for (unsigned n = 0; n < guestPathLength; n++)
80			code = new GenericCode(code, guestPath[n]);
81		return code.yield();
82	} else
83		return NULL;		// not found, no error
84}
85
86
87//
88// Identify a guest by returning its StaticCode and running CodeDirectory hash.
89// This uses cshosting RPCs to ask the host (or its proxy).
90//
91SecStaticCode *GenericCode::identifyGuest(SecCode *guest, CFDataRef *cdhashOut)
92{
93	if (GenericCode *iguest = dynamic_cast<GenericCode *>(guest)) {
94		FilePathOut path;
95		CFRef<CFDataRef> cdhash;
96		CFDictionary attributes(errSecCSHostProtocolInvalidAttribute);
97		identifyGuest(iguest->guestRef(), path, cdhash.aref(), attributes.aref());
98		DiskRep::Context ctx;
99		if (CFNumberRef architecture = attributes.get<CFNumberRef>(kSecGuestAttributeArchitecture)) {
100			cpu_type_t cpu = cfNumber<cpu_type_t>(architecture);
101			if (CFNumberRef subarchitecture = attributes.get<CFNumberRef>(kSecGuestAttributeSubarchitecture))
102				ctx.arch = Architecture(cpu, cfNumber<cpu_subtype_t>(subarchitecture));
103			else
104				ctx.arch = Architecture(cpu);
105		}
106		SecPointer<GenericStaticCode> code = new GenericStaticCode(DiskRep::bestGuess(path, &ctx));
107		CODESIGN_GUEST_IDENTIFY_GENERIC(iguest, iguest->guestRef(), code);
108		if (cdhash) {
109			CODESIGN_GUEST_CDHASH_GENERIC(iguest, (void *)CFDataGetBytePtr(cdhash), (unsigned)CFDataGetLength(cdhash));
110			*cdhashOut = cdhash.yield();
111		}
112		return code.yield();
113	} else
114		MacOSError::throwMe(errSecCSNotAHost);
115}
116
117// helper to drive the identifyGuest hosting IPC and return results as CF objects
118void GenericCode::identifyGuest(SecGuestRef guest, char *path, CFDataRef &cdhash, CFDictionaryRef &attributes)
119{
120	if (Port host = hostingPort()) {
121		HashDataOut hash;
122		uint32_t hashLength;
123		XMLBlobOut attr;
124		uint32_t attrLength;
125		CALL(host, identifyGuest, guest, path, hash, &hashLength, &attr, &attrLength);
126		if (hashLength)
127			cdhash = makeCFData(hash, hashLength);
128		if (attrLength) {
129			CFRef<CFDataRef> attrData = makeCFData(attr, attrLength);
130			attributes = makeCFDictionaryFrom(attrData);
131#if ROSETTA_TEST_HACK
132			CFMutableDictionaryRef hattr = makeCFMutableDictionary(attributes);
133			CFDictionaryAddValue(hattr, kSecGuestAttributeArchitecture, CFTempNumber(CPU_TYPE_POWERPC));
134			CFRelease(attributes);
135			attributes = hattr;
136#endif
137		}
138	} else
139		MacOSError::throwMe(errSecCSNotAHost);
140}
141
142
143//
144// Get the Code Signing Status Word for a Code.
145// This uses cshosting RPCs to ask the host (or its proxy).
146//
147SecCodeStatus GenericCode::getGuestStatus(SecCode *guest)
148{
149	if (Port host = hostingPort()) {
150		uint32_t status;
151		CALL(host, guestStatus, safe_cast<GenericCode *>(guest)->guestRef(), &status);
152		return status;
153	} else
154		MacOSError::throwMe(errSecCSNotAHost);
155}
156
157
158//
159// Status changes are transmitted through the cshosting RPCs.
160//
161void GenericCode::changeGuestStatus(SecCode *iguest, SecCodeStatusOperation operation, CFDictionaryRef arguments)
162{
163	if (/* GenericCode *guest = */dynamic_cast<GenericCode *>(iguest))
164		switch (operation) {
165		case kSecCodeOperationNull:
166			break;
167		case kSecCodeOperationInvalidate:
168		case kSecCodeOperationSetHard:
169		case kSecCodeOperationSetKill:
170			MacOSError::throwMe(errSecCSUnimplemented);
171			break;
172		default:
173			MacOSError::throwMe(errSecCSUnimplemented);
174		}
175	else
176		MacOSError::throwMe(errSecCSNoSuchCode);
177}
178
179
180//
181// Return the Hosting Port for this Code.
182// May return MACH_PORT_NULL if the code is not a code host.
183// Throws if we can't get the hosting port for some reason (and can't positively
184// determine that there is none).
185//
186// Note that we do NOT cache negative outcomes. Being a host is a dynamic property,
187// and this Code may not have commenced hosting operations yet. For non- (or not-yet-)hosts
188// we simply return NULL.
189//
190Port GenericCode::hostingPort()
191{
192	if (!mHostingPort) {
193		if (staticCode()->codeDirectory()->flags & kSecCodeSignatureHost) {
194			mHostingPort = getHostingPort();
195			CODESIGN_GUEST_HOSTINGPORT(this, mHostingPort);
196		}
197	}
198	return mHostingPort;
199}
200
201
202//
203// A pure GenericHost has no idea where to get a hosting port from.
204// This method must be overridden to get one.
205// However, we do handle a contiguous chain of GenericCodes by deferring
206// to our next-higher host for it.
207//
208mach_port_t GenericCode::getHostingPort()
209{
210	if (GenericCode *genericHost = dynamic_cast<GenericCode *>(host()))
211		return genericHost->getHostingPort();
212	else
213		MacOSError::throwMe(errSecCSNotAHost);
214}
215
216
217} // CodeSigning
218} // Security
219