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