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