313 lines
12 KiB
C++
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;
|
|
}
|