tripwire-open-source/src/tripwire/policyupdate.cpp

313 lines
12 KiB
C++

//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000-2018 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.
//
// policyupdate.cpp
#include "stdtripwire.h"
#include "policyupdate.h"
#include "tw/fcoreport.h"
#include "core/errorbucketimpl.h"
#include "integritycheck.h"
#include "updatedb.h"
#include "core/usernotify.h"
#include "fco/iterproxy.h"
#include "fco/fcopropset.h"
#include "tripwireutil.h"
#include "tripwirestrings.h"
#include "fco/twfactory.h"
#include "fco/fconametranslator.h"
#include "fco/genreswitcher.h"
///////////////////////////////////////////////////////////////////////////////
// util_FCOInSpecList -- given a spec list and an fco name, return true if the
// name belongs in one of the specs
///////////////////////////////////////////////////////////////////////////////
static bool util_FCOInSpecList(const cFCOSpecList& specList, const cFCOName& name)
{
cFCOSpecListCanonicalIter iter(specList);
for (iter.SeekBegin(); !iter.Done(); iter.Next())
{
if (iter.Spec()->SpecContainsFCO(name))
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// util_PruneExtraObjects -- checks every object in the database against the
// provided spec set and adds them to the vector if they don't fall under any specs.
///////////////////////////////////////////////////////////////////////////////
static void util_PruneExtraObjects(cDbDataSourceIter iter, cFCOSpecListCanonicalIter specIter, bool bFirst = true)
{
if (!bFirst)
{
iter.Descend();
}
for (iter.SeekBegin(); !iter.Done(); /*iter.Next()*/)
{
// process our children first...
//
if (iter.CanDescend())
{
util_PruneExtraObjects(iter, specIter, false);
}
if (iter.HasFCOData())
{
// figure out if this fco fits in the new policy
//
bool bFoundSpec = false;
for (specIter.SeekBegin(); !specIter.Done(); specIter.Next())
{
if (specIter.Spec()->SpecContainsFCO(iter.GetName()))
{
// this doesn't belong in the new db; remove it!
//
bFoundSpec = true;
break;
}
}
if (!bFoundSpec)
{
TW_NOTIFY_VERBOSE(
_T("--- %s%s\n"),
TSS_GetString(cTripwire, tripwire::STR_NOTIFY_DB_REMOVING).c_str(),
iTWFactory::GetInstance()->GetNameTranslator()->ToStringDisplay(iter.GetName()).c_str());
iter.RemoveFCOData();
}
}
//
// remove the infrastructure, if appropriate...
// this is also where the iterator incrementing occurs, so we need to
// be careful with the Next()s
//
if (!iter.HasFCOData())
{
if (iter.CanDescend() && iter.CanRemoveChildArray())
iter.RemoveChildArray();
if (!iter.CanDescend())
iter.RemoveFCO();
else
iter.Next();
}
else
iter.Next();
}
}
///////////////////////////////////////////////////////////////////////////////
// ctor
///////////////////////////////////////////////////////////////////////////////
cPolicyUpdate::cPolicyUpdate(cGenre::Genre genreNum,
const cFCOSpecList& oldPolicy,
const cFCOSpecList& newPolicy,
cHierDatabase& db,
cErrorBucket* pBucket)
: mOldPolicy(oldPolicy), mNewPolicy(newPolicy), mDb(db), mGenre(genreNum), mpBucket(pBucket)
{
}
///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
bool cPolicyUpdate::Execute(uint32_t flags) // throw (eError)
{
// here is my current idea for the algorithm: first, do an integrity check with the new policy on the database and
// a special flag passed to Execute() to modify what properties are checked. Then, take the resulting
// report print to the user its contents, minus any violations that don't make sense to report (see my EPS for
// details on what doesn't get displayed) Finally, do a database update with the full report, making sure that
// properties that were from the policy are also removed from existing FCOs in the database ( I might need to
// add a parameter to DbUpdate to accomodate that).
//
// first, do the integrity check with the new database...
//
TW_NOTIFY_NORMAL(TSS_GetString(cTripwire, tripwire::STR_PU_PROCESSING_GENRE).c_str(),
cGenreSwitcher::GetInstance()->GenreToString(cGenreSwitcher::GetInstance()->CurrentGenre(), true));
TW_NOTIFY_NORMAL(TSS_GetString(cTripwire, tripwire::STR_PU_INTEGRITY_CHECK).c_str());
//
iFCONameTranslator* pTrans = iTWFactory::GetInstance()->GetNameTranslator();
bool bResult = true; // begin by assuming success
cFCOReport report;
cIntegrityCheck ic(mGenre, mNewPolicy, mDb, report, mpBucket);
//
// set up flags for the property calculator and iterators
//
uint32_t icFlags = cIntegrityCheck::FLAG_COMPARE_VALID_PROPS_ONLY | cIntegrityCheck::FLAG_INVALIDATE_EXTRA_DB_PROPS |
cIntegrityCheck::FLAG_SET_NEW_PROPS;
if (flags & FLAG_ERASE_FOOTPRINTS_PU)
{
icFlags |= cIntegrityCheck::FLAG_ERASE_FOOTPRINTS_IC;
}
if (flags & FLAG_DIRECT_IO)
{
icFlags |= cIntegrityCheck::FLAG_DIRECT_IO;
}
ic.Execute(icFlags);
//TODO-- the second flag I just added probably makes the flag to cUpdateDb::Execute() unnecessary;
// I should probably remove it.
//
// Note that I will probably end up making two passes through the report (one for reporting to the user and one for
// the db update). I don't think there is a clean way to do this in a single report pass, other than rewriting
// db update, which I am not going to do.
//
cFCOReportSpecIter specIter(report, mGenre);
for (specIter.SeekBegin(); !specIter.Done(); specIter.Next())
{
// notify the user of what we are doing...
TW_NOTIFY_VERBOSE(_T("%s%s\n"),
TSS_GetString(cTripwire, tripwire::STR_NOTIFY_PROCESSING).c_str(),
pTrans->ToStringDisplay(specIter.GetSpec()->GetStartPoint()).c_str());
//
// report objects in the added set should only be reported if they fall in a spec
// in the old policy
//
iFCOSet* pAddSet = specIter.GetAddedSet();
const cIterProxy<iFCOIter> pIter = pAddSet->GetIter();
for (pIter->SeekBegin(); !pIter->Done(); pIter->Next())
{
if (util_FCOInSpecList(mOldPolicy, pIter->FCO()->GetName()))
{
// this is an error that should be reported to the user.
ePolicyUpdateAddedFCO e(pTrans->ToStringDisplay(pIter->FCO()->GetName()));
if ((flags & FLAG_SECURE_MODE) == 0)
e.SetFlags(eError::NON_FATAL);
else
e.SetFlags(eError::SUPRESS_THIRD_MSG);
mpBucket->AddError(e);
bResult = false;
}
}
//
// objects in the removed set should only be reported if they fall in a spec in the
// new policy..
//
iFCOSet* pRmSet = specIter.GetRemovedSet();
const cIterProxy<iFCOIter> pRmIter = pRmSet->GetIter();
for (pRmIter->SeekBegin(); !pRmIter->Done(); pRmIter->Next())
{
if (util_FCOInSpecList(mNewPolicy, pRmIter->FCO()->GetName()))
{
// this is an error that should be reported to the user.
ePolicyUpdateRemovedFCO e(pTrans->ToStringDisplay(pRmIter->FCO()->GetName()));
if ((flags & FLAG_SECURE_MODE) == 0)
e.SetFlags(eError::NON_FATAL);
else
e.SetFlags(eError::SUPRESS_THIRD_MSG);
mpBucket->AddError(e);
bResult = false;
}
}
//
// every object in the changed set should be document....
//
cFCOReportChangeIter changeIter(specIter);
for (changeIter.SeekBegin(); !changeIter.Done(); changeIter.Next())
{
//----------------------------------------------------------------
// I need to construct a string that contains all of the
// property names that have changed.
//----------------------------------------------------------------
TSTRING badPropStr = TSS_GetString(cTripwire, tripwire::STR_PU_BAD_PROPS);
badPropStr += pTrans->ToStringDisplay(changeIter.GetOld()->GetName());
const cFCOPropVector& changeVector = changeIter.GetChangeVector();
for (int i = 0; i < changeVector.GetSize(); i++)
{
if (changeVector.ContainsItem(i))
{
badPropStr += _T("\n> ");
badPropStr += changeIter.GetOld()->GetPropSet()->GetPropName(i);
}
}
// add this to the error bucket
ePolicyUpdateChangedFCO e(badPropStr);
if ((flags & FLAG_SECURE_MODE) == 0)
e.SetFlags(eError::NON_FATAL);
else
e.SetFlags(eError::SUPRESS_THIRD_MSG);
mpBucket->AddError(e);
bResult = false;
}
}
//
// now, we will update the database with everything in the report...
// TODO -- don't do this if the secure mode flag was passed in
//
TW_NOTIFY_NORMAL(TSS_GetString(cTripwire, tripwire::STR_PU_UPDATE_DB).c_str());
//
cUpdateDb update(mDb, report, mpBucket);
uint32_t updateDBFlags = cUpdateDb::FLAG_REPLACE_PROPS;
if (flags & FLAG_ERASE_FOOTPRINTS_PU)
{
updateDBFlags |= cUpdateDb::FLAG_ERASE_FOOTPRINTS_UD;
}
update.Execute(updateDBFlags);
// the last thing that we have to do is to remove everything that is still
// in the database that does not belong in the new database (ie -- does not fall under any
// new rules)
//
// TODO -- is there any way to do this that does not involve iterating over the entire database?
// TODO -- I should probably write a general-purpose database iterator class to do this...
//
TW_NOTIFY_NORMAL(TSS_GetString(cTripwire, tripwire::STR_PU_PRUNING).c_str());
//
cDbDataSourceIter i(&mDb);
i.SetErrorBucket(mpBucket);
if (flags & FLAG_ERASE_FOOTPRINTS_PU)
{
i.SetIterFlags(iFCODataSourceIter::DO_NOT_MODIFY_OBJECTS);
}
const cFCOSpecListCanonicalIter newPolIter(mNewPolicy);
util_PruneExtraObjects(i, newPolIter);
return bResult;
}