2 * Copyright (c) 2006-2010 Apple 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 "csutilities.h"
29 #include <Security/CSCommon.h>
30 #include <security_utilities/unix++.h>
31 #include <security_utilities/cfmunge.h>
34 namespace CodeSigning
{
38 // Construction and maintainance
40 ResourceBuilder::ResourceBuilder(const std::string
&root
, CFDictionaryRef rulesDict
, CodeDirectory::HashAlgorithm hashType
)
41 : ResourceEnumerator(root
), mHashType(hashType
)
43 CFDictionary
rules(rulesDict
, errSecCSResourceRulesInvalid
);
44 rules
.apply(this, &ResourceBuilder::addRule
);
48 ResourceBuilder::~ResourceBuilder()
50 for (Rules::iterator it
= mRules
.begin(); it
!= mRules
.end(); ++it
)
56 // Parse and add one matching rule
58 void ResourceBuilder::addRule(CFTypeRef key
, CFTypeRef value
)
60 string pattern
= cfString(key
, errSecCSResourceRulesInvalid
);
63 if (CFGetTypeID(value
) == CFBooleanGetTypeID()) {
64 if (value
== kCFBooleanFalse
)
67 CFDictionary
rule(value
, errSecCSResourceRulesInvalid
);
68 if (CFNumberRef weightRef
= rule
.get
<CFNumberRef
>("weight"))
69 weight
= cfNumber
<unsigned int>(weightRef
);
70 if (CFBooleanRef omitRef
= rule
.get
<CFBooleanRef
>("omit"))
71 if (omitRef
== kCFBooleanTrue
)
73 if (CFBooleanRef optRef
= rule
.get
<CFBooleanRef
>("optional"))
74 if (optRef
== kCFBooleanTrue
)
77 addRule(new Rule(pattern
, weight
, flags
));
82 // Locate the next non-ignored file, look up its rule, and return it.
83 // Returns NULL when we're out of files.
85 FTSENT
*ResourceBuilder::next(string
&path
, Rule
* &rule
)
87 while (FTSENT
*ent
= ResourceEnumerator::next(path
)) {
88 // find best matching rule
89 Rule
*bestRule
= NULL
;
90 for (Rules::const_iterator it
= mRules
.begin(); it
!= mRules
.end(); ++it
) {
92 if (rule
->match(path
.c_str())) {
93 if (rule
->flags
& exclusion
) {
97 if (!bestRule
|| rule
->weight
> bestRule
->weight
)
101 if (!bestRule
|| (bestRule
->flags
& omitted
))
111 // Build the ResourceDirectory given the currently established rule set.
113 CFDictionaryRef
ResourceBuilder::build()
115 secdebug("codesign", "start building resource directory");
116 CFRef
<CFMutableDictionaryRef
> files
= makeCFMutableDictionary();
120 while (FTSENT
*ent
= next(path
, rule
)) {
122 CFRef
<CFDataRef
> hash
= hashFile(ent
->fts_accpath
);
123 if (rule
->flags
== 0) { // default case - plain hash
124 cfadd(files
, "{%s=%O}", path
.c_str(), hash
.get());
125 secdebug("csresource", "%s added simple (rule %p)", path
.c_str(), rule
);
126 } else { // more complicated - use a sub-dictionary
127 cfadd(files
, "{%s={hash=%O,optional=%B}}",
128 path
.c_str(), hash
.get(), rule
->flags
& optional
);
129 secdebug("csresource", "%s added complex (rule %p)", path
.c_str(), rule
);
132 secdebug("codesign", "finished code directory with %d entries",
133 int(CFDictionaryGetCount(files
)));
135 return cfmake
<CFDictionaryRef
>("{rules=%O,files=%O}", mRawRules
.get(), files
.get());
140 // Hash a file and return a CFDataRef with the hash
142 CFDataRef
ResourceBuilder::hashFile(const char *path
)
144 UnixPlusPlus::AutoFileDesc
fd(path
);
145 fd
.fcntl(F_NOCACHE
, true); // turn off page caching (one-pass)
146 MakeHash
<ResourceBuilder
> hasher(this);
147 hashFileData(fd
, hasher
.get());
148 Hashing::Byte digest
[hasher
->digestLength()];
149 hasher
->finish(digest
);
150 return CFDataCreate(NULL
, digest
, sizeof(digest
));
155 // Regex matching objects
157 ResourceBuilder::Rule::Rule(const std::string
&pattern
, unsigned w
, uint32_t f
)
158 : weight(w
), flags(f
)
160 if (::regcomp(this, pattern
.c_str(), REG_EXTENDED
| REG_NOSUB
)) //@@@ REG_ICASE?
161 MacOSError::throwMe(errSecCSResourceRulesInvalid
);
162 secdebug("csresource", "%p rule %s added (weight %d, flags 0x%x)",
163 this, pattern
.c_str(), w
, f
);
166 ResourceBuilder::Rule::~Rule()
171 bool ResourceBuilder::Rule::match(const char *s
) const
173 switch (::regexec(this, s
, 0, NULL
, 0)) {
179 MacOSError::throwMe(errSecCSResourceRulesInvalid
);
184 std::string
ResourceBuilder::escapeRE(const std::string
&s
)
187 for (string::const_iterator it
= s
.begin(); it
!= s
.end(); ++it
) {
189 if (strchr("\\[]{}().+*", c
))
200 ResourceSeal::ResourceSeal(CFTypeRef it
)
203 MacOSError::throwMe(errSecCSResourcesInvalid
);
204 if (CFGetTypeID(it
) == CFDataGetTypeID()) {
205 mHash
= CFDataRef(it
);
209 if (!cfscan(it
, "{hash=%XO,?optional=%B}", &mHash
, &mOptional
))
210 MacOSError::throwMe(errSecCSResourcesInvalid
);
215 } // end namespace CodeSigning
216 } // end namespace Security