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"
32 namespace CodeSigning
{
35 DirScanner::DirScanner(const char *path
)
38 this->path
= std::string(path
);
42 DirScanner::DirScanner(string path
)
49 DirScanner::~DirScanner()
52 (void) closedir(this->dp
);
55 void DirScanner::initialize()
57 if (this->dp
== NULL
) {
59 if ((this->dp
= opendir(this->path
.c_str())) == NULL
) {
60 if (errno
== ENOENT
) {
68 MacOSError::throwMe(errSecInternalError
);
71 struct dirent
* DirScanner::getNext()
75 int rc
= readdir_r(this->dp
, &this->entBuffer
, &ent
);
77 UnixError::throwMe(rc
);
78 } while (ent
&& (strcmp(ent
->d_name
, ".") == 0 || strcmp(ent
->d_name
, "..") == 0));
82 bool DirScanner::initialized()
87 void DirScanner::unlink(const struct dirent
* ent
, int flags
)
89 UnixError::check(::unlinkat(dirfd(this->dp
), ent
->d_name
, flags
));
92 bool DirScanner::isRegularFile(dirent
* dp
)
102 MacOSError::check(::stat((this->path
+ "/" + dp
->d_name
).c_str(), &st
));
103 return S_ISREG(st
.st_mode
);
110 DirValidator::~DirValidator()
112 for (Rules::iterator it
= mRules
.begin(); it
!= mRules
.end(); ++it
)
116 void DirValidator::validate(const string
&root
, OSStatus error
)
118 std::set
<Rule
*> reqMatched
;
120 while (FTSENT
*ent
= fts_read(fts
)) {
121 const char *relpath
= ent
->fts_path
+ root
.size() + 1; // skip prefix + "/"
122 bool executable
= ent
->fts_statp
->st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
);
124 switch (ent
->fts_info
) {
126 secinfo("dirval", "file %s", ent
->fts_path
);
127 rule
= match(relpath
, file
, executable
);
130 secinfo("dirval", "symlink %s", ent
->fts_path
);
131 char target
[PATH_MAX
];
132 ssize_t len
= ::readlink(ent
->fts_accpath
, target
, sizeof(target
)-1);
134 UnixError::throwMe();
136 rule
= match(relpath
, symlink
, executable
, target
);
140 secinfo("dirval", "entering %s", ent
->fts_path
);
141 if (ent
->fts_level
== FTS_ROOTLEVEL
)
142 continue; // skip root directory
143 rule
= match(relpath
, directory
, executable
);
144 if (!rule
|| !(rule
->flags
& descend
))
145 fts_set(fts
, ent
, FTS_SKIP
); // do not descend
148 secinfo("dirval", "leaving %s", ent
->fts_path
);
151 secinfo("dirval", "type %d (errno %d): %s", ent
->fts_info
, ent
->fts_errno
, ent
->fts_path
);
152 MacOSError::throwMe(error
); // not a file, symlink, or directory
155 MacOSError::throwMe(error
); // no match
156 else if (rule
->flags
& required
)
157 reqMatched
.insert(rule
);
159 if (reqMatched
.size() != mRequireCount
) {
160 secinfo("dirval", "matched %lu of %d required rules", reqMatched
.size(), mRequireCount
);
161 MacOSError::throwMe(error
); // not all required rules were matched
165 DirValidator::Rule
* DirValidator::match(const char *path
, uint32_t flags
, bool executable
, const char *target
)
167 for (Rules::iterator it
= mRules
.begin(); it
!= mRules
.end(); ++it
) {
169 if ((rule
->flags
& flags
)
170 && !(executable
&& (rule
->flags
& noexec
))
172 && (!target
|| rule
->matchTarget(path
, target
)))
178 DirValidator::FTS::FTS(const string
&path
, int options
)
180 const char * paths
[2] = { path
.c_str(), NULL
};
181 mFTS
= fts_open((char * const *)paths
, options
, NULL
);
183 UnixError::throwMe();
186 DirValidator::FTS::~FTS()
191 DirValidator::Rule::Rule(const string
&pattern
, uint32_t flags
, TargetPatternBuilder targetBlock
)
192 : ResourceBuilder::Rule(pattern
, 0, flags
), mTargetBlock(NULL
)
195 mTargetBlock
= Block_copy(targetBlock
);
198 DirValidator::Rule::~Rule()
201 Block_release(mTargetBlock
);
204 bool DirValidator::Rule::matchTarget(const char *path
, const char *target
) const
207 Syslog::notice("code signing internal problem: !mTargetBlock");
208 MacOSError::throwMe(errSecCSInternalError
);
210 string pattern
= mTargetBlock(path
, target
);
212 return true; // always match empty pattern
213 secinfo("dirval", "%s: match target %s against %s", path
, target
, pattern
.c_str());
215 if (::regcomp(&re
, pattern
.c_str(), REG_EXTENDED
| REG_NOSUB
)) {
216 Syslog::notice("code signing internal problem: failed to compile internal RE");
217 MacOSError::throwMe(errSecCSInternalError
);
219 int rv
= ::regexec(&re
, target
, 0, NULL
, 0);
227 Syslog::notice("code signing internal error: regexec failed error=%d", rv
);
228 MacOSError::throwMe(errSecCSInternalError
);
233 } // end namespace CodeSigning
234 } // end namespace Security