1/*
2 * Copyright (c) 2012 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// drmaker - create automatic Designated Requirements
26//
27#include "drmaker.h"
28#include "csutilities.h"
29#include <Security/oidsbase.h>
30#include <Security/SecCertificatePriv.h>
31//#include <Security/cssmapplePriv.h>
32
33namespace Security {
34namespace CodeSigning {
35
36
37static const uint8_t adcSdkMarker[] = { APPLE_EXTENSION_OID, 2, 1 };		// iOS intermediate marker
38const CSSM_DATA adcSdkMarkerOID = { sizeof(adcSdkMarker), (uint8_t *)adcSdkMarker };
39
40static const uint8_t caspianSdkMarker[] = { APPLE_EXTENSION_OID, 2, 6 }; // Caspian intermediate marker
41const CSSM_DATA devIdSdkMarkerOID = { sizeof(caspianSdkMarker), (uint8_t *)caspianSdkMarker };
42static const uint8_t caspianLeafMarker[] = { APPLE_EXTENSION_OID, 1, 13 }; // Caspian leaf certificate marker
43const CSSM_DATA devIdLeafMarkerOID = { sizeof(caspianLeafMarker), (uint8_t *)caspianLeafMarker };
44
45
46
47DRMaker::DRMaker(const Requirement::Context &context)
48	: ctx(context)
49{
50}
51
52DRMaker::~DRMaker()
53{
54}
55
56
57//
58// Generate the default (implicit) Designated Requirement for this StaticCode.
59// This is a heuristic of sorts, and may change over time (for the better, we hope).
60//
61Requirement *DRMaker::make()
62{
63	// we can't make an explicit DR for a (proposed) ad-hoc signing because that requires the CodeDirectory (which we ain't got yet)
64	if (ctx.certCount() == 0)
65		return NULL;
66
67	// always require the identifier
68	this->put(opAnd);
69	this->ident(ctx.identifier);
70
71	SHA1::Digest anchorHash;
72	hashOfCertificate(ctx.cert(Requirement::anchorCert), anchorHash);
73	if (isAppleCA(anchorHash)
74#if	defined(TEST_APPLE_ANCHOR)
75		|| !memcmp(anchorHash, Requirement::testAppleAnchorHash(), SHA1::digestLength)
76#endif
77		)
78		appleAnchor();
79	else
80		nonAppleAnchor();
81
82	return Maker::make();
83}
84
85
86void DRMaker::nonAppleAnchor()
87{
88	// get the Organization DN element for the leaf
89	CFRef<CFStringRef> leafOrganization;
90	MacOSError::check(SecCertificateCopySubjectComponent(ctx.cert(Requirement::leafCert),
91		&CSSMOID_OrganizationName, &leafOrganization.aref()));
92
93	// now step up the cert chain looking for the first cert with a different one
94	int slot = Requirement::leafCert;						// start at leaf
95	if (leafOrganization) {
96		while (SecCertificateRef ca = ctx.cert(slot+1)) {		// NULL if you over-run the anchor slot
97			CFRef<CFStringRef> caOrganization;
98			MacOSError::check(SecCertificateCopySubjectComponent(ca, &CSSMOID_OrganizationName, &caOrganization.aref()));
99			if (!caOrganization || CFStringCompare(leafOrganization, caOrganization, 0) != kCFCompareEqualTo)
100				break;
101			slot++;
102		}
103		if (slot == ctx.certCount() - 1)		// went all the way to the anchor...
104			slot = Requirement::anchorCert;					// ... so say that
105	}
106
107	// nail the last cert with the leaf's Organization value
108	SHA1::Digest authorityHash;
109	hashOfCertificate(ctx.cert(slot), authorityHash);
110	this->anchor(slot, authorityHash);
111}
112
113
114void DRMaker::appleAnchor()
115{
116	if (isIOSSignature()) {
117		// get the Common Name DN element for the leaf
118		CFRef<CFStringRef> leafCN;
119		MacOSError::check(SecCertificateCopySubjectComponent(ctx.cert(Requirement::leafCert),
120			&CSSMOID_CommonName, &leafCN.aref()));
121
122		// apple anchor generic and ...
123		this->put(opAnd);
124		this->anchorGeneric();			// apple generic anchor and...
125		// ... leaf[subject.CN] = <leaf's subject> and ...
126		this->put(opAnd);
127		this->put(opCertField);			// certificate
128		this->put(0);					// leaf
129		this->put("subject.CN");		// [subject.CN]
130		this->put(matchEqual);			// =
131		this->putData(leafCN);			// <leaf CN>
132		// ... cert 1[field.<marker>] exists
133		this->put(opCertGeneric);		// certificate
134		this->put(1);					// 1
135		this->putData(adcSdkMarkerOID.Data, adcSdkMarkerOID.Length); // [field.<marker>]
136		this->put(matchExists);			// exists
137		return;
138	}
139
140	if (isDeveloperIDSignature()) {
141		// get the Organizational Unit DN element for the leaf (it contains the TEAMID)
142		CFRef<CFStringRef> teamID;
143		MacOSError::check(SecCertificateCopySubjectComponent(ctx.cert(Requirement::leafCert),
144			&CSSMOID_OrganizationalUnitName, &teamID.aref()));
145
146		// apple anchor generic and ...
147		this->put(opAnd);
148		this->anchorGeneric();			// apple generic anchor and...
149
150		// ... certificate 1[intermediate marker oid] exists and ...
151		this->put(opAnd);
152		this->put(opCertGeneric);		// certificate
153		this->put(1);					// 1
154		this->putData(caspianSdkMarker, sizeof(caspianSdkMarker));
155		this->put(matchExists);			// exists
156
157		// ... certificate leaf[Caspian cert oid] exists and ...
158		this->put(opAnd);
159		this->put(opCertGeneric);		// certificate
160		this->put(0);					// leaf
161		this->putData(caspianLeafMarker, sizeof(caspianLeafMarker));
162		this->put(matchExists);			// exists
163
164		// ... leaf[subject.OU] = <leaf's subject>
165		this->put(opCertField);			// certificate
166		this->put(0);					// leaf
167		this->put("subject.OU");		// [subject.OU]
168		this->put(matchEqual);			// =
169		this->putData(teamID);			// TEAMID
170		return;
171	}
172
173	// otherwise, claim this program for Apple Proper
174	this->anchor();
175}
176
177bool DRMaker::isIOSSignature()
178{
179	if (ctx.certCount() == 3)		// leaf, one intermediate, anchor
180		if (SecCertificateRef intermediate = ctx.cert(1)) // get intermediate
181			if (certificateHasField(intermediate, CssmOid::overlay(adcSdkMarkerOID)))
182				return true;
183	return false;
184}
185
186bool DRMaker::isDeveloperIDSignature()
187{
188	if (ctx.certCount() == 3)		// leaf, one intermediate, anchor
189		if (SecCertificateRef intermediate = ctx.cert(1)) // get intermediate
190			if (certificateHasField(intermediate, CssmOid::overlay(devIdSdkMarkerOID)))
191				return true;
192	return false;
193}
194
195
196} // end namespace CodeSigning
197} // end namespace Security
198