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 "dirscanner.h"
31 namespace CodeSigning
{
34 DirScanner::DirScanner(const char *path
)
37 this->path
= std::string(path
);
41 DirScanner::DirScanner(string path
)
48 DirScanner::~DirScanner()
51 (void) closedir(this->dp
);
54 void DirScanner::initialize()
56 if (this->dp
== NULL
) {
58 if ((this->dp
= opendir(this->path
.c_str())) == NULL
) {
59 if (errno
== ENOENT
) {
67 MacOSError::throwMe(errSecInternalError
);
70 struct dirent
* DirScanner::getNext()
72 return readdir(this->dp
);
75 bool DirScanner::initialized()
81 DirValidator::~DirValidator()
83 for (Rules::iterator it
= mRules
.begin(); it
!= mRules
.end(); ++it
)
87 void DirValidator::validate(const string
&root
, OSStatus error
)
89 std::set
<Rule
*> reqMatched
;
91 while (FTSENT
*ent
= fts_read(fts
)) {
92 const char *relpath
= ent
->fts_path
+ root
.size() + 1; // skip prefix + "/"
93 bool executable
= ent
->fts_statp
->st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
);
95 switch (ent
->fts_info
) {
97 secdebug("dirval", "file %s", ent
->fts_path
);
98 rule
= match(relpath
, file
, executable
);
101 secdebug("dirval", "symlink %s", ent
->fts_path
);
102 char target
[PATH_MAX
];
103 ssize_t len
= ::readlink(ent
->fts_accpath
, target
, sizeof(target
)-1);
105 UnixError::throwMe();
107 rule
= match(relpath
, symlink
, executable
, target
);
111 secdebug("dirval", "entering %s", ent
->fts_path
);
112 if (ent
->fts_level
== FTS_ROOTLEVEL
)
113 continue; // skip root directory
114 rule
= match(relpath
, directory
, executable
);
115 if (!rule
|| !(rule
->flags
& descend
))
116 fts_set(fts
, ent
, FTS_SKIP
); // do not descend
119 secdebug("dirval", "leaving %s", ent
->fts_path
);
122 secdebug("dirval", "type %d (errno %d): %s", ent
->fts_info
, ent
->fts_errno
, ent
->fts_path
);
123 MacOSError::throwMe(error
); // not a file, symlink, or directory
126 MacOSError::throwMe(error
); // no match
127 else if (rule
->flags
& required
)
128 reqMatched
.insert(rule
);
130 if (reqMatched
.size() != mRequireCount
) {
131 secdebug("dirval", "matched %d of %d required rules", reqMatched
.size(), mRequireCount
);
132 MacOSError::throwMe(error
); // not all required rules were matched
136 DirValidator::Rule
* DirValidator::match(const char *path
, uint32_t flags
, bool executable
, const char *target
)
138 for (Rules::iterator it
= mRules
.begin(); it
!= mRules
.end(); ++it
) {
140 if ((rule
->flags
& flags
)
141 && !(executable
&& (rule
->flags
& noexec
))
143 && (!target
|| rule
->matchTarget(path
, target
)))
149 DirValidator::FTS::FTS(const string
&path
, int options
)
151 const char * paths
[2] = { path
.c_str(), NULL
};
152 mFTS
= fts_open((char * const *)paths
, options
, NULL
);
154 UnixError::throwMe();
157 DirValidator::FTS::~FTS()
162 DirValidator::Rule::Rule(const string
&pattern
, uint32_t flags
, TargetPatternBuilder targetBlock
)
163 : ResourceBuilder::Rule(pattern
, 0, flags
), mTargetBlock(NULL
)
166 mTargetBlock
= Block_copy(targetBlock
);
169 DirValidator::Rule::~Rule()
172 Block_release(mTargetBlock
);
175 bool DirValidator::Rule::matchTarget(const char *path
, const char *target
) const
178 MacOSError::throwMe(errSecCSInternalError
);
179 string pattern
= mTargetBlock(path
, target
);
181 return true; // always match empty pattern
182 secdebug("dirval", "%s: match target %s against %s", path
, target
, pattern
.c_str());
184 if (::regcomp(&re
, pattern
.c_str(), REG_EXTENDED
| REG_NOSUB
))
185 MacOSError::throwMe(errSecCSInternalError
);
186 int rv
= ::regexec(&re
, target
, 0, NULL
, 0);
194 MacOSError::throwMe(errSecCSInternalError
);
199 } // end namespace CodeSigning
200 } // end namespace Security