// // The developer of the original code and/or files is Tripwire, Inc. // Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire, // Inc. Tripwire is a registered trademark of Tripwire, Inc. All rights // reserved. // // This program is free software. The contents of this file are subject // to the terms of the GNU General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version. You may redistribute it and/or modify it // only in compliance with the GNU General Public License. // // This program is distributed in the hope that it will be useful. // However, this program is distributed AS-IS WITHOUT ANY // WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS // FOR A PARTICULAR PURPOSE. Please see the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, // USA. // // Nothing in the GNU General Public License or any other license to use // the code or files shall permit you to use Tripwire's trademarks, // service marks, or other intellectual property without Tripwire's // prior written consent. // // If you have any questions, please contact Tripwire, Inc. at either // info@tripwire.org or www.tripwire.org. // /////////////////////////////////////////////////////////////////////////////// // fspropcalc.cpp #include "stdfs.h" #include "core/debug.h" #include "core/errorbucket.h" #include "core/errorbucket.h" #include "fco/fconame.h" #include "fco/fconametranslator.h" #include "fco/twfactory.h" #include "fspropcalc.h" #include "fsobject.h" #include cFSPropCalc::cFSPropCalc() : mCollAction(iFCOPropCalc::PROP_LEAVE), mCalcFlags(0), mpErrorBucket(0) { } cFSPropCalc::~cFSPropCalc() { } /////////////////////////////////////////////////////////////////////////////// // NeedsStat -- returns true if any properties are in the vector that require // a stat() call /////////////////////////////////////////////////////////////////////////////// static bool NeedsStat(const cFCOPropVector& v) { return ( v.ContainsItem(cFSPropSet::PROP_DEV) || v.ContainsItem(cFSPropSet::PROP_RDEV) || v.ContainsItem(cFSPropSet::PROP_INODE) || v.ContainsItem(cFSPropSet::PROP_MODE) || v.ContainsItem(cFSPropSet::PROP_NLINK) || v.ContainsItem(cFSPropSet::PROP_UID) || v.ContainsItem(cFSPropSet::PROP_GID) || v.ContainsItem(cFSPropSet::PROP_SIZE) || v.ContainsItem(cFSPropSet::PROP_ATIME) || v.ContainsItem(cFSPropSet::PROP_MTIME) || v.ContainsItem(cFSPropSet::PROP_CTIME) || v.ContainsItem(cFSPropSet::PROP_BLOCK_SIZE) || v.ContainsItem(cFSPropSet::PROP_BLOCKS) || v.ContainsItem(cFSPropSet::PROP_FILETYPE) || v.ContainsItem(cFSPropSet::PROP_GROWING_FILE) ); } /////////////////////////////////////////////////////////////////////////////// // GetSymLinkStr -- returns a string that represents what a symbolic link is // pointing at. If this returns false, call iFSServices::GetErrnoString() // to get the error message. /////////////////////////////////////////////////////////////////////////////// static bool GetSymLinkStr(const TSTRING& strName, cArchive& arch) { char buf[1024]; // TODO: is this big enough? #if defined(O_PATH) int fd = open(strName.c_str(), (O_PATH | O_NOFOLLOW | O_NOATIME)); int rtn = readlinkat(fd, 0, buf, 1024); close(fd); #else int rtn = readlink( strName.c_str(), buf, 1024 ); #endif if(rtn == -1) return false; // the return value is the number of characters written. arch.WriteBlob(buf, rtn); return true; } void cFSPropCalc::AddPropCalcError(const eError& e) { if(mpErrorBucket) mpErrorBucket->AddError(e); } bool cFSPropCalc::DoStat( const TSTRING& strName, cFSStatArgs& statArgs ) { cDebug d("cFSPropCalc::DoStat"); try { d.TraceDetail("---Performing Stat()\n"); iFSServices::GetInstance()->Stat(strName, statArgs); } catch(eError& e) { d.TraceError("Error getting stat info for %s : %s\n", strName.c_str(), e.GetMsg().c_str()); e.SetFatality(false); AddPropCalcError(e); return false; } catch(std::exception& e) { d.TraceError("Error getting stat info for %s : %s\n", strName.c_str(), e.what()); AddPropCalcError( eFSPropCalc( strName, e.what(), eError::NON_FATAL ) ); return false; } catch(...) { d.TraceError("Unknown error getting stat info for %s\n", strName.c_str()); AddPropCalcError( eFSPropCalc( strName, "unknown", eError::NON_FATAL ) ); return false; } return true; } bool cFSPropCalc::DoOpen( const TSTRING& strName, cFileArchive& arch ) { try { arch.OpenRead(strName.c_str(), ((mCalcFlags & iFCOPropCalc::DIRECT_IO) ? cFileArchive::FA_SCANNING | cFileArchive::FA_DIRECT : cFileArchive::FA_SCANNING) ); } catch (eError&) { AddPropCalcError( eArchiveOpen( strName, iFSServices::GetInstance()->GetErrString(), eError::NON_FATAL)); return false; } catch (std::exception& e) { AddPropCalcError( eArchiveOpen( strName, e.what(), eError::NON_FATAL ) ); return false; } catch (...) { AddPropCalcError( eArchiveOpen( strName, "unknown", eError::NON_FATAL ) ); return false; } return true; } bool cFSPropCalc::DoHash( const TSTRING& strName, cBidirArchive* pTheArch, cArchiveSigGen& asg, cFileArchive& arch ) { cDebug d("cFSPropCalc::DoHash"); try { pTheArch->Seek( 0, cBidirArchive::BEGINNING ); asg.CalculateSignatures( *pTheArch ); arch.Close(); } catch (eError& e) { d.TraceError("Error generating hashes for %s : %s\n", strName.c_str(), e.GetMsg().c_str()); e.SetFatality(false); AddPropCalcError(e); return false; } catch (std::exception& e) { d.TraceError("Error generating hashes for %s : %s\n", strName.c_str(), e.what()); AddPropCalcError( eArchiveRead( strName, e.what(), eError::NON_FATAL ) ); return false; } catch (...) { d.TraceError("Unknown error generating hashes for %s\n", strName.c_str()); AddPropCalcError( eArchiveRead( strName, "unknown", eError::NON_FATAL ) ); return false; } return true; } void cFSPropCalc::HandleStatProperties( const cFCOPropVector& propsToCheck, const cFSStatArgs& ss, cFSPropSet& propSet) { if(propsToCheck.ContainsItem(cFSPropSet::PROP_DEV)) propSet.SetDev(ss.dev); if(propsToCheck.ContainsItem(cFSPropSet::PROP_RDEV)) propSet.SetRDev(ss.rdev); if(propsToCheck.ContainsItem(cFSPropSet::PROP_INODE)) propSet.SetInode(ss.ino); if(propsToCheck.ContainsItem(cFSPropSet::PROP_MODE)) propSet.SetMode(ss.mode); if(propsToCheck.ContainsItem(cFSPropSet::PROP_NLINK)) propSet.SetNLink(ss.nlink); if(propsToCheck.ContainsItem(cFSPropSet::PROP_UID)) propSet.SetUID(ss.uid); if(propsToCheck.ContainsItem(cFSPropSet::PROP_GID)) propSet.SetGID(ss.gid); if(propsToCheck.ContainsItem(cFSPropSet::PROP_SIZE)) propSet.SetSize(ss.size); if(propsToCheck.ContainsItem(cFSPropSet::PROP_ATIME)) propSet.SetAccessTime(ss.atime); if(propsToCheck.ContainsItem(cFSPropSet::PROP_MTIME)) propSet.SetModifyTime(ss.mtime); if(propsToCheck.ContainsItem(cFSPropSet::PROP_CTIME)) propSet.SetCreateTime(ss.ctime); if(propsToCheck.ContainsItem(cFSPropSet::PROP_BLOCK_SIZE)) propSet.SetBlockSize(ss.blksize); if(propsToCheck.ContainsItem(cFSPropSet::PROP_BLOCKS)) propSet.SetBlocks(ss.blocks); if(propsToCheck.ContainsItem(cFSPropSet::PROP_GROWING_FILE)) propSet.SetGrowingFile(ss.size); if(propsToCheck.ContainsItem(cFSPropSet::PROP_FILETYPE)) { // TODO -- It _really_ bites duplicating code here and in fsdatasource.cpp // *** This _has_ to be remedied somehow! // set the file type switch(ss.mFileType) { case cFSStatArgs::TY_FILE: propSet.SetFileType(cFSPropSet::FT_FILE); break; case cFSStatArgs::TY_DIR: propSet.SetFileType(cFSPropSet::FT_DIR); break; case cFSStatArgs::TY_BLOCKDEV: propSet.SetFileType(cFSPropSet::FT_BLOCKDEV); break; case cFSStatArgs::TY_CHARDEV: propSet.SetFileType(cFSPropSet::FT_CHARDEV); break; case cFSStatArgs::TY_SYMLINK: propSet.SetFileType(cFSPropSet::FT_SYMLINK); break; case cFSStatArgs::TY_FIFO: propSet.SetFileType(cFSPropSet::FT_FIFO); break; case cFSStatArgs::TY_SOCK: propSet.SetFileType(cFSPropSet::FT_SOCK); break; case cFSStatArgs::TY_DOOR: propSet.SetFileType(cFSPropSet::FT_DOOR); break; case cFSStatArgs::TY_PORT: propSet.SetFileType(cFSPropSet::FT_PORT); break; default: // set it to invalid propSet.SetFileType(cFSPropSet::FT_INVALID); } } } void cFSPropCalc::HandleHashes( const cFCOPropVector& propsToCheck, const TSTRING& strName, cFSPropSet& propSet) { bool hash_success = false; // if the file type is not a regular file, we will // not try to open the file for signature generation ASSERT( propSet.GetValidVector().ContainsItem(cFSPropSet::PROP_FILETYPE) ); if( propSet.GetFileType() == cFSPropSet::FT_FILE || propSet.GetFileType() == cFSPropSet::FT_SYMLINK ) { if( // if we need to open the file propsToCheck.ContainsItem(cFSPropSet::PROP_CRC32) || propsToCheck.ContainsItem(cFSPropSet::PROP_MD5) || propsToCheck.ContainsItem(cFSPropSet::PROP_SHA) || propsToCheck.ContainsItem(cFSPropSet::PROP_HAVAL) ) { cFileArchive arch; cMemoryArchive memArch; cBidirArchive* pTheArch; hash_success = true; if(propSet.GetFileType() == cFSPropSet::FT_SYMLINK) { pTheArch = &memArch; if(! GetSymLinkStr(strName, memArch)) { // add it to the bucket... AddPropCalcError( eArchiveOpen( strName, iFSServices::GetInstance()->GetErrString(), eError::NON_FATAL ) ); hash_success = false; } } else { pTheArch = &arch; hash_success = DoOpen(strName, arch); } // // if we have successfully initialized the archive // if (hash_success) { cArchiveSigGen asg; if(propsToCheck.ContainsItem(cFSPropSet::PROP_CRC32)) { propSet.SetDefinedCRC32(true); asg.AddSig( propSet.GetCRC32() ); } if(propsToCheck.ContainsItem(cFSPropSet::PROP_MD5)) { propSet.SetDefinedMD5(true); asg.AddSig( propSet.GetMD5() ); } if(propsToCheck.ContainsItem(cFSPropSet::PROP_SHA)) { propSet.SetDefinedSHA(true); asg.AddSig( propSet.GetSHA() ); } if(propsToCheck.ContainsItem(cFSPropSet::PROP_HAVAL)) { propSet.SetDefinedHAVAL(true); asg.AddSig( propSet.GetHAVAL() ); } // // calculate the signatures // hash_success = DoHash(strName, pTheArch, asg, arch); } } } if (!hash_success) { // We can't calculate signatures, set them to undefined if (propsToCheck.ContainsItem(cFSPropSet::PROP_CRC32)) propSet.SetDefinedCRC32(false); if (propsToCheck.ContainsItem(cFSPropSet::PROP_MD5)) propSet.SetDefinedMD5(false); if (propsToCheck.ContainsItem(cFSPropSet::PROP_SHA)) propSet.SetDefinedSHA(false); if (propsToCheck.ContainsItem(cFSPropSet::PROP_HAVAL)) propSet.SetDefinedHAVAL(false); } } /////////////////////////////////////////////////////////////////////////////// // VisitFSObject -- this is the workhorse method that actually fills out the // passed in FSObject' properties /////////////////////////////////////////////////////////////////////////////// void cFSPropCalc::VisitFSObject(cFSObject& obj) { cDebug d("cFSPropCalc::VisitFSObject"); d.TraceDetail(_T("Visiting %s\n"), obj.GetName().AsString().c_str()); // if we are not in overwrite mode, we need to alter the // properties we are calculating... cFCOPropVector propsToCheck(mPropVector); if(mCollAction == iFCOPropCalc::PROP_LEAVE) { cFCOPropVector inBoth = propsToCheck; inBoth &= obj.GetPropSet()->GetValidVector(); propsToCheck ^= inBoth; } #ifdef _DEBUG d.TraceDetail("----->Collision Action = %s\n", mCollAction == iFCOPropCalc::PROP_LEAVE ? "Leave" : "Replace"); d.TraceDetail("----->Object's valid properties (a):\n"); obj.GetPropSet()->GetValidVector().TraceContents(cDebug::D_DETAIL); d.TraceDetail("----->Properties to calculate: (b)\n"); mPropVector.TraceContents(cDebug::D_DETAIL); d.TraceDetail("----->Properties to change in object ((a&b)^b for Leave or b for Replace):\n"); propsToCheck.TraceContents(cDebug::D_DETAIL); #endif //_DEBUG // only do the stat() if it is necessary cFSStatArgs ss; TSTRING strName = iTWFactory::GetInstance()->GetNameTranslator()->ToStringAPI( obj.GetName() ); // get a reference to the fco's property set cFSPropSet& propSet = obj.GetFSPropSet(); // // just return if this object is invalid // if( propSet.GetFileType() == cFSPropSet::FT_INVALID ) return; if( NeedsStat(propsToCheck) ) { if (!DoStat(strName, ss)) return; HandleStatProperties(propsToCheck, ss, propSet); } HandleHashes(propsToCheck, strName, propSet); } void cFSPropCalc::SetPropVector(const cFCOPropVector& pv) { mPropVector = pv; } const cFCOPropVector& cFSPropCalc::GetPropVector() const { return mPropVector; } iFCOVisitor* cFSPropCalc::GetVisitor() { return this; } const iFCOVisitor* cFSPropCalc::GetVisitor() const { return this; } void cFSPropCalc::SetErrorBucket(cErrorBucket* pBucket) { mpErrorBucket = pBucket; } const cErrorBucket* cFSPropCalc::GetErrorBucket() const { return mpErrorBucket; } iFCOPropCalc::CollisionAction cFSPropCalc::GetCollisionAction() const { return mCollAction; } void cFSPropCalc::SetCollisionAction(CollisionAction a) { ASSERT((a == iFCOPropCalc::PROP_OVERWRITE) || (a == PROP_LEAVE)); mCollAction = a; }