2 * Copyright (c) 2014 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@
26 #include <security_utilities/cfutilities.h>
27 #include <security_utilities/debugging.h>
28 #include <security_utilities/logging.h>
29 #include "dirscanner.h"
34 namespace CodeSigning
{
37 DirScanner::DirScanner(const char *path
)
40 this->path
= std::string(path
);
44 DirScanner::DirScanner(string path
)
51 DirScanner::~DirScanner()
54 (void) closedir(this->dp
);
57 void DirScanner::initialize()
59 if (this->dp
== NULL
) {
61 if ((this->dp
= opendir(this->path
.c_str())) == NULL
) {
62 if (errno
== ENOENT
) {
70 MacOSError::throwMe(errSecInternalError
);
73 struct dirent
* DirScanner::getNext()
77 int rc
= readdir_r(this->dp
, &this->entBuffer
, &ent
);
79 UnixError::throwMe(rc
);
80 } while (ent
&& (strcmp(ent
->d_name
, ".") == 0 || strcmp(ent
->d_name
, "..") == 0));
84 bool DirScanner::initialized()
89 void DirScanner::unlink(const struct dirent
* ent
, int flags
)
91 UnixError::check(::unlinkat(dirfd(this->dp
), ent
->d_name
, flags
));
94 bool DirScanner::isRegularFile(dirent
* dp
)
104 MacOSError::check(::stat((this->path
+ "/" + dp
->d_name
).c_str(), &st
));
105 return S_ISREG(st
.st_mode
);
112 DirValidator::~DirValidator()
114 for (Rules::iterator it
= mRules
.begin(); it
!= mRules
.end(); ++it
)
118 void DirValidator::validate(const string
&root
, OSStatus error
)
120 std::set
<Rule
*> reqMatched
;
122 while (FTSENT
*ent
= fts_read(fts
)) {
123 const char *relpath
= ent
->fts_path
+ root
.size() + 1; // skip prefix + "/"
124 bool executable
= ent
->fts_statp
->st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
);
126 switch (ent
->fts_info
) {
128 secinfo("dirval", "file %s", ent
->fts_path
);
129 rule
= match(relpath
, file
, executable
);
132 secinfo("dirval", "symlink %s", ent
->fts_path
);
133 char target
[PATH_MAX
];
134 ssize_t len
= ::readlink(ent
->fts_accpath
, target
, sizeof(target
)-1);
136 UnixError::throwMe();
138 rule
= match(relpath
, symlink
, executable
, target
);
142 secinfo("dirval", "entering %s", ent
->fts_path
);
143 if (ent
->fts_level
== FTS_ROOTLEVEL
)
144 continue; // skip root directory
145 rule
= match(relpath
, directory
, executable
);
146 if (!rule
|| !(rule
->flags
& descend
))
147 fts_set(fts
, ent
, FTS_SKIP
); // do not descend
150 secinfo("dirval", "leaving %s", ent
->fts_path
);
153 secinfo("dirval", "type %d (errno %d): %s", ent
->fts_info
, ent
->fts_errno
, ent
->fts_path
);
154 MacOSError::throwMe(error
); // not a file, symlink, or directory
157 MacOSError::throwMe(error
); // no match
158 else if (rule
->flags
& required
)
159 reqMatched
.insert(rule
);
161 if (reqMatched
.size() != (unsigned long) mRequireCount
) {
163 os
<< "matched " << reqMatched
.size() << " of " << mRequireCount
<< " required rules";
164 secinfo("dirval", "%s", os
.str().c_str());
165 MacOSError::throwMe(error
); // not all required rules were matched
169 DirValidator::Rule
* DirValidator::match(const char *path
, uint32_t flags
, bool executable
, const char *target
)
171 for (Rules::iterator it
= mRules
.begin(); it
!= mRules
.end(); ++it
) {
173 if ((rule
->flags
& flags
)
174 && !(executable
&& (rule
->flags
& noexec
))
176 && (!target
|| rule
->matchTarget(path
, target
)))
182 DirValidator::FTS::FTS(const string
&path
, int options
)
184 const char * paths
[2] = { path
.c_str(), NULL
};
185 mFTS
= fts_open((char * const *)paths
, options
, NULL
);
187 UnixError::throwMe();
190 DirValidator::FTS::~FTS()
195 DirValidator::Rule::Rule(const string
&pattern
, uint32_t flags
, TargetPatternBuilder targetBlock
)
196 : ResourceBuilder::Rule(pattern
, 0, flags
), mTargetBlock(NULL
)
199 mTargetBlock
= Block_copy(targetBlock
);
202 DirValidator::Rule::~Rule()
205 Block_release(mTargetBlock
);
208 bool DirValidator::Rule::matchTarget(const char *path
, const char *target
) const
211 Syslog::notice("code signing internal problem: !mTargetBlock");
212 MacOSError::throwMe(errSecCSInternalError
);
214 string pattern
= mTargetBlock(path
, target
);
216 return true; // always match empty pattern
217 secinfo("dirval", "%s: match target %s against %s", path
, target
, pattern
.c_str());
219 if (::regcomp(&re
, pattern
.c_str(), REG_EXTENDED
| REG_NOSUB
)) {
220 Syslog::notice("code signing internal problem: failed to compile internal RE");
221 MacOSError::throwMe(errSecCSInternalError
);
223 int rv
= ::regexec(&re
, target
, 0, NULL
, 0);
231 Syslog::notice("code signing internal error: regexec failed error=%d", rv
);
232 MacOSError::throwMe(errSecCSInternalError
);
237 } // end namespace CodeSigning
238 } // end namespace Security