a98f92496e46788df51088bd46893772041b5640
[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 "renum.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 (!bestRule || rule->weight > bestRule->weight)
92 bestRule = rule;
93 }
94 if (!bestRule || (bestRule->flags & omitted))
95 continue;
96 rule = bestRule;
97 return ent;
98 }
99 return NULL;
100 }
101
102
103 //
104 // Build the ResourceDirectory given the currently established rule set.
105 //
106 CFDictionaryRef ResourceBuilder::build()
107 {
108 secdebug("codesign", "start building resource directory");
109 CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary(0);
110
111 string path;
112 Rule *rule;
113 while (FTSENT *ent = next(path, rule)) {
114 assert(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);
123 }
124 }
125 secdebug("codesign", "finished code directory with %d entries",
126 int(CFDictionaryGetCount(files)));
127
128 return makeCFDictionary(2,
129 CFSTR("rules"), mRawRules.get(),
130 CFSTR("files"), files.get()
131 );
132 }
133
134
135 //
136 // Hash a file and return a CFDataRef with the hash
137 //
138 CFDataRef ResourceBuilder::hashFile(const char *path)
139 {
140 CFRef<CFDataRef> data = cfLoadFile(path);
141 secdebug("rdirenum", " %s (%d bytes)", path, int(CFDataGetLength(data)));
142 SHA1 hasher;
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));
147 }
148
149
150 //
151 // Regex matching objects
152 //
153 ResourceBuilder::Rule::Rule(const std::string &pattern, unsigned w, uint32_t f)
154 : weight(w), flags(f)
155 {
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);
160 }
161
162 ResourceBuilder::Rule::~Rule()
163 {
164 ::regfree(this);
165 }
166
167 bool ResourceBuilder::Rule::match(const char *s) const
168 {
169 switch (::regexec(this, s, 0, NULL, 0)) {
170 case 0:
171 return true;
172 case REG_NOMATCH:
173 return false;
174 default:
175 MacOSError::throwMe(errSecCSResourceRulesInvalid);
176 }
177 }
178
179
180 //
181 // Resource Seals
182 //
183 ResourceSeal::ResourceSeal(CFTypeRef it)
184 {
185 if (it == NULL)
186 MacOSError::throwMe(errSecCSResourcesInvalid);
187 if (CFGetTypeID(it) == CFDataGetTypeID()) {
188 mHash = CFDataRef(it);
189 mOptional = false;
190 } else {
191 mOptional = false;
192 if (!cfscan(it, "{hash=%XO,?optional=%B}", &mHash, &mOptional)
193 || size_t(CFDataGetLength(mHash)) != SHA1::digestLength)
194 MacOSError::throwMe(errSecCSResourcesInvalid);
195 }
196 }
197
198
199 } // end namespace CodeSigning
200 } // end namespace Security