]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/resources.cpp
c1f9bb2e10db2389fc54191cee1aca94c7f67aff
[apple/libsecurity_codesigning.git] / lib / resources.cpp
1 /*
2 * Copyright (c) 2006 Apple Computer, 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 // resource directory construction and verification
26 //
27 #include "resources.h"
28 #include <Security/CSCommon.h>
29 #include <security_codesigning/cfmunge.h>
30
31 namespace Security {
32 namespace CodeSigning {
33
34
35 //
36 // Construction and maintainance
37 //
38 ResourceBuilder::ResourceBuilder(const std::string &root, CFDictionaryRef rulesDict)
39 : ResourceEnumerator(root)
40 {
41 CFDictionary rules(rulesDict, errSecCSResourceRulesInvalid);
42 rules.apply(this, &ResourceBuilder::addRule);
43 mRawRules = rules;
44 }
45
46 ResourceBuilder::~ResourceBuilder()
47 {
48 for (Rules::iterator it = mRules.begin(); it != mRules.end(); ++it)
49 delete *it;
50 }
51
52
53 //
54 // Parse and add one matching rule
55 //
56 void ResourceBuilder::addRule(CFTypeRef key, CFTypeRef value)
57 {
58 string pattern = cfString(key, errSecCSResourceRulesInvalid);
59 unsigned weight = 1;
60 uint32_t flags = 0;
61 if (CFGetTypeID(value) == CFBooleanGetTypeID()) {
62 if (value == kCFBooleanFalse)
63 flags |= omitted;
64 } else {
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)
70 flags |= omitted;
71 if (CFBooleanRef optRef = rule.get<CFBooleanRef>("optional"))
72 if (optRef == kCFBooleanTrue)
73 flags |= optional;
74 }
75 addRule(new Rule(pattern, weight, flags));
76 }
77
78
79 //
80 // Locate the next non-ignored file, look up its rule, and return it.
81 // Returns NULL when we're out of files.
82 //
83 FTSENT *ResourceBuilder::next(string &path, Rule * &rule)
84 {
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) {
89 Rule *rule = *it;
90 if (rule->match(path.c_str())) {
91 if (rule->flags & exclusion) {
92 bestRule = NULL;
93 break;
94 }
95 if (!bestRule || rule->weight > bestRule->weight)
96 bestRule = rule;
97 }
98 }
99 if (!bestRule || (bestRule->flags & omitted))
100 continue;
101 rule = bestRule;
102 return ent;
103 }
104 return NULL;
105 }
106
107
108 //
109 // Build the ResourceDirectory given the currently established rule set.
110 //
111 CFDictionaryRef ResourceBuilder::build()
112 {
113 secdebug("codesign", "start building resource directory");
114 CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary(0);
115
116 string path;
117 Rule *rule;
118 while (FTSENT *ent = next(path, rule)) {
119 assert(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);
128 }
129 }
130 secdebug("codesign", "finished code directory with %d entries",
131 int(CFDictionaryGetCount(files)));
132
133 return makeCFDictionary(2,
134 CFSTR("rules"), mRawRules.get(),
135 CFSTR("files"), files.get()
136 );
137 }
138
139
140 //
141 // Hash a file and return a CFDataRef with the hash
142 //
143 CFDataRef ResourceBuilder::hashFile(const char *path)
144 {
145 CFRef<CFDataRef> data = cfLoadFile(path);
146 secdebug("rdirenum", " %s (%d bytes)", path, int(CFDataGetLength(data)));
147 SHA1 hasher;
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));
152 }
153
154
155 //
156 // Regex matching objects
157 //
158 ResourceBuilder::Rule::Rule(const std::string &pattern, unsigned w, uint32_t f)
159 : weight(w), flags(f)
160 {
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);
165 }
166
167 ResourceBuilder::Rule::~Rule()
168 {
169 ::regfree(this);
170 }
171
172 bool ResourceBuilder::Rule::match(const char *s) const
173 {
174 switch (::regexec(this, s, 0, NULL, 0)) {
175 case 0:
176 return true;
177 case REG_NOMATCH:
178 return false;
179 default:
180 MacOSError::throwMe(errSecCSResourceRulesInvalid);
181 }
182 }
183
184
185 //
186 // Resource Seals
187 //
188 ResourceSeal::ResourceSeal(CFTypeRef it)
189 {
190 if (it == NULL)
191 MacOSError::throwMe(errSecCSResourcesInvalid);
192 if (CFGetTypeID(it) == CFDataGetTypeID()) {
193 mHash = CFDataRef(it);
194 mOptional = false;
195 } else {
196 mOptional = false;
197 if (!cfscan(it, "{hash=%XO,?optional=%B}", &mHash, &mOptional)
198 || size_t(CFDataGetLength(mHash)) != SHA1::digestLength)
199 MacOSError::throwMe(errSecCSResourcesInvalid);
200 }
201 }
202
203
204 } // end namespace CodeSigning
205 } // end namespace Security