2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 // resource directory construction and verification
27 #include "resources.h"
28 #include <Security/CSCommon.h>
29 #include <security_codesigning/cfmunge.h>
32 namespace CodeSigning
{
36 // Construction and maintainance
38 ResourceBuilder::ResourceBuilder(const std::string
&root
, CFDictionaryRef rulesDict
)
39 : ResourceEnumerator(root
)
41 CFDictionary
rules(rulesDict
, errSecCSResourceRulesInvalid
);
42 rules
.apply(this, &ResourceBuilder::addRule
);
46 ResourceBuilder::~ResourceBuilder()
48 for (Rules::iterator it
= mRules
.begin(); it
!= mRules
.end(); ++it
)
54 // Parse and add one matching rule
56 void ResourceBuilder::addRule(CFTypeRef key
, CFTypeRef value
)
58 string pattern
= cfString(key
, errSecCSResourceRulesInvalid
);
61 if (CFGetTypeID(value
) == CFBooleanGetTypeID()) {
62 if (value
== kCFBooleanFalse
)
65 CFDictionary
rule(value
, errSecCSResourceRulesInvalid
);
66 if (CFNumberRef weightRef
= rule
.get
<CFNumberRef
>("weight"))
67 weight
= cfNumber
<unsigned int>(weightRef
);
68 if (CFBooleanRef omitRef
= rule
.get
<CFBooleanRef
>("omit"))
69 if (omitRef
== kCFBooleanTrue
)
71 if (CFBooleanRef optRef
= rule
.get
<CFBooleanRef
>("optional"))
72 if (optRef
== kCFBooleanTrue
)
75 addRule(new Rule(pattern
, weight
, flags
));
80 // Locate the next non-ignored file, look up its rule, and return it.
81 // Returns NULL when we're out of files.
83 FTSENT
*ResourceBuilder::next(string
&path
, Rule
* &rule
)
85 while (FTSENT
*ent
= ResourceEnumerator::next(path
)) {
86 // find best matching rule
87 Rule
*bestRule
= NULL
;
88 for (Rules::const_iterator it
= mRules
.begin(); it
!= mRules
.end(); ++it
) {
90 if (rule
->match(path
.c_str())) {
91 if (rule
->flags
& exclusion
) {
95 if (!bestRule
|| rule
->weight
> bestRule
->weight
)
99 if (!bestRule
|| (bestRule
->flags
& omitted
))
109 // Build the ResourceDirectory given the currently established rule set.
111 CFDictionaryRef
ResourceBuilder::build()
113 secdebug("codesign", "start building resource directory");
114 CFRef
<CFMutableDictionaryRef
> files
= makeCFMutableDictionary(0);
118 while (FTSENT
*ent
= next(path
, rule
)) {
120 CFRef
<CFDataRef
> hash
= hashFile(ent
->fts_accpath
);
121 if (rule
->flags
== 0) { // default case - plain hash
122 cfadd(files
, "{%s=%O}", path
.c_str(), hash
.get());
123 secdebug("csresource", "%s added simple (rule %p)", path
.c_str(), rule
);
124 } else { // more complicated - use a sub-dictionary
125 cfadd(files
, "{%s={hash=%O,optional=%B}}",
126 path
.c_str(), hash
.get(), rule
->flags
& optional
);
127 secdebug("csresource", "%s added complex (rule %p)", path
.c_str(), rule
);
130 secdebug("codesign", "finished code directory with %d entries",
131 int(CFDictionaryGetCount(files
)));
133 return makeCFDictionary(2,
134 CFSTR("rules"), mRawRules
.get(),
135 CFSTR("files"), files
.get()
141 // Hash a file and return a CFDataRef with the hash
143 CFDataRef
ResourceBuilder::hashFile(const char *path
)
145 CFRef
<CFDataRef
> data
= cfLoadFile(path
);
146 secdebug("rdirenum", " %s (%d bytes)", path
, int(CFDataGetLength(data
)));
148 hasher(CFDataGetBytePtr(data
), CFDataGetLength(data
));
149 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
150 hasher
.finish(digest
);
151 return CFDataCreate(NULL
, digest
, sizeof(digest
));
156 // Regex matching objects
158 ResourceBuilder::Rule::Rule(const std::string
&pattern
, unsigned w
, uint32_t f
)
159 : weight(w
), flags(f
)
161 if (::regcomp(this, pattern
.c_str(), REG_EXTENDED
| REG_NOSUB
)) //@@@ REG_ICASE?
162 MacOSError::throwMe(errSecCSResourceRulesInvalid
);
163 secdebug("csresource", "%p rule %s added (weight %d, flags 0x%x)",
164 this, pattern
.c_str(), w
, f
);
167 ResourceBuilder::Rule::~Rule()
172 bool ResourceBuilder::Rule::match(const char *s
) const
174 switch (::regexec(this, s
, 0, NULL
, 0)) {
180 MacOSError::throwMe(errSecCSResourceRulesInvalid
);
188 ResourceSeal::ResourceSeal(CFTypeRef it
)
191 MacOSError::throwMe(errSecCSResourcesInvalid
);
192 if (CFGetTypeID(it
) == CFDataGetTypeID()) {
193 mHash
= CFDataRef(it
);
197 if (!cfscan(it
, "{hash=%XO,?optional=%B}", &mHash
, &mOptional
)
198 || size_t(CFDataGetLength(mHash
)) != SHA1::digestLength
)
199 MacOSError::throwMe(errSecCSResourcesInvalid
);
204 } // end namespace CodeSigning
205 } // end namespace Security