tripwire-open-source/src/fs/fspropcalc.cpp

477 lines
15 KiB
C++

//
// 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 <unistd.h>
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;
}