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"
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 (!bestRule
|| rule
->weight
> bestRule
->weight
)
94 if (!bestRule
|| (bestRule
->flags
& omitted
))
104 // Build the ResourceDirectory given the currently established rule set.
106 CFDictionaryRef
ResourceBuilder::build()
108 secdebug("codesign", "start building resource directory");
109 CFRef
<CFMutableDictionaryRef
> files
= makeCFMutableDictionary(0);
113 while (FTSENT
*ent
= next(path
, rule
)) {
115 CFRef
<CFDataRef
> hash
= hashFile(ent
->fts_accpath
);
116 if (rule
->flags
== 0) { // default case - plain hash
117 cfadd(files
, "{%s=%O}", path
.c_str(), hash
.get());
118 secdebug("csresource", "%s added simple (rule %p)", path
.c_str(), rule
);
119 } else { // more complicated - use a sub-dictionary
120 cfadd(files
, "{%s={hash=%O,optional=%B}}",
121 path
.c_str(), hash
.get(), rule
->flags
& optional
);
122 secdebug("csresource", "%s added complex (rule %p)", path
.c_str(), rule
);
125 secdebug("codesign", "finished code directory with %d entries",
126 int(CFDictionaryGetCount(files
)));
128 return makeCFDictionary(2,
129 CFSTR("rules"), mRawRules
.get(),
130 CFSTR("files"), files
.get()
136 // Hash a file and return a CFDataRef with the hash
138 CFDataRef
ResourceBuilder::hashFile(const char *path
)
140 CFRef
<CFDataRef
> data
= cfLoadFile(path
);
141 secdebug("rdirenum", " %s (%d bytes)", path
, int(CFDataGetLength(data
)));
143 hasher(CFDataGetBytePtr(data
), CFDataGetLength(data
));
144 unsigned char digest
[CC_SHA1_DIGEST_LENGTH
];
145 hasher
.finish(digest
);
146 return CFDataCreate(NULL
, digest
, sizeof(digest
));
151 // Regex matching objects
153 ResourceBuilder::Rule::Rule(const std::string
&pattern
, unsigned w
, uint32_t f
)
154 : weight(w
), flags(f
)
156 if (::regcomp(this, pattern
.c_str(), REG_EXTENDED
| REG_NOSUB
)) //@@@ REG_ICASE?
157 MacOSError::throwMe(errSecCSResourceRulesInvalid
);
158 secdebug("csresource", "%p rule %s added (weight %d, flags 0x%x)",
159 this, pattern
.c_str(), w
, f
);
162 ResourceBuilder::Rule::~Rule()
167 bool ResourceBuilder::Rule::match(const char *s
) const
169 switch (::regexec(this, s
, 0, NULL
, 0)) {
175 MacOSError::throwMe(errSecCSResourceRulesInvalid
);
183 ResourceSeal::ResourceSeal(CFTypeRef it
)
186 MacOSError::throwMe(errSecCSResourcesInvalid
);
187 if (CFGetTypeID(it
) == CFDataGetTypeID()) {
188 mHash
= CFDataRef(it
);
192 if (!cfscan(it
, "{hash=%XO,?optional=%B}", &mHash
, &mOptional
)
193 || size_t(CFDataGetLength(mHash
)) != SHA1::digestLength
)
194 MacOSError::throwMe(errSecCSResourcesInvalid
);
199 } // end namespace CodeSigning
200 } // end namespace Security