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

2803 lines
106 KiB
C++

//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000-2017 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.
//
///////////////////////////////////////////////////////////////////////////////
// twcmdline.cpp
#include "stdtripwire.h"
#include "twcmdline.h"
#include "core/cmdlineparser.h"
#include "tw/configfile.h"
#include "core/serializerimpl.h" // cSerializerImpl
#include "core/archive.h" // cArchive and friends
#include "fco/fcospeclist.h" // cFCOSpecList
#include "tripwirestrings.h" // tripwire.exe specific strings
#include "twcrypto/keyfile.h" // cKeyFile -- used for encryption
#include "tw/twutil.h"
#include "twcmdlineutil.h" // utility functions for the cmd line
#include "tw/fcoreport.h" // for reports
#include "tw/textreportviewer.h" // text report viewer
#include "core/usernotify.h" // for notifying the user of events
#include "tw/fcoreportutil.h" // for finalizing report
#include "tw/fcodatabaseutil.h" // for finalizing database
#include "tw/headerinfo.h" // for finalizing database
#include "fco/fcopropdisplayer.h"
#include "tw/systeminfo.h"
#include "tw/filemanipulator.h"
#include "fco/twfactory.h"
#include "mailmessage.h" // used for email reporting
#include "fco/genrespeclist.h"
#include "core/twlimits.h" // for severity limits
#include "core/errorgeneral.h"
#include "core/corestrings.h"
#include "tw/twerrors.h"
#include "core/stringutil.h"
#include "util/fileutil.h"
#include "tw/twstrings.h"
#include "syslog_trip.h"
#include <set>
#include "fco/parsergenreutil.h" // this is needed to figure out if a path is fully qualified for the current genre.
#include "tw/fcodatabasefile.h"
#include "fco/signature.h"
#include "fco/genreswitcher.h"
#include "generatedb.h"
#include "integritycheck.h"
#include "updatedb.h"
#include "policyupdate.h"
#include "core/platform.h"
#ifdef TW_PROFILE
#include "tasktimer.h"
#endif
#include "fs/fsdatasourceiter.h" // for cross file systems flag
#include <unistd.h> // for _exit()
//-----------------------------------------------------------------------------
// #defines
//-----------------------------------------------------------------------------
// this is used to make required condition checking in the Init() functions a little more compact
#define TEST_INIT_REQUIREMENT(t, c, n)\
if(! t)\
{\
cTWUtil::PrintErrorMsg(eTWInitialization(TSS_GetString( c, n ), 0)); \
return false;\
}
static bool util_GetEditor( TSTRING& strEd );
//#############################################################################
// cTWCmdLine
//#############################################################################
///////////////////////////////////////////////////////////////////////////////
// GetMode -- processes the command line arguments and creates an appropriate
// structure for the selected mode, or NULL if an error occurs
///////////////////////////////////////////////////////////////////////////////
iTWMode* cTWCmdLine::GetMode(int argc, const TCHAR *const * argv)
{
// note -- it is assumed the executable name is the first parameter
if(argc < 2)
{
TCOUT << TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_VERSION) << std::endl;
TCOUT << TSS_GetString( cTW, tw::STR_VERSION) << std::endl;
TCOUT << TSS_GetString( cTW, tw::STR_GET_HELP) << std::endl;
return NULL;
}
int mode = MODE;
const TCHAR* pcMode;
if(_tcscmp(argv[1], _T("-m")) == 0)
{
if(argc < 3)
{
TCOUT << TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_VERSION) << std::endl;
TCOUT << TSS_GetString( cTW, tw::STR_VERSION) << std::endl;
TCOUT << TSS_GetString( cTW, tw::STR_ERR_NO_MODE) << std::endl;
TCOUT << TSS_GetString( cTW, tw::STR_GET_HELP) << std::endl;
return NULL;
}
pcMode = argv[2];
if (_tcscmp(argv[2], _T("i")) == 0)
mode = MODE_INIT;
else if (_tcscmp(argv[2], _T("c")) == 0)
mode = MODE_CHECK;
else if (_tcscmp(argv[2], _T("u")) == 0)
mode = MODE_UPDATE_DB;
else if (_tcscmp(argv[2], _T("p")) == 0)
mode = MODE_UPDATE_POL;
else if (_tcscmp(argv[2], _T("t")) == 0)
mode = MODE_TEST;
}
else
{
pcMode = argv[1];
if (_tcscmp(argv[1], _T("--init")) == 0)
mode = MODE_INIT;
else if (_tcscmp(argv[1], _T("--check")) == 0)
mode = MODE_CHECK;
else if (_tcscmp(argv[1], _T("--update")) == 0)
mode = MODE_UPDATE_DB;
else if (_tcscmp(argv[1], _T("--update-policy")) == 0)
mode = MODE_UPDATE_POL;
else if (_tcscmp(argv[1], _T("--version")) == 0)
mode = MODE_VERSION;
else if (_tcscmp(argv[1], _T("--help")) == 0 || _tcscmp(argv[1], _T("-?")) == 0)
mode = MODE_HELP;
else if (_tcscmp(argv[1], _T("--test")) == 0)
mode = MODE_TEST;
#ifdef DEBUG
if (_tcscmp(argv[1], _T("--explore")) == 0)
mode = MODE_EXPLORE;
if (_tcscmp(argv[1], _T("--verifydb")) == 0)
mode = MODE_DEBUG;
#endif
}
if(mode == MODE)
{
// unknown mode switch
cDebug d("cTWCmdLine::GetMode");
d.TraceError(_T("Error: Bad mode switch: %s\n"), pcMode);
TCERR << TSS_GetString( cTW, tw::STR_UNKNOWN_MODE_SPECIFIED)
<< pcMode << std::endl
<< TSS_GetString( cTW, tw::STR_GET_HELP) << std::endl;
return NULL;
}
iTWMode* pRtn = 0;
switch(mode)
{
case cTWCmdLine::MODE_INIT:
pRtn = new cTWModeDbInit;
break;
case cTWCmdLine::MODE_CHECK:
pRtn = new cTWModeIC;
break;
case cTWCmdLine::MODE_UPDATE_DB:
pRtn = new cTWModeDbUpdate;
break;
case cTWCmdLine::MODE_UPDATE_POL:
pRtn = new cTWModePolUpdate;
break;
case cTWCmdLine::MODE_TEST:
pRtn = new cTWModeTest;
break;
case cTWCmdLine::MODE_HELP:
pRtn = new cTWModeHelp;
break;
case cTWCmdLine::MODE_VERSION:
pRtn = new cTWModeVersion;
break;
//Explore and Debug modes are invisible unless DEBUG is defined.
#ifdef DEBUG
case cTWCmdLine::MODE_EXPLORE:
pRtn = new cTWModeExploreDb;
break;
case cTWCmdLine::MODE_DEBUG:
pRtn = new cTWModeDebugDb;
break;
#endif
default:
ASSERT(false);
}
return pRtn;
}
///////////////////////////////////////////////////////////////////////////////
// InitCmdLineCommon -- initializes the command line switches common to all
// modes
///////////////////////////////////////////////////////////////////////////////
static void InitCmdLineCommon(cCmdLineParser& parser)
{
parser.AddArg(cTWCmdLine::HELP, TSTRING(_T("?")), TSTRING(_T("help")), cCmdLineParser::PARAM_NONE);
parser.AddArg(cTWCmdLine::MODE, TSTRING(_T("m")), TSTRING(_T("")), cCmdLineParser::PARAM_ONE);
parser.AddArg(cTWCmdLine::VERBOSE, TSTRING(_T("v")), TSTRING(_T("verbose")), cCmdLineParser::PARAM_NONE);
parser.AddArg(cTWCmdLine::SILENT, TSTRING(_T("s")), TSTRING(_T("silent")), cCmdLineParser::PARAM_NONE);
parser.AddArg(cTWCmdLine::SILENT, TSTRING(_T("")), TSTRING(_T("quiet")), cCmdLineParser::PARAM_NONE);
//parser.AddArg(cTWCmdLine::NO_BACKUP, TSTRING(_T("B")), TSTRING(_T("nobackup")), cCmdLineParser::PARAM_NONE);
// this was dropped from 1.5 -- mdb
parser.AddArg(cTWCmdLine::LOCAL_PASSPHRASE, TSTRING(_T("P")), TSTRING(_T("local-passphrase")),cCmdLineParser::PARAM_ONE);
parser.AddArg(cTWCmdLine::POL_FILE, TSTRING(_T("p")), TSTRING(_T("polfile")), cCmdLineParser::PARAM_ONE);
parser.AddArg(cTWCmdLine::CFG_FILE, TSTRING(_T("c")), TSTRING(_T("cfgfile")), cCmdLineParser::PARAM_ONE);
parser.AddArg(cTWCmdLine::SITE_KEY_FILE, TSTRING(_T("S")), TSTRING(_T("site-keyfile")), cCmdLineParser::PARAM_ONE);
parser.AddArg(cTWCmdLine::LOCAL_KEY_FILE, TSTRING(_T("L")), TSTRING(_T("local-keyfile")), cCmdLineParser::PARAM_ONE);
parser.AddArg(cTWCmdLine::DB_FILE, TSTRING(_T("d")), TSTRING(_T("dbfile")), cCmdLineParser::PARAM_ONE);
parser.AddArg(cTWCmdLine::REPORT_FILE, TSTRING(_T("r")), TSTRING(_T("twrfile")), cCmdLineParser::PARAM_ONE);
//
// mutual exclusions
//
parser.AddMutEx(cTWCmdLine::VERBOSE, cTWCmdLine::SILENT);
}
///////////////////////////////////////////////////////////////////////////////
// FillOutConfigInfo -- fills out all the common info with config file information
///////////////////////////////////////////////////////////////////////////////
static void FillOutConfigInfo(cTWModeCommon* pModeInfo, const cConfigFile& cf)
{
TSTRING str;
if(cf.Lookup(TSTRING(_T("POLFILE")), str))
{
pModeInfo->mPolFile = str;
}
if(cf.Lookup(TSTRING(_T("DBFILE")), str))
{
pModeInfo->mDbFile = str;
}
if(cf.Lookup(TSTRING(_T("SITEKEYFILE")), str))
{
pModeInfo->mSiteKeyFile = str;
}
if(cf.Lookup(TSTRING(_T("LOCALKEYFILE")), str))
{
pModeInfo->mLocalKeyFile = str;
}
if(cf.Lookup(TSTRING(_T("REPORTFILE")), str))
{
pModeInfo->mReportFile = str;
}
if(cf.Lookup(TSTRING(_T("EDITOR")), str))
{
pModeInfo->mEditor = str;
}
if(cf.Lookup(TSTRING(_T("LATEPROMPTING")), str))
{
if (_tcsicmp(str.c_str(), _T("true")) == 0)
pModeInfo->mbLatePassphrase = true;
}
if(cf.Lookup(TSTRING(_T("RESETACCESSTIME")), str))
{
// We do not support reset access time on Unix, so we issue a warning.
// This used to be a fatal error, however this prevents
// cross platform config files.
cTWUtil::PrintErrorMsg(eTWInvalidConfigFileKey(_T("RESETACCESSTIME"), eError::NON_FATAL));
}
if(cf.Lookup(TSTRING(_T("LOOSEDIRECTORYCHECKING")), str))
{
if (_tcsicmp(str.c_str(), _T("true")) == 0)
pModeInfo->mfLooseDirs = true;
}
TSTRING temp_directory;
cf.Lookup(TSTRING(_T("TEMPDIRECTORY")), temp_directory);
if (temp_directory.empty())
{
#if IS_AROS
temp_directory = "T:";
#else
temp_directory = "/tmp/";
#endif
}
// make sure we have a trailing slash -- thanks Jarno...
//
if (*temp_directory.rbegin() != '/')
{
temp_directory.push_back('/');
}
// make sure it exists...
//
#if USES_DEVICE_PATH
temp_directory = cDevicePath::AsNative(temp_directory);
#endif
if (access(temp_directory.c_str(), F_OK) != 0)
{
TSTRING errStr = TSS_GetString( cCore, core::STR_BAD_TEMPDIRECTORY );
TSTRING tmpStr = _T("Directory: ");
tmpStr += (temp_directory + _T("\n"));
tmpStr += errStr;
throw eTWInvalidTempDirectory(tmpStr);
}
else
{
iFSServices::GetInstance()->SetTempDirName(temp_directory);
}
if (cf.Lookup(TSTRING(_T("GLOBALEMAIL")), str))
{
if (str.length() != 0)
pModeInfo->mGlobalEmail = str;
}
//
// Set the report-viewing level if one has been specified, use
// default level otherwise.
//
if(cf.Lookup(TSTRING(_T("EMAILREPORTLEVEL")), str))
{
if (_tcsicmp(str.c_str(), _T("0")) == 0)
pModeInfo->mEmailReportLevel = cTextReportViewer::SINGLE_LINE;
else if (_tcsicmp(str.c_str(), _T("1")) == 0)
pModeInfo->mEmailReportLevel = cTextReportViewer::PARSEABLE;
else if (_tcsicmp(str.c_str(), _T("2")) == 0)
pModeInfo->mEmailReportLevel = cTextReportViewer::SUMMARY_ONLY;
else if (_tcsicmp(str.c_str(), _T("3")) == 0)
pModeInfo->mEmailReportLevel = cTextReportViewer::CONCISE_REPORT;
else if (_tcsicmp(str.c_str(), _T("4")) == 0)
pModeInfo->mEmailReportLevel = cTextReportViewer::FULL_REPORT;
else
{
// They specified an illegal level, error.
TSTRING errStr = _T("Invalid Level: ");
errStr += str;
throw eTWInvalidReportLevelCfg( errStr );
}
}
else
{
// no level was specified in the configuration file, use default.
pModeInfo->mEmailReportLevel = cTextReportViewer::CONCISE_REPORT;
}
// Decide what mail method should be used to email reports
if(cf.Lookup(TSTRING(_T("MAILMETHOD")), str))
{
if (_tcsicmp(str.c_str(), _T("SENDMAIL")) == 0)
pModeInfo->mMailMethod = cMailMessage::MAIL_BY_PIPE;
else if( _tcsicmp( str.c_str(), _T("SMTP") ) == 0 )
pModeInfo->mMailMethod = cMailMessage::MAIL_BY_SMTP;
else
pModeInfo->mMailMethod = cMailMessage::INVALID_METHOD;
}
else
{
pModeInfo->mMailMethod = cMailMessage::NO_METHOD;
}
#if !SUPPORTS_NETWORKING
if (pModeInfo->mMailMethod == cMailMessage::MAIL_BY_SMTP)
throw eMailSMTPNotSupported();
#endif
// Get the SMTP server
if(cf.Lookup(TSTRING(_T("SMTPHOST")), str))
{
pModeInfo->mSmtpHost = str;
}
else
{
pModeInfo->mSmtpHost = _T("127.0.0.1"); // this is the default
}
// Get the SMTP port number
if(cf.Lookup(TSTRING(_T("SMTPPORT")), str))
{
int i = _ttoi( str.c_str() );
if( i < 0 || i > SHRT_MAX )
throw eTWInvalidPortNumber( str );
pModeInfo->mSmtpPort = static_cast<unsigned short>( i );
}
else
{
pModeInfo->mSmtpPort = 25; // this is the default
}
// Get the mail program to use if we're piping our email
if(cf.Lookup(TSTRING(_T("MAILPROGRAM")), str))
{
pModeInfo->mMailProgram = str;
}
else
{
pModeInfo->mMailProgram.erase(); // MAILPROGRAM is not required to be specified
}
// Get the mail program to use if we're piping our email
if(cf.Lookup(TSTRING(_T("MAILNOVIOLATIONS")), str))
{
if (_tcsicmp(str.c_str(), _T("true")) == 0)
pModeInfo->mMailNoViolations = true;
else
pModeInfo->mMailNoViolations = false;
}
else
{
pModeInfo->mMailNoViolations = true; // MAILPROGRAM is not required to be specified
}
if(cf.Lookup(TSTRING(_T("MAILFROMADDRESS")), str))
{
pModeInfo->mMailFrom = str;
}
// SYSLOG reporting
if(cf.Lookup(TSTRING(_T("SYSLOGREPORTING")), str))
{
#if SUPPORTS_SYSLOG
if (_tcsicmp(str.c_str(), _T("true")) == 0)
pModeInfo->mbLogToSyslog = true;
else
pModeInfo->mbLogToSyslog = false;
#else
throw eTWSyslogNotSupported();
#endif
}
else
{
pModeInfo->mbLogToSyslog = false;
}
// Crossing file systems
if(cf.Lookup(TSTRING(_T("CROSSFILESYSTEMS")), str))
{
if (_tcsicmp(str.c_str(), _T("true")) == 0)
pModeInfo->mbCrossFileSystems = true;
else
pModeInfo->mbCrossFileSystems = false;
}
if (cf.Lookup(TSTRING(_T("HASH_DIRECT_IO")), str))
{
#if SUPPORTS_DIRECT_IO
if (_tcsicmp(str.c_str(), _T("true")) == 0)
{
pModeInfo->mbDirectIO = true;
cArchiveSigGen::SetUseDirectIO(true);
}
#else
throw eTWDirectIONotSupported();
#endif
}
if(cf.Lookup(TSTRING(_T("RESOLVE_IDS_TO_NAMES")), str))
{
if (_tcsicmp(str.c_str(), _T("true")) == 0)
iFSServices::GetInstance()->SetResolveNames(true);
else
iFSServices::GetInstance()->SetResolveNames(false);
}
//
// turn all of the file names into full paths (they're relative to the exe dir)
//
TSTRING fullPath;
if(iFSServices::GetInstance()->FullPath( fullPath, pModeInfo->mPolFile, cSystemInfo::GetExeDir() ))
pModeInfo->mPolFile = fullPath;
if(iFSServices::GetInstance()->FullPath( fullPath, pModeInfo->mDbFile, cSystemInfo::GetExeDir() ))
pModeInfo->mDbFile = fullPath;
if(iFSServices::GetInstance()->FullPath( fullPath, pModeInfo->mSiteKeyFile, cSystemInfo::GetExeDir() ))
pModeInfo->mSiteKeyFile = fullPath;
if(iFSServices::GetInstance()->FullPath( fullPath, pModeInfo->mLocalKeyFile, cSystemInfo::GetExeDir() ))
pModeInfo->mLocalKeyFile = fullPath;
if(iFSServices::GetInstance()->FullPath( fullPath, pModeInfo->mReportFile, cSystemInfo::GetExeDir() ))
pModeInfo->mReportFile = fullPath;
}
///////////////////////////////////////////////////////////////////////////////
// FillOutCmdLineInfo -- fills out info common to all modes that appears on the
// command line.
///////////////////////////////////////////////////////////////////////////////
static void FillOutCmdLineInfo(cTWModeCommon* pModeInfo, const cCmdLineParser& cmdLine)
{
cCmdLineIter iter(cmdLine);
for(iter.SeekBegin(); ! iter.Done(); iter.Next())
{
switch(iter.ArgId())
{
case cTWCmdLine::VERBOSE:
pModeInfo->mVerbosity = 2;
break;
case cTWCmdLine::SILENT:
pModeInfo->mVerbosity = 0;
break;
case cTWCmdLine::POL_FILE:
ASSERT(iter.NumParams() > 0); // should be caught by cmd line parser
pModeInfo->mPolFile = iter.ParamAt(0);
break;
case cTWCmdLine::SITE_KEY_FILE:
ASSERT(iter.NumParams() > 0); // should be caught by cmd line parser
pModeInfo->mSiteKeyFile = iter.ParamAt(0);
break;
case cTWCmdLine::LOCAL_KEY_FILE:
ASSERT(iter.NumParams() > 0); // should be caught by cmd line parser
pModeInfo->mLocalKeyFile = iter.ParamAt(0);
break;
case cTWCmdLine::DB_FILE:
ASSERT(iter.NumParams() > 0); // should be caught by cmd line parser
pModeInfo->mDbFile = iter.ParamAt(0);
break;
case cTWCmdLine::REPORT_FILE:
ASSERT(iter.NumParams() > 0); // should be caught by cmd line parser
pModeInfo->mReportFile = iter.ParamAt(0);
break;
case cTWCmdLine::LOCAL_PASSPHRASE:
{
ASSERT(iter.NumParams() > 0); // should be caught by cmd line parser
pModeInfo->mLocalPassphrase = cStringUtil::TstrToWstr(iter.ParamAt(0));
pModeInfo->mLocalProvided = true;
}
break;
/* case cTWCmdLine::NO_BACKUP:
pModeInfo->mbBackup = false;
break;
*/ }
}
//
// turn all of the file names into full paths for nice presentation to the user.
//
TSTRING fullPath;
if(iFSServices::GetInstance()->FullPath(fullPath, pModeInfo->mPolFile))
pModeInfo->mPolFile = fullPath;
if(iFSServices::GetInstance()->FullPath(fullPath, pModeInfo->mSiteKeyFile))
pModeInfo->mSiteKeyFile = fullPath;
if(iFSServices::GetInstance()->FullPath(fullPath, pModeInfo->mLocalKeyFile))
pModeInfo->mLocalKeyFile = fullPath;
if(iFSServices::GetInstance()->FullPath(fullPath, pModeInfo->mDbFile))
pModeInfo->mDbFile = fullPath;
if(iFSServices::GetInstance()->FullPath(fullPath, pModeInfo->mReportFile))
pModeInfo->mReportFile = fullPath;
// use the verbosity information
ASSERT((pModeInfo->mVerbosity >= 0) && (pModeInfo->mVerbosity < 3));
iUserNotify::GetInstance()->SetVerboseLevel(pModeInfo->mVerbosity);
// if a passphrase was passed on the command line, then late prompting should
// be set to false so that wrong passwords are detected immediately
if( pModeInfo->mLocalProvided && pModeInfo->mbLatePassphrase )
{
// TODO -- I don't know if we should display this or not -- mdb
//iUserNotify::GetInstance()->Notify( iUserNotify::V_VERBOSE, TSS_GetString( cTripwire, tripwire::STR_NOTIFY_CHANGE_PROMPT_TIME).c_str() );
pModeInfo->mbLatePassphrase = false;
}
}
//#############################################################################
// cTWModeDbInit
//#############################################################################
class cTWModeDbInit_i : public cTWModeCommon
{
public:
bool mbEncryptDb;
// ctor can set up some default values
cTWModeDbInit_i() : cTWModeCommon(), mbEncryptDb(true) {}
};
///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeDbInit::cTWModeDbInit()
{
mpData = new cTWModeDbInit_i;
}
cTWModeDbInit::~cTWModeDbInit()
{
delete mpData;
}
///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeDbInit::InitCmdLineParser(cCmdLineParser& cmdLine)
{
// initialize the switches common to all modes...
InitCmdLineCommon(cmdLine);
cmdLine.AddArg(cTWCmdLine::MODE_INIT, TSTRING(_T("")), TSTRING(_T("init")), cCmdLineParser::PARAM_NONE);
cmdLine.AddArg(cTWCmdLine::NO_ENCRYPT, TSTRING(_T("e")), TSTRING(_T("no-encryption")), cCmdLineParser::PARAM_NONE);
cmdLine.AddArg(cTWCmdLine::PARAMS, TSTRING(_T("")), TSTRING(_T("")), cCmdLineParser::PARAM_NONE);
// mutual exclusion...
cmdLine.AddMutEx(cTWCmdLine::NO_ENCRYPT, cTWCmdLine::LOCAL_PASSPHRASE);
cmdLine.AddMutEx(cTWCmdLine::NO_ENCRYPT, cTWCmdLine::LOCAL_KEY_FILE);
}
///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeDbInit::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
// first, fill out everything with the config file info...
FillOutConfigInfo(mpData, cf);
// now, parse the command line...
// this takes care of the common stuff...
FillOutCmdLineInfo(mpData, cmdLine);
// now do the stuff specific to this mode...
cCmdLineIter iter(cmdLine);
for(iter.SeekBegin(); ! iter.Done(); iter.Next())
{
switch(iter.ArgId())
{
case cTWCmdLine::NO_ENCRYPT:
mpData->mbEncryptDb = false;
break;
default:
// should I do anything, or just ignore this?
;
}
}
//----------------------------------------
// I require the following information:
//
// * local key file
// * site key file
// * policy file
// * db file
//-----------------------------------------
TEST_INIT_REQUIREMENT((! mpData->mLocalKeyFile.empty()), cTW, tw::STR_ERR_MISSING_LOCAL_KEY);
TEST_INIT_REQUIREMENT((! mpData->mSiteKeyFile.empty()), cTW, tw::STR_ERR_MISSING_SITE_KEY);
TEST_INIT_REQUIREMENT((! mpData->mPolFile.empty()), cTW, tw::STR_ERR_MISSING_POLICY);
TEST_INIT_REQUIREMENT((! mpData->mDbFile.empty()), cTW, tw::STR_ERR_MISSING_DB);
// make sure that the config file and site key file are in sync...
//
if (cTWUtil::VerifyCfgSiteKey( mstrConfigFile, mpData->mSiteKeyFile ) == false)
cTWUtil::PrintErrorMsg(eTWCfgUnencrypted(_T(""), eError::NON_FATAL|eError::SUPRESS_THIRD_MSG));
// Set the cross file systems flag appropriately.
cFSDataSourceIter::SetFileSystemCrossing(mpData->mbCrossFileSystems);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWModeDbInit::Execute(cErrorQueue* pQueue)
{
cFCODatabaseFile dbFile;
cFCODatabaseFile::iterator dbIter( dbFile );
dbFile.SetFileName( mpData->mDbFile );
try
{
// don't go any further if we won't be able to write to the db file...
cFileUtil::TestFileWritable(mpData->mDbFile);
// open the keyfile for early passphrase...
cKeyFile keyfile;
const cElGamalSigPrivateKey* pPrivateKey = 0;
if(mpData->mbEncryptDb)
{
cTWUtil::OpenKeyFile(keyfile, mpData->mLocalKeyFile);
// open the private key for "early passphrase"
if(! mpData->mbLatePassphrase)
pPrivateKey = cTWUtil::CreatePrivateKey(keyfile, mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0, cTWUtil::KEY_LOCAL);
}
//Parse the policy file
cGenreSpecListVector genreSpecList;
cTWCmdLineUtil::ParsePolicyFile(genreSpecList, mpData->mPolFile, mpData->mSiteKeyFile, pQueue);
#ifdef TW_PROFILE
cWin32TaskTimer timer("cTripwire::GenerateDatabase");
timer.Start();
#endif
iUserNotify::GetInstance()->Notify(1, TSS_GetString( cTripwire, tripwire::STR_GENERATING_DB ).c_str() );
uint32 gdbFlags = 0;
gdbFlags |= ( mpData->mbResetAccessTime ? cGenerateDb::FLAG_ERASE_FOOTPRINTS_GD : 0 );
gdbFlags |= ( mpData->mbDirectIO ? cGenerateDb::FLAG_DIRECT_IO : 0 );
// loop through the genres
cGenreSpecListVector::iterator genreIter;
for (genreIter = genreSpecList.begin(); genreIter != genreSpecList.end(); ++genreIter)
{
// make this genre the current genre.
//
cGenreSwitcher::GetInstance()->SelectGenre( genreIter->GetGenre() );
// notify the user
//
iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL,
TSS_GetString( cTripwire, tripwire::STR_PROCESSING_GENRE ).c_str(),
cGenreSwitcher::GetInstance()->GenreToString( cGenreSwitcher::GetInstance()->CurrentGenre(), true ) );
// add an entry to the database for each genre we are parsing
dbFile.AddGenre( genreIter->GetGenre(), &dbIter );
// copy the spec list to the database...
dbIter.GetSpecList() = genreIter->GetSpecList();
// generate the database...
// TODO -- turn pQueue into an error bucket
cGenerateDb::Execute( dbIter.GetSpecList(), dbIter.GetDb(), dbIter.GetGenreHeader().GetPropDisplayer(), pQueue, gdbFlags );
}
cFCODatabaseUtil::CalculateHeader(
dbFile.GetHeader(),
mpData->mPolFile,
mstrConfigFile,
mpData->mDbFile,
mstrCmdLine,
cSystemInfo::GetExeStartTime(),
0
);
#ifdef TW_PROFILE
timer.Stop();
#endif
// write the db to disk...
if(mpData->mbEncryptDb)
{
// open the key file for late passphrase
if(! pPrivateKey)
{
ASSERT(mpData->mbLatePassphrase);
pPrivateKey = cTWUtil::CreatePrivateKey(keyfile, mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0, cTWUtil::KEY_LOCAL);
}
// backup the file we are about to overwrite
cFileUtil::BackupFile(mpData->mDbFile);
cTWUtil::WriteDatabase(mpData->mDbFile.c_str(), dbFile, true, pPrivateKey);
keyfile.ReleasePrivateKey();
}
else
{
cFileUtil::BackupFile(mpData->mDbFile); // backup the file we are about to overwrite
cTWUtil::WriteDatabase(mpData->mDbFile.c_str(), dbFile, false, NULL); // false means no encryption
}
}
catch(eError& e)
{
if (mpData->mbLogToSyslog)
{
TSTRING msg = TSS_GetString( cTripwire, tripwire::STR_SYSLOG_INIT_FAIL_MSG );
msg.append(cErrorTable::GetInstance()->Get( e.GetID() ));
cSyslog::Log(TSS_GetString( cTripwire, tripwire::STR_SYSLOG_IDENT ).c_str(), cSyslog::LOG_ERROR, msg.c_str());
}
cTWUtil::PrintErrorMsg(e);
return 8;
}
// everything went ok; return 0
iUserNotify::GetInstance()->Notify(1, TSS_GetString( cTripwire, tripwire::STR_INIT_SUCCESS ).c_str() );
if (mpData->mbLogToSyslog)
{
TSTRING msg = TSS_GetString( cTripwire, tripwire::STR_SYSLOG_INIT_MSG ) + mpData->mDbFile;
cSyslog::Log(TSS_GetString( cTripwire, tripwire::STR_SYSLOG_IDENT ).c_str(), cSyslog::LOG_SUCCESS, msg.c_str());
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// GetModeUsage
///////////////////////////////////////////////////////////////////////////////
TSTRING cTWModeDbInit::GetModeUsage( void )
{
return ( TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_INIT ) );
}
//#############################################################################
// cTWModeIC
//#############################################################################
class cTWModeIC_i : public cTWModeCommon
{
public:
bool mbUpdate; // launch the editor after integrity checking is done
bool mbPrintToStdout; // print a text version of the report to stdout?
bool mbEmail; // email the report to the appropriate parties?
TSTRING mIgnoreProps; // symbols of properties to ignore
bool mbEncryptReport; // should the report be encrypted?
int mSeverityLevel; // only policy rules greater than or equal to the specified level will be checked.
bool mbTrimBySeverity; // do we use mSeverityLevel to trim the spec list?
TSTRING mSeverityName; // gets mapped to number, then treated like mSeverityLevel
TSTRING mRuleName; // only the named rule will be checked
TSTRING mGenreName; // if not empty, specifies the genre to check
bool mbSecureMode; // are we in extra-pedantic mode? (only valid with mbUpdate == true)
//TSTRING mCmdLine; // entire command line
std::vector<TSTRING> mFilesToCheck;
// ctor can set up some default values
cTWModeIC_i() : cTWModeCommon(), mbUpdate(false), mbPrintToStdout(true), mbEmail(false), mbEncryptReport(false),
mSeverityLevel(-1), mbTrimBySeverity(false), mbSecureMode(false)
{}
};
///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeIC::cTWModeIC()
{
mpData = new cTWModeIC_i;
}
cTWModeIC::~cTWModeIC()
{
delete mpData;
}
///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeIC::InitCmdLineParser(cCmdLineParser& cmdLine)
{
// initialize the switches common to all modes...
InitCmdLineCommon(cmdLine);
cmdLine.AddArg(cTWCmdLine::MODE_CHECK, TSTRING(_T("")), TSTRING(_T("check")), cCmdLineParser::PARAM_NONE);
cmdLine.AddArg(cTWCmdLine::INTER_UPDATE, TSTRING(_T("I")), TSTRING(_T("interactive")), cCmdLineParser::PARAM_NONE);
cmdLine.AddArg(cTWCmdLine::EDITOR, TSTRING(_T("V")), TSTRING(_T("visual")), cCmdLineParser::PARAM_ONE);
cmdLine.AddArg(cTWCmdLine::NO_TTY, TSTRING(_T("n")), TSTRING(_T("no-tty-output")), cCmdLineParser::PARAM_NONE);
cmdLine.AddArg(cTWCmdLine::MAIL_REPORT, TSTRING(_T("M")), TSTRING(_T("email-report")), cCmdLineParser::PARAM_NONE);
cmdLine.AddArg(cTWCmdLine::IGNORE_PROP, TSTRING(_T("i")), TSTRING(_T("ignore")), cCmdLineParser::PARAM_ONE);
cmdLine.AddArg(cTWCmdLine::ENCRYPT_REPORT, TSTRING(_T("E")), TSTRING(_T("signed-report")), cCmdLineParser::PARAM_NONE);
cmdLine.AddArg(cTWCmdLine::SEVERITY_LEVEL, TSTRING(_T("l")), TSTRING(_T("severity")), cCmdLineParser::PARAM_ONE);
//cmdLine.AddArg(cTWCmdLine::SEVERITY_NAME, TSTRING(_T("V")), TSTRING(_T("severityname")), cCmdLineParser::PARAM_ONE);
cmdLine.AddArg(cTWCmdLine::RULE_NAME, TSTRING(_T("R")), TSTRING(_T("rule-name")), cCmdLineParser::PARAM_ONE);
cmdLine.AddArg(cTWCmdLine::GENRE_NAME, TSTRING(_T("x")), TSTRING(_T("section")), cCmdLineParser::PARAM_ONE);
cmdLine.AddArg(cTWCmdLine::PARAMS, TSTRING(_T("")), TSTRING(_T("")), cCmdLineParser::PARAM_MANY);
cmdLine.AddArg(cTWCmdLine::HEXADECIMAL, TSTRING(_T("h")), TSTRING(_T("hexadecimal")), cCmdLineParser::PARAM_NONE);
// multiple levels of reporting
cmdLine.AddArg(cTWCmdLine::REPORTLEVEL, TSTRING(_T("t")), TSTRING(_T("email-report-level")), cCmdLineParser::PARAM_ONE);
// mutual exclusion...
// you can't specify any of these 3 things together...
cmdLine.AddMutEx(cTWCmdLine::SEVERITY_LEVEL, cTWCmdLine::RULE_NAME);
cmdLine.AddDependency( cTWCmdLine::REPORTLEVEL, cTWCmdLine::MAIL_REPORT );
// Report error if the user has specified an editor and we're not in interactive update
// mode: This needs to happen here, so we don't nail someone for having the editor
// specified in the config. file. We should only be this picky if both parameters are
// passed on the command line.
cmdLine.AddDependency( cTWCmdLine::EDITOR, cTWCmdLine::INTER_UPDATE );
}
///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeIC::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
// We will use this to access the parsed cmdLine.
cCmdLineIter iter(cmdLine);
// First, fill out everything with the config file info...
FillOutConfigInfo(mpData, cf);
// Now, parse the command line...
// this takes care of the common stuff...
FillOutCmdLineInfo(mpData, cmdLine);
// now do the stuff specific to this mode..
for(iter.SeekBegin(); ! iter.Done(); iter.Next())
{
switch(iter.ArgId())
{
case cTWCmdLine::INTER_UPDATE:
mpData->mbUpdate = true;
break;
case cTWCmdLine::EDITOR:
ASSERT(iter.NumParams() > 0);
mpData->mEditor = iter.ParamAt(0);
break;
case cTWCmdLine::NO_TTY:
mpData->mbPrintToStdout = false;
break;
case cTWCmdLine::MAIL_REPORT:
mpData->mbEmail = true;
break;
case cTWCmdLine::IGNORE_PROP:
ASSERT(iter.NumParams() > 0);
mpData->mIgnoreProps = iter.ParamAt(0);
break;
case cTWCmdLine::ENCRYPT_REPORT:
mpData->mbEncryptReport = true;
break;
case cTWCmdLine::SEVERITY_LEVEL:
ASSERT(iter.NumParams() > 0);
if (_istdigit(iter.ParamAt(0).c_str()[0]))
{
// interpret severity value
int iSev;
cSeverityLimits sl;
if( ! sl.InterpretInt( iter.ParamAt(0), &iSev ) )
throw eICBadSevVal( iter.ParamAt(0) );
mpData->mSeverityLevel = iSev;
mpData->mbTrimBySeverity = true;
}
else
{
mpData->mSeverityName = iter.ParamAt(0);
}
break;
case cTWCmdLine::REPORTLEVEL:
{
if ( iter.ParamAt(0) == _T("0"))
mpData->mEmailReportLevel = cTextReportViewer::SINGLE_LINE;
else if( iter.ParamAt(0) == _T("1") )
mpData->mEmailReportLevel = cTextReportViewer::PARSEABLE;
else if( iter.ParamAt(0) == _T("2") )
mpData->mEmailReportLevel = cTextReportViewer::SUMMARY_ONLY;
else if( iter.ParamAt(0) == _T("3") )
mpData->mEmailReportLevel = cTextReportViewer::CONCISE_REPORT;
else if( iter.ParamAt(0) == _T("4") )
mpData->mEmailReportLevel = cTextReportViewer::FULL_REPORT;
else
{
// They specified an illegal level, error.
TSTRING errStr = _T("Invalid Level: ");
errStr += iter.ParamAt(0);
throw eTWInvalidReportLevel( errStr );
}
}
//done with report-level stuff.
break;
case cTWCmdLine::RULE_NAME:
ASSERT(iter.NumParams() > 0);
mpData->mRuleName = iter.ParamAt(0);
break;
case cTWCmdLine::GENRE_NAME:
ASSERT(iter.NumParams() > 0);
mpData->mGenreName = iter.ParamAt(0);
break;
case cTWCmdLine::HEXADECIMAL:
cArchiveSigGen::SetHex(true);
break;
case cTWCmdLine::PARAMS:
{
// pack all of these onto the files to check list...
mpData->mFilesToCheck.clear();
for(int i=0; i < iter.NumParams(); i++)
{
mpData->mFilesToCheck.push_back(iter.ParamAt(i));
}
}
default:
// should I do anything, or just ignore this?
;
}
}
// if an editor is not provided in cfg or on cmdline, try to get it from environment
if( mpData->mbUpdate && mpData->mEditor.empty() )
util_GetEditor( mpData->mEditor );
//----------------------------------------
// I require the following information:
//
// * local key file
// * site key file
// * policy file
// * db file
// * report file
// * editor (if interactive is specified)
//-----------------------------------------
TEST_INIT_REQUIREMENT((! mpData->mLocalKeyFile.empty()), cTW, tw::STR_ERR_MISSING_LOCAL_KEY);
TEST_INIT_REQUIREMENT((! mpData->mSiteKeyFile.empty()), cTW, tw::STR_ERR_MISSING_SITE_KEY);
TEST_INIT_REQUIREMENT((! mpData->mPolFile.empty()), cTW, tw::STR_ERR_MISSING_POLICY);
TEST_INIT_REQUIREMENT((! mpData->mDbFile.empty()), cTW, tw::STR_ERR_MISSING_DB);
TEST_INIT_REQUIREMENT((! mpData->mReportFile.empty()), cTW, tw::STR_ERR_MISSING_REPORT);
TEST_INIT_REQUIREMENT((! (mpData->mEditor.empty() && mpData->mbUpdate)),cTW, tw::STR_ERR_MISSING_EDITOR);
///////////////////////////////////////////
// do some email-related verifications
///////////////////////////////////////////
if(mpData->mbEmail)
{
// make sure that email reporting and a file list were both not specified...
TEST_INIT_REQUIREMENT( mpData->mFilesToCheck.empty(), cTripwire, tripwire::STR_ERR_IC_EMAIL_AND_FILES );
// make sure that we have a valid mail method
TEST_INIT_REQUIREMENT( ( cMailMessage::NO_METHOD != mpData->mMailMethod ), cTripwire, tripwire::STR_ERR_NO_MAIL_METHOD );
TEST_INIT_REQUIREMENT( ( cMailMessage::INVALID_METHOD != mpData->mMailMethod ), cTripwire, tripwire::STR_ERR_INVALID_MAIL_METHOD );
// error if method is SENDMAIL and no MAILPROGRAM
TEST_INIT_REQUIREMENT( ( cMailMessage::MAIL_BY_PIPE != mpData->mMailMethod || !mpData->mMailProgram.empty()),
cTripwire, tripwire::STR_ERR_MISSING_MAILPROGRAM );
}
// make sure that the config file and site key file are in sync...
//
if (cTWUtil::VerifyCfgSiteKey( mstrConfigFile, mpData->mSiteKeyFile ) == false)
cTWUtil::PrintErrorMsg(eTWCfgUnencrypted(_T(""), eError::NON_FATAL|eError::SUPRESS_THIRD_MSG));
// Set the cross file systems flag appropriately.
cFSDataSourceIter::SetFileSystemCrossing(mpData->mbCrossFileSystems);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWModeIC::Execute(cErrorQueue* pQueue)
{
cDebug d("cTWModeIC::Execute");
cFCODatabaseFile dbFile;
cFCODatabaseFile::iterator dbIter( dbFile );
cFCOReport report;
cFCOReportHeader reportHeader;
bool bEncrypted, bCheckMade = false, bEmailError = false;
try
{
// don't go any further if we won't be able to write the report to disk...
cFileUtil::TestFileWritable(mpData->mReportFile);
// open the database; note that ReadDatabase will set bEncrypted and open the
// key file if necessary
cKeyFile localKeyfile;
cTWUtil::OpenKeyFile(localKeyfile, mpData->mLocalKeyFile);
cTWUtil::ReadDatabase(mpData->mDbFile.c_str(), dbFile, localKeyfile.GetPublicKey(), bEncrypted);
//
// give a warning if the user that created the database is not the same as the user running right now...
//
TSTRING userName;
if( iFSServices::GetInstance()->GetCurrentUserName( userName ) )
{
if( dbFile.GetHeader().GetCreator().compare( userName.c_str() ) != 0 )
{
// TODO -- move these strings to the string table
TOSTRINGSTREAM str;
str << TSS_GetString( cTripwire, tripwire::STR_ERR2_DIFFERENT_USERS1) << dbFile.GetHeader().GetCreator()
<< TSS_GetString( cTripwire, tripwire::STR_ERR2_DIFFERENT_USERS2) << userName << std::ends;
cTWUtil::PrintErrorMsg( eICDifferentUsers( str.str(), eError::NON_FATAL ) );
}
}
// give a stern warning if the database is not encrypted
if(! bEncrypted && iUserNotify::GetInstance()->GetVerboseLevel() > iUserNotify::V_SILENT)
{
cTWUtil::PrintErrorMsg(eTWDbNotEncrypted(_T(""), eError::NON_FATAL|eError::SUPRESS_THIRD_MSG));
}
if( (!bEncrypted) && (mpData->mLocalProvided) && (!mpData->mbEncryptReport) )
{
//Issue a warning if a passphrase has been supplied, and the database is NOT encrypted.
cTWUtil::PrintErrorMsg( eTWPassForUnencryptedDb( _T(""), eError::NON_FATAL));
}
// get the password here if they are saving an encrypted report
const cElGamalSigPrivateKey* pPrivateKey = 0;
if(mpData->mbEncryptReport)
{
// if the key file isn't open (ie -- the report wasn't encrypted) then we need to
// open it now...
cTWUtil::OpenKeyFile(localKeyfile, mpData->mLocalKeyFile);
// open the private key for "early passphrase"
if(! mpData->mbLatePassphrase)
{
// OK, here is the deal: if the user specified a password on the command line
// and we are going to do an interactive update after the IC and both the report
// and db are encrypted, then we need to save the passphrase so it doesn't get
// scrambled by CreatePrivateKey
if(mpData->mLocalProvided && mpData->mbUpdate && bEncrypted)
{
wc16_string passPhrase = mpData->mLocalPassphrase.c_str();
pPrivateKey = cTWUtil::CreatePrivateKey(localKeyfile, passPhrase.c_str(), cTWUtil::KEY_LOCAL);
}
else
pPrivateKey = cTWUtil::CreatePrivateKey(localKeyfile, mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0, cTWUtil::KEY_LOCAL);
}
}
// either integrity check using the policy file or a list of files
if(mpData->mFilesToCheck.size() != 0)
{
// we are checking files...
iUserNotify::GetInstance()->Notify(1, TSS_GetString( cTripwire, tripwire::STR_IC_FILES).c_str() );
////
// tell the user we are ignoring severity level and rule name, if they specified them...
//
if( ! mpData->mRuleName.empty() )
iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, TSS_GetString( cTripwire, tripwire::STR_IC_IGNORING_RULE_NAME).c_str() );
if( mpData->mbTrimBySeverity )
iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, TSS_GetString( cTripwire, tripwire::STR_IC_IGNORING_SEV_NUM).c_str() );
if( ! mpData->mSeverityName.empty() )
iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, TSS_GetString( cTripwire, tripwire::STR_IC_IGNORING_SEV_NAME).c_str() );
if( ! mpData->mGenreName.empty() )
iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, TSS_GetString( cTripwire, tripwire::STR_IC_IGNORING_GENRE_NAME).c_str() );
//
////
//**********************************
// new, experimental stuff.. trying out new command line interface
//
cTWUtil::GenreObjList listOut;
cTWUtil::ParseObjectList( listOut, mpData->mFilesToCheck );
//
// now, we will iterate over the list, performing an integrity check for each genre
//
for( cTWUtil::GenreObjList::iterator genreIter = listOut.begin(); genreIter != listOut.end(); ++genreIter )
{
dbIter.SeekToGenre( genreIter->first );
if( ! dbIter.Done() )
{
cGenreSwitcher::GetInstance()->SelectGenre( (cGenre::Genre)dbIter.GetGenre() );
TW_UNIQUE_PTR<iParserGenreUtil> pParseUtil (iTWFactory::GetInstance()->CreateParserGenreUtil());
//
// I have to turn this into a list of cFCONames
//
std::list<cFCOName> fcoNames;
for(cTWUtil::ObjList::iterator it = genreIter->second.begin(); it != genreIter->second.end(); ++it)
{
// if this is not an absolute path, warn and continue...
//
try
{
fcoNames.push_back( cTWUtil::ParseObjectName( *it ) );
}
catch( eError& e )
{
e.SetFatality( false );
pQueue->AddError( e );
report.GetErrorQueue()->AddError( e );
}
}
// TODO -- emit "processing XXX"
cIntegrityCheck ic( (cGenre::Genre)genreIter->first, dbIter.GetSpecList(), dbIter.GetDb(), report, pQueue );
//If any sort of exception escapes the IC, make sure it goes in the report.
try
{
uint32 icFlags = 0;
icFlags |= ( mpData->mfLooseDirs ? cIntegrityCheck::FLAG_LOOSE_DIR : 0 );
icFlags |= ( mpData->mbResetAccessTime ? cIntegrityCheck::FLAG_ERASE_FOOTPRINTS_IC : 0 );
icFlags |= ( mpData->mbDirectIO ? cIntegrityCheck::FLAG_DIRECT_IO : 0 );
ic.ExecuteOnObjectList( fcoNames, icFlags );
}
catch( eError& e )
{
if( pQueue )
pQueue->AddError(e);
}
catch( std::exception& e )
{
if (pQueue )
pQueue->AddError(eIC(e.what()));
}
catch(...)
{
if (pQueue )
pQueue->AddError(eIC("Unknown"));
}
// put all info into report
cFCOReportGenreIter rgi( report );
rgi.SeekToGenre( (cGenre::Genre) genreIter->first );
rgi.GetGenreHeader().SetPropDisplayer( iTWFactory::GetInstance()->CreatePropDisplayer() );
rgi.GetGenreHeader().GetPropDisplayer()->Merge( dbIter.GetGenreHeader().GetPropDisplayer() );
rgi.GetGenreHeader().SetObjectsScanned( ic.ObjectsScanned() );
}
else
{
//TODO -- what is the right thing to do here?
// error and continue or exit?
//
eTWDbDoesntHaveGenre e( cGenreSwitcher::GetInstance()->GenreToString( (cGenre::Genre)genreIter->first, true ) );
e.SetFatality( false );
cTWUtil::PrintErrorMsg( e );
}
}
//
//**********************************
}
else
{
// we are checking a subset of the policy file
// parse the policy file
cGenreSpecListVector genreSpecList;
cTWCmdLineUtil::ParsePolicyFile(genreSpecList, mpData->mPolFile, mpData->mSiteKeyFile, pQueue);
//
// make sure the parsed policy is equivelant to the one in the database...
// TODO -- should this go in the report? should I make a report or just bail? I guess
// that for now, I will bail. (it throws an exception on failure
//
cTWCmdLineUtil::VerifyPolicy( genreSpecList, dbFile ) ;
//
// if they are only checking one genre, figure out which one it is ...
//
cGenre::Genre genreToCheck = cGenre::GENRE_INVALID;
if( ! mpData->mGenreName.empty() )
{
genreToCheck = cGenreSwitcher::GetInstance()->StringToGenre( mpData->mGenreName.c_str() );
if( genreToCheck == cGenre::GENRE_INVALID )
{
throw eICUnknownGenre( mpData->mGenreName );
}
if( ! cGenreSwitcher::GetInstance()->IsGenreAppropriate( genreToCheck ) )
{
throw eICInappropriateGenre( mpData->mGenreName );
}
iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL,
TSS_GetString( cTripwire, tripwire::STR_CHECKING_GENRE).c_str(),
cGenreSwitcher::GetInstance()->GenreToString( genreToCheck ) );
}
// iterate over the genres...
//
cGenreSpecListVector::iterator genreIter;
for (genreIter = genreSpecList.begin(); genreIter != genreSpecList.end(); ++genreIter)
{
// if they are only checking one genre and it is not this one, then
// skip everything else...
//
if( (genreToCheck != cGenre::GENRE_INVALID) && (genreToCheck != genreIter->GetGenre()) )
continue;
// make this genre the current genre.
//
cGenreSwitcher::GetInstance()->SelectGenre( genreIter->GetGenre() );
//
// seek the database iter to this genre...
//
dbIter.SeekToGenre( genreIter->GetGenre() );
if( dbIter.Done() )
{
// TODO -- what is the right thing to do here? throw or warn and continue?
// I think that I should add it to the report's error queue and spit it to
// stderr and continue
//
TCERR << _T("Genre ") << cGenreSwitcher::GetInstance()->GenreToString( genreIter->GetGenre() )
<< _T(" not in database; skipping.") << std::endl;
continue;
}
// message to the user...
//
iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL,
TSS_GetString( cTripwire, tripwire::STR_PROCESSING_GENRE ).c_str(),
cGenreSwitcher::GetInstance()->GenreToString( cGenreSwitcher::GetInstance()->CurrentGenre(), true ) );
cFCOSpecList specList = genreIter->GetSpecList();
// trim ignored properties from the specs...
if(! mpData->mIgnoreProps.empty())
cTWCmdLineUtil::TrimPropsFromSpecs(specList, mpData->mIgnoreProps);
// trim ignored rule names from the specs...
if(! mpData->mRuleName.empty())
cTWCmdLineUtil::TrimSpecsByName(specList, mpData->mRuleName);
// trim spec list based on severity
if(mpData->mbTrimBySeverity)
cTWCmdLineUtil::TrimSpecsBySeverity(specList, mpData->mSeverityLevel);
// trim specs by severity name
if(! mpData->mSeverityName.empty())
cTWCmdLineUtil::TrimSpecsBySeverityName(specList, mpData->mSeverityName);
//
// only do the integrity check if there is something to check!
//
if( ! specList.IsEmpty() )
{
bCheckMade = true; //we've checked at least one rule.
// do the integrity check...
iUserNotify::GetInstance()->Notify(1, TSS_GetString( cTripwire, tripwire::STR_INTEGRITY_CHECK).c_str());
#ifdef TW_PROFILE
cWin32TaskTimer timer("cTripwire::IntegrityCheck");
timer.Start();
#endif
cIntegrityCheck ic( (cGenre::Genre)dbIter.GetGenre(), specList, dbIter.GetDb(), report, pQueue );
//If any sort of exception escapes the IC, make sure it goes in the report.
try
{
uint32 icFlags = 0;
icFlags |= ( mpData->mfLooseDirs ? cIntegrityCheck::FLAG_LOOSE_DIR : 0 );
icFlags |= ( mpData->mbResetAccessTime ? cIntegrityCheck::FLAG_ERASE_FOOTPRINTS_IC : 0 );
icFlags |= ( mpData->mbDirectIO ? cIntegrityCheck::FLAG_DIRECT_IO : 0 );
ic.Execute( icFlags );
}
catch( eError& e )
{
if( pQueue )
pQueue->AddError(e);
}
catch( std::exception& e )
{
if (pQueue )
pQueue->AddError(eIC(e.what()));
}
catch(...)
{
if (pQueue )
pQueue->AddError(eIC("Unknown"));
}
// put all display info into report
cFCOReportGenreIter rgi( report );
rgi.SeekToGenre( (cGenre::Genre) dbIter.GetGenre() );
rgi.GetGenreHeader().SetPropDisplayer( iTWFactory::GetInstance()->CreatePropDisplayer() );
rgi.GetGenreHeader().GetPropDisplayer()->Merge( dbIter.GetGenreHeader().GetPropDisplayer() );
rgi.GetGenreHeader().SetObjectsScanned( ic.ObjectsScanned() );
#ifdef TW_PROFILE
timer.Stop();
#endif
}
else
{
iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL,
TSS_GetString( cTripwire, tripwire::STR_ERR_IC_NO_SPECS_LEFT).c_str(),
cGenreSwitcher::GetInstance()->GenreToString( genreIter->GetGenre(), true ) );
}
}
}
// save out the report...
cFCOReportUtil::CalculateHeaderInfo(
reportHeader,
mpData->mPolFile,
mstrConfigFile,
mpData->mDbFile,
mstrCmdLine,
cSystemInfo::GetExeStartTime(),
dbFile.GetHeader().GetLastDBUpdateTime()
);
cFCOReportUtil::FinalizeReport( report );
// email the report if that is desired...
if(mpData->mbEmail)
{
bEmailError = cTWCmdLineUtil::EmailReport(reportHeader, report, mpData) == 0;
// We should notify them that no email was sent should the following condition
// apply.
if( !bCheckMade )
{
iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL,
TSS_GetString( cTripwire, tripwire::STR_IC_NOEMAIL_SENT).c_str(),
_T("") );
}
}
// write the report to disk...
if(mpData->mbEncryptReport)
{
// get the private key for "late passphrase"
if(! pPrivateKey)
{
ASSERT(mpData->mbLatePassphrase);
// OK, here is the deal: if the user specified a password on the command line
// and we are going to do an interactive update after the IC and both the report
// and db are encrypted, then we need to save the passphrase so it doesn't get
// scrambled by CreatePrivateKey
if(mpData->mLocalProvided && mpData->mbUpdate && bEncrypted)
{
wc16_string passPhrase = mpData->mLocalPassphrase.c_str();
pPrivateKey = cTWUtil::CreatePrivateKey(localKeyfile, passPhrase.c_str(), cTWUtil::KEY_LOCAL);
}
else
pPrivateKey = cTWUtil::CreatePrivateKey(localKeyfile, mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0, cTWUtil::KEY_LOCAL);
}
// backup the file we are about to overwrite
cFileUtil::BackupFile(mpData->mReportFile);
// write the report
cTWUtil::WriteReport(mpData->mReportFile.c_str(), reportHeader, report, true, pPrivateKey);
localKeyfile.ReleasePrivateKey();
}
else
{
cFileUtil::BackupFile(mpData->mReportFile); // backup the file we are about to overwrite
cTWUtil::WriteReport(mpData->mReportFile.c_str(), reportHeader, report, false, NULL); // false means no encryption
}
// print a text version of the report to stdout...
if(mpData->mbPrintToStdout)
{
// print a couple of new lines so that the report isn't butted up against the rest of the output.
iUserNotify::GetInstance()->Notify(iUserNotify::V_NORMAL, _T("\n\n"));
cTextReportViewer trv( reportHeader, report );
trv.PrintTextReport( _T("-"), cTextReportViewer::SUMMARY_ONLY );
}
}
catch(eError& e)
{
if (mpData->mbLogToSyslog)
{
TSTRING msg = TSS_GetString( cTripwire, tripwire::STR_SYSLOG_IC_FAIL_MSG );
msg.append(cErrorTable::GetInstance()->Get( e.GetID() ));
cSyslog::Log(TSS_GetString( cTripwire, tripwire::STR_SYSLOG_IDENT ).c_str(), cSyslog::LOG_ERROR, msg.c_str());
}
cTWUtil::PrintErrorMsg(e);
return 8;
//TODO -- send a sad email here if mbEmail == true
}
// tell the user the report was saved
iUserNotify::GetInstance()->Notify(1, TSS_GetString( cTripwire, tripwire::STR_IC_SUCCESS).c_str());
if (mpData->mbLogToSyslog)
{
cTextReportViewer trv( reportHeader, report );
TSTRING msg = TSS_GetString( cTripwire, tripwire::STR_SYSLOG_IC_MSG );
msg.append(mpData->mDbFile);
msg.append(_T(" "));
msg.append(trv.SingleLineReport());
cSyslog::Log(TSS_GetString( cTripwire, tripwire::STR_SYSLOG_IDENT ).c_str(), cSyslog::LOG_SUCCESS, msg.c_str());
}
// go directly into interactive update if that is what they want...
//
if(mpData->mbUpdate)
{
cTWModeDbUpdate dbUpdate;
dbUpdate.Init(mpData, &dbFile, &reportHeader, &report, bEncrypted);
int ret = dbUpdate.Execute(pQueue);
if (ret != 0)
return ret;
}
// the return level is based on the type of violations that were found:
// 1 = added files
// 2 = removed files
// 4 = changed files
// 8 = ERROR_IC = Execution Error
if(bEmailError)
return (ERROR_IC | cTWCmdLineUtil::GetICReturnValue(report));
else
return cTWCmdLineUtil::GetICReturnValue(report);
}
///////////////////////////////////////////////////////////////////////////////
// GetModeUsage
///////////////////////////////////////////////////////////////////////////////
TSTRING cTWModeIC::GetModeUsage( void )
{
return ( TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_CHECK ) );
}
//#############################################################################
// cTWModeDbUpdate
//#############################################################################
class cTWModeDbUpdate_i : public cTWModeCommon
{
public:
bool mbInteractive; // don't do interactive update; just integrate the report file
bool mbSecureMode; // are we in extra-pedantic mode?
//std::string mSitePassphrase; // pass phrase for site key
//bool mSiteProvided;
// these members are only used when this mode is entered after integrity checking.
// they are set to variables that are on cTWModeIC's stack, so they should _not_ be deleted.
// if they are non-null when Execute() is called, they should be used as the db and report.
bool mbEncryptDb;
cFCOReport* mpReport;
cFCODatabaseFile* mpDbFile;
cFCOReportHeader* mpReportHeader;
// ctor can set up some default values
cTWModeDbUpdate_i() : cTWModeCommon(), mbInteractive(true), mbSecureMode(true), mbEncryptDb(true), /*mSiteProvided(false),*/ mpReport(0), mpDbFile(0), mpReportHeader(0) {}
};
///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeDbUpdate::cTWModeDbUpdate()
{
mpData = new cTWModeDbUpdate_i;
}
cTWModeDbUpdate::~cTWModeDbUpdate()
{
delete mpData;
}
///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeDbUpdate::InitCmdLineParser(cCmdLineParser& cmdLine)
{
// initialize the switches common to all modes...
InitCmdLineCommon(cmdLine);
cmdLine.AddArg(cTWCmdLine::MODE_UPDATE_DB, TSTRING(_T("")), TSTRING(_T("update")), cCmdLineParser::PARAM_NONE);
cmdLine.AddArg(cTWCmdLine::ACCEPT_ALL, TSTRING(_T("a")), TSTRING(_T("accept-all")), cCmdLineParser::PARAM_NONE);
cmdLine.AddArg(cTWCmdLine::SECURE_MODE, TSTRING(_T("Z")), TSTRING(_T("secure-mode")), cCmdLineParser::PARAM_ONE);
cmdLine.AddArg(cTWCmdLine::EDITOR, TSTRING(_T("V")), TSTRING(_T("visual")), cCmdLineParser::PARAM_ONE);
cmdLine.AddArg(cTWCmdLine::PARAMS, TSTRING(_T("")), TSTRING(_T("")), cCmdLineParser::PARAM_NONE);
// mutual exclusion...
cmdLine.AddMutEx(cTWCmdLine::ACCEPT_ALL, cTWCmdLine::EDITOR);
}
///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeDbUpdate::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
// first, fill out everything with the config file info...
FillOutConfigInfo(mpData, cf);
// now, parse the command line...
// this takes care of the common stuff...
FillOutCmdLineInfo(mpData, cmdLine);
// now do the stuff specific to this mode...
cCmdLineIter iter(cmdLine);
for(iter.SeekBegin(); ! iter.Done(); iter.Next())
{
switch(iter.ArgId())
{
case cTWCmdLine::ACCEPT_ALL:
mpData->mbInteractive = false;
break;
case cTWCmdLine::SECURE_MODE:
ASSERT(iter.NumParams() > 0);
if(iter.ParamAt(0).compare(_T("high")) == 0)
mpData->mbSecureMode = true;
else if(iter.ParamAt(0).compare(_T("low")) == 0)
mpData->mbSecureMode = false;
else
{
// invalid parameter to secure mode switch...
// TODO -- print this to stderr; how do I display (1) the switch name
// and (2) the possible values?
// TODO -- move {high, low} somewhere else
TCERR << TSS_GetString( cTripwire, tripwire::STR_ERR_BAD_PARAM) << _T(" : ")
<< iter.ParamAt(0).c_str() << _T("; must be {high, low}") << std::endl;
return false;
}
break;
case cTWCmdLine::EDITOR:
ASSERT(iter.NumParams() > 0);
mpData->mEditor = iter.ParamAt(0);
break;
default:
// should I do anything, or just ignore this?
;
}
}
// if an editor is not provided in cfg or on cmdline, try to get it from environment
if( mpData->mbInteractive && mpData->mEditor.empty() )
util_GetEditor( mpData->mEditor );
//----------------------------------------
// I require the following information:
//
// * local key file
// * site key file
// * policy file
// * db file
// * report file
// * editor (if accept all is not specified)
//-----------------------------------------
TEST_INIT_REQUIREMENT((! mpData->mLocalKeyFile.empty()), cTW, tw::STR_ERR_MISSING_LOCAL_KEY);
TEST_INIT_REQUIREMENT((! mpData->mSiteKeyFile.empty()), cTW, tw::STR_ERR_MISSING_SITE_KEY);
TEST_INIT_REQUIREMENT((! mpData->mPolFile.empty()), cTW, tw::STR_ERR_MISSING_POLICY);
TEST_INIT_REQUIREMENT((! mpData->mDbFile.empty()), cTW, tw::STR_ERR_MISSING_DB);
TEST_INIT_REQUIREMENT((! mpData->mReportFile.empty()), cTW, tw::STR_ERR_MISSING_REPORT);
TEST_INIT_REQUIREMENT(((!mpData->mEditor.empty()) || (!mpData->mbInteractive)), cTW, tw::STR_ERR_MISSING_EDITOR);
// make sure that the config file and site key file are in sync...
//
if (cTWUtil::VerifyCfgSiteKey( mstrConfigFile, mpData->mSiteKeyFile ) == false)
cTWUtil::PrintErrorMsg(eTWCfgUnencrypted(_T(""), eError::NON_FATAL|eError::SUPRESS_THIRD_MSG));
// Set the cross file systems flag appropriately.
cFSDataSourceIter::SetFileSystemCrossing(mpData->mbCrossFileSystems);
return true;
}
void cTWModeDbUpdate::Init(const cTWModeIC_i* pICData, cFCODatabaseFile* dbFile, cFCOReportHeader* prh, cFCOReport* pReport, bool bEncryptDb)
{
mpData->mbInteractive = true; // always interactive
mpData->mbSecureMode = pICData->mbSecureMode;
//mpData->mbBackup = pICData->mbBackup;
mpData->mDbFile = pICData->mDbFile;
mpData->mLocalKeyFile = pICData->mLocalKeyFile;
mpData->mLocalPassphrase= pICData->mLocalPassphrase;
mpData->mLocalProvided = pICData->mLocalProvided;
mpData->mPolFile = pICData->mPolFile;
mpData->mReportFile = pICData->mReportFile;
mpData->mSiteKeyFile = pICData->mSiteKeyFile;
mpData->mVerbosity = pICData->mVerbosity;
mpData->mEditor = pICData->mEditor;
mpData->mbLatePassphrase= pICData->mbLatePassphrase;
mpData->mbEncryptDb = bEncryptDb;
mpData->mpDbFile = dbFile;
if(pReport)
{
ASSERT(prh != 0);
mpData->mpReport = pReport;
mpData->mpReportHeader = prh;
}
else
{
mpData->mpReport = 0;
mpData->mpReportHeader = 0;
}
}
///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWModeDbUpdate::Execute(cErrorQueue* pQueue)
{
try
{
// don't go any further if we won't be able to write to the db file...
cFileUtil::TestFileWritable(mpData->mDbFile);
// open the database
cFCODatabaseFile dbFile;
cKeyFile localKeyfile;
bool bDbEncrypted, bReportEncrypted;
cTWUtil::OpenKeyFile(localKeyfile, mpData->mLocalKeyFile);
if(! mpData->mpDbFile)
{
// open the database; note that ReadDatabase will set bEncrypted and open the
// key file if necessary
cTWUtil::ReadDatabase(mpData->mDbFile.c_str(), dbFile, localKeyfile.GetPublicKey(), bDbEncrypted);
mpData->mpDbFile = &dbFile;
}
else
{
// we were passed the database; we should open the key file now if the db needs
// to be saved encrypted...
if(mpData->mbEncryptDb)
bDbEncrypted = true;
else
bDbEncrypted = false;
}
if( (!bDbEncrypted) && (mpData->mLocalProvided) )
{
//Issue a warning if a passphrase has been supplied, and the database is NOT encrypted.
cTWUtil::PrintErrorMsg( eTWPassForUnencryptedDb( _T(""), eError::NON_FATAL));
}
// open the report...
cFCOReport report;
cFCOReportHeader reportHeader;
if(! mpData->mpReport)
{
cTWUtil::ReadReport(mpData->mReportFile.c_str(), reportHeader, report, localKeyfile.GetPublicKey(), false, bReportEncrypted);
mpData->mpReport = &report;
mpData->mpReportHeader = &reportHeader;
}
else
ASSERT(mpData->mpReportHeader != 0);
// open the private key for "early passphrase"
// I don't think that early passphrase should apply to db update; there is no reason the user
// would want to enter the passphrase before editing the report .. mdb
// TODO: Well ok, can we remove this commented out code then?
const cElGamalSigPrivateKey* pPrivateKey = 0;
/*
if(bDbEncrypted && (! mpData->mbLatePassphrase))
{
pPrivateKey = cTWUtil::CreatePrivateKey(localKeyfile, mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0);
}
*/
// have the user modify the report, if they want to
if(mpData->mbInteractive)
{
// put a try/catch here to allow ourselves to print a message
// saying the editor launch failed
try
{
cTextReportViewer trv( *mpData->mpReportHeader, *mpData->mpReport );
trv.DisplayReportAndHaveUserUpdateIt( mpData->mEditor, cTextReportViewer::FULL_REPORT );
}
catch(...)
{
TCERR << TSS_GetString( cTripwire, tripwire::STR_ERR_UPDATE_ED_LAUNCH) << std::endl;
throw;
}
}
// if the report is empty, there is no need to do the update...
if(! cTWCmdLineUtil::ReportContainsFCO(*mpData->mpReport))
{
// tell them ...
iUserNotify::GetInstance()->Notify(1, TSS_GetString( cTripwire, tripwire::STR_REPORT_EMPTY).c_str());
// keep those pesky ASSERTs from cKeyFile away!
if(pPrivateKey)
localKeyfile.ReleasePrivateKey();
return 0; // is this the right return val?
}
// give a stern warning if the database is not encrypted
if(! bDbEncrypted && iUserNotify::GetInstance()->GetVerboseLevel() > iUserNotify::V_SILENT)
{
// warn if the database is not encrytped
cTWUtil::PrintErrorMsg(eTWDbNotEncrypted(_T(""), eError::NON_FATAL|eError::SUPRESS_THIRD_MSG));
}
// iterate over all the genres in the report...
//
cFCOReportGenreIter genreIter( *mpData->mpReport );
for( genreIter.SeekBegin(); ! genreIter.Done(); genreIter.Next() )
{
cGenreSwitcher::GetInstance()->SelectGenre( genreIter.GetGenre() );
//
// seek the db iter to the current genre...
//
cFCODatabaseFile::iterator dbIter( *mpData->mpDbFile );
dbIter.SeekToGenre( genreIter.GetGenre() );
if( dbIter.Done() )
{
//TODO -- what is the right thing to do here?
//
ASSERT( false );
throw eTWDbDoesntHaveGenre();
}
//
// actually do the integrity check...
//
uint32 udFlags = 0;
udFlags |= ( mpData->mbResetAccessTime ? cUpdateDb::FLAG_ERASE_FOOTPRINTS_UD : 0 );
cUpdateDb update( dbIter.GetDb(), *mpData->mpReport, pQueue );
if( (! update.Execute( udFlags )) && mpData->mbSecureMode )
{
// we will not perform the update; simply exit.
TCOUT << TSS_GetString( cTripwire, tripwire::STR_DB_NOT_UPDATED) << std::endl;
return 8;
}
//
// update the header info for the database...
//
// merge the prop displayers...
dbIter.GetGenreHeader().GetPropDisplayer()->Merge( genreIter.GetGenreHeader().GetPropDisplayer() );
}
// Update the header
// Note: We DO NOT call cFCODatabaseUtil::CalculateHeader() here. We only
// want to update these entries.
mpData->mpDbFile->GetHeader().SetCreator(cTWUtil::GetCurrentUser());
mpData->mpDbFile->GetHeader().SetLastDBUpdateTime( cSystemInfo::GetExeStartTime() );
//
// write the db to disk...
//
if(bDbEncrypted)
{
// open the private key for "late prompting"
if(! pPrivateKey)
{
pPrivateKey = cTWUtil::CreatePrivateKey(localKeyfile, mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0, cTWUtil::KEY_LOCAL);
}
// backup the file we are about to overwrite
cFileUtil::BackupFile(mpData->mDbFile);
cTWUtil::WriteDatabase(mpData->mDbFile.c_str(), *mpData->mpDbFile, true, pPrivateKey);
localKeyfile.ReleasePrivateKey();
}
else
{
cFileUtil::BackupFile(mpData->mDbFile); // backup the file we are about to overwrite
cTWUtil::WriteDatabase(mpData->mDbFile.c_str(), *mpData->mpDbFile, false, NULL); // false means no encryption
}
}
catch(eError& e)
{
if (mpData->mbLogToSyslog)
{
TSTRING msg = TSS_GetString( cTripwire, tripwire::STR_SYSLOG_UP_FAIL_MSG );
msg.append(cErrorTable::GetInstance()->Get( e.GetID() ));
cSyslog::Log(TSS_GetString( cTripwire, tripwire::STR_SYSLOG_IDENT ).c_str(), cSyslog::LOG_ERROR, msg.c_str());
}
cTWUtil::PrintErrorMsg(e);
return 8;
}
if (mpData->mbLogToSyslog)
{
TSTRING msg = TSS_GetString( cTripwire, tripwire::STR_SYSLOG_UP_MSG );
msg.append(mpData->mDbFile);
cSyslog::Log(TSS_GetString( cTripwire, tripwire::STR_SYSLOG_IDENT ).c_str(), cSyslog::LOG_SUCCESS, msg.c_str());
}
// everything went ok; return 0
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// GetModeUsage
///////////////////////////////////////////////////////////////////////////////
TSTRING cTWModeDbUpdate::GetModeUsage( void )
{
return ( TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_UPDATE ) );
}
//#############################################################################
// cTWModePolUpdate
//#############################################################################
class cTWModePolUpdate_i : public cTWModeCommon
{
public:
TSTRING mTextPolFile;
wc16_string mSitePassphrase;
bool mSiteProvided;
bool mbSecureMode;
// ctor can set up some default values
cTWModePolUpdate_i() : cTWModeCommon(), mSiteProvided(false), mbSecureMode(true) {}
};
///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModePolUpdate::cTWModePolUpdate()
{
mpData = new cTWModePolUpdate_i;
}
cTWModePolUpdate::~cTWModePolUpdate()
{
delete mpData;
}
///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModePolUpdate::InitCmdLineParser(cCmdLineParser& cmdLine)
{
// initialize the switches common to all modes...
InitCmdLineCommon(cmdLine);
cmdLine.AddArg(cTWCmdLine::MODE_UPDATE_POL, TSTRING(_T("")), TSTRING(_T("update-policy")), cCmdLineParser::PARAM_NONE);
cmdLine.AddArg(cTWCmdLine::TEXT_POL_FILE, TSTRING(_T("")), TSTRING(_T("")), cCmdLineParser::PARAM_ONE);
cmdLine.AddArg(cTWCmdLine::LOCAL_PASSPHRASE,TSTRING(_T("P")), TSTRING(_T("local-passphrase")), cCmdLineParser::PARAM_ONE);
cmdLine.AddArg(cTWCmdLine::SITE_PASSPHRASE, TSTRING(_T("Q")), TSTRING(_T("site-passphrase")), cCmdLineParser::PARAM_ONE);
cmdLine.AddArg(cTWCmdLine::SECURE_MODE, TSTRING(_T("Z")), TSTRING(_T("secure-mode")), cCmdLineParser::PARAM_ONE);
}
///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModePolUpdate::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
// first, fill out everything with the config file info...
FillOutConfigInfo(mpData, cf);
// now, parse the command line...
// this takes care of the common stuff...
FillOutCmdLineInfo(mpData, cmdLine);
// now do the stuff specific to this mode...
cCmdLineIter iter(cmdLine);
for(iter.SeekBegin(); ! iter.Done(); iter.Next())
{
TSTRING fullPath;
switch(iter.ArgId())
{
case cTWCmdLine::TEXT_POL_FILE:
ASSERT(iter.NumParams() > 0);
mpData->mTextPolFile = iter.ParamAt(0);
//
// convert it to a full path
//
if( iFSServices::GetInstance()->FullPath( fullPath, mpData->mTextPolFile ) )
mpData->mTextPolFile = fullPath;
break;
case cTWCmdLine::SITE_PASSPHRASE:
ASSERT(iter.NumParams() > 0);
mpData->mSitePassphrase = cStringUtil::TstrToWstr(iter.ParamAt(0));
mpData->mSiteProvided = true;
break;
case cTWCmdLine::SECURE_MODE:
ASSERT(iter.NumParams() > 0);
if(iter.ParamAt(0).compare(_T("high")) == 0)
mpData->mbSecureMode = true;
else if(iter.ParamAt(0).compare(_T("low")) == 0)
mpData->mbSecureMode = false;
else
{
// invalid parameter to secure mode switch...
// TODO -- print this to stderr; how do I display (1) the switch name
// and (2) the possible values?
// TODO -- move {high, low} somewhere else
TCERR << TSS_GetString( cTripwire, tripwire::STR_ERR_BAD_PARAM) << _T(" : ")
<< iter.ParamAt(0).c_str() << _T("; must be {high, low}") << std::endl;
return false;
}
break;
default:
// should I do anything, or just ignore this?
;
}
}
//----------------------------------------
// I require the following information:
//
// * local key file
// * site key file
// * policy file
// * db file
//-----------------------------------------
TEST_INIT_REQUIREMENT((! mpData->mLocalKeyFile.empty()), cTW, tw::STR_ERR_MISSING_LOCAL_KEY);
TEST_INIT_REQUIREMENT((! mpData->mSiteKeyFile.empty()), cTW, tw::STR_ERR_MISSING_SITE_KEY);
TEST_INIT_REQUIREMENT((! mpData->mPolFile.empty()), cTW, tw::STR_ERR_MISSING_POLICY);
TEST_INIT_REQUIREMENT((! mpData->mDbFile.empty()), cTW, tw::STR_ERR_MISSING_DB);
// if a passphrase was passed on the command line, then late prompting should
// be set to false so that wrong passwords are detected immediately
//
// Note that the local passphrase was already checked against in FillOutCmdLineInfo(),
// so I only need to test the site passphrase
if( mpData->mSiteProvided && mpData->mbLatePassphrase )
{
// TODO -- I don't know if we should display this or not -- mdb
//iUserNotify::GetInstance()->Notify( iUserNotify::V_VERBOSE, TSS_GetString( cTripwire, tripwire::STR_NOTIFY_CHANGE_PROMPT_TIME).c_str() );
mpData->mbLatePassphrase = false;
}
// make sure that the config file and site key file are in sync...
//
if (cTWUtil::VerifyCfgSiteKey( mstrConfigFile, mpData->mSiteKeyFile ) == false)
cTWUtil::PrintErrorMsg(eTWCfgUnencrypted(_T(""), eError::NON_FATAL|eError::SUPRESS_THIRD_MSG));
// Set the cross file systems flag appropriately.
cFSDataSourceIter::SetFileSystemCrossing(mpData->mbCrossFileSystems);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWModePolUpdate::Execute(cErrorQueue* pQueue)
{
try
{
// make sure all of the files are accessable
cFileUtil::TestFileWritable(mpData->mPolFile);
cFileUtil::TestFileWritable(mpData->mDbFile);
// open and parse the new policy file...
cGenreSpecListVector genreSpecList;
cTWCmdLineUtil::ParseTextPolicyFile(genreSpecList, mpData->mTextPolFile, mpData->mSiteKeyFile, pQueue);
cFCODatabaseFile dbFile;
cKeyFile localKeyfile, siteKeyfile;
cPrivateKeyProxy privateLocal, privateSite;
cTWUtil::OpenKeyFile(localKeyfile, mpData->mLocalKeyFile);
cTWUtil::OpenKeyFile(siteKeyfile, mpData->mSiteKeyFile);
// open the old database; note that ReadDatabase will set bEncrypted and open the
// key file if necessary
bool bDbEncrypted;
bool bPolEncrypted = true;
cTWUtil::ReadDatabase(mpData->mDbFile.c_str(), dbFile, localKeyfile.GetPublicKey(), bDbEncrypted);
//
// give a warning if the user that created the database is not the same as the user running right now...
//
TSTRING userName;
if( iFSServices::GetInstance()->GetCurrentUserName( userName ) )
{
if( dbFile.GetHeader().GetCreator().compare( userName.c_str() ) != 0 )
{
// TODO -- move these strings to the string table
TOSTRINGSTREAM str;
str << TSS_GetString( cTripwire, tripwire::STR_ERR2_DIFFERENT_USERS1) << dbFile.GetHeader().GetCreator()
<< TSS_GetString( cTripwire, tripwire::STR_ERR2_DIFFERENT_USERS2) << userName << std::ends;
cTWUtil::PrintErrorMsg( eICDifferentUsers( str.str(), eError::NON_FATAL ) );
}
}
// warn if the database is not encrytped
if(! bDbEncrypted && iUserNotify::GetInstance()->GetVerboseLevel() > iUserNotify::V_SILENT)
{
cTWUtil::PrintErrorMsg(eTWDbNotEncrypted(_T(""), eError::NON_FATAL|eError::SUPRESS_THIRD_MSG));
}
if( (!bDbEncrypted) && (mpData->mLocalProvided) )
{
//Issue a warning if a passphrase has been supplied, and the database is NOT encrypted.
cTWUtil::PrintErrorMsg( eTWPassForUnencryptedDb( _T(""), eError::NON_FATAL));
}
try
{
cFileManipulator polManipulator(mpData->mPolFile.c_str());
polManipulator.Init();
bPolEncrypted = polManipulator.GetEncoding() == cFileHeader::ASYM_ENCRYPTION;
}
catch (eError&) {}
//
// get the passwords for early prompting
//
if(! mpData->mbLatePassphrase)
{
if(bDbEncrypted)
cTWUtil::CreatePrivateKey(privateLocal, localKeyfile, mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0, cTWUtil::KEY_LOCAL);
if(bPolEncrypted)
cTWUtil::CreatePrivateKey(privateSite, siteKeyfile, mpData->mSiteProvided ? mpData->mSitePassphrase.c_str() : 0, cTWUtil::KEY_SITE);
}
/////////////////////////////////////////////////////////
// iterate through all the genres in the new policy...
/////////////////////////////////////////////////////////
cFCODatabaseFile::iterator dbIter( dbFile );
cGenreSpecListVector::iterator genreIter;
for (genreIter = genreSpecList.begin(); genreIter != genreSpecList.end(); ++genreIter)
{
// make this genre the current genre.
//
cGenreSwitcher::GetInstance()->SelectGenre( genreIter->GetGenre() );
//
// seek the database iter to this genre...
//
dbIter.SeekToGenre( genreIter->GetGenre() );
if( ! dbIter.Done() )
{
// perform the policy update
//
cPolicyUpdate pu( genreIter->GetGenre(), dbIter.GetSpecList(), genreIter->GetSpecList(), dbIter.GetDb(), pQueue );
uint32 puFlags = 0;
puFlags |= mpData->mbSecureMode ? cPolicyUpdate::FLAG_SECURE_MODE : 0;
puFlags |= ( mpData->mbResetAccessTime ? cPolicyUpdate::FLAG_ERASE_FOOTPRINTS_PU : 0 );
puFlags |= ( mpData->mbDirectIO ? cPolicyUpdate::FLAG_DIRECT_IO : 0 );
if( (! pu.Execute(puFlags)) && (mpData->mbSecureMode) )
{
// they were in secure mode and errors occured; an error condition
TCOUT << TSS_GetString( cTripwire, tripwire::STR_ERR_POL_UPDATE) << std::endl;
return 8;
}
// we need to update the database's prop set...
//
dbIter.GetSpecList() = genreIter->GetSpecList();
//
// TODO -- do some header stuff here?
//
}
else
{
// do a database init for this genre; it did not appear in the old policy...
//
TW_NOTIFY_NORMAL( TSS_GetString( cTripwire, tripwire::STR_PU_ADDING_GENRE ).c_str(),
cGenreSwitcher::GetInstance()->GenreToString( genreIter->GetGenre(), true ) );
dbFile.AddGenre( genreIter->GetGenre(), &dbIter );
// copy the spec list to the database...
dbIter.GetSpecList() = genreIter->GetSpecList();
// generate the database...
// TODO -- turn pQueue into an error bucket
uint32 gdbFlags = 0;
gdbFlags |= ( mpData->mbResetAccessTime ? cGenerateDb::FLAG_ERASE_FOOTPRINTS_GD : 0 );
gdbFlags |= ( mpData->mbDirectIO ? cGenerateDb::FLAG_DIRECT_IO : 0 );
cGenerateDb::Execute( dbIter.GetSpecList(), dbIter.GetDb(), dbIter.GetGenreHeader().GetPropDisplayer(), pQueue, gdbFlags );
//TODO -- what other prop displayer stuff do I have to do here?
}
}
//////////////////////////////////////////////////////////////////////////////////////
// now, we need to prune genres that are in the database but not in the new policy...
//////////////////////////////////////////////////////////////////////////////////////
for( dbIter.SeekBegin(); ! dbIter.Done(); )
{
bool bFoundGenre = false;
for (genreIter = genreSpecList.begin(); genreIter != genreSpecList.end(); ++genreIter)
{
if( genreIter->GetGenre() == dbIter.GetGenre() )
{
bFoundGenre = true;
break;
}
}
if( ! bFoundGenre )
{
// we need to remove this genre from the db...
//
dbIter.Remove();
}
else
dbIter.Next() ;
}
// let's go ahead and update the database and policy file...
//
// open the site key file and get the site passphrase
//
if(!privateSite.Valid() && bPolEncrypted)
cTWUtil::CreatePrivateKey(privateSite, siteKeyfile, mpData->mSiteProvided ? mpData->mSitePassphrase.c_str() : 0, cTWUtil::KEY_SITE);
//
// get the passphrase for the database, if necessary (it is important that we get both
// private keys before saving anything
//
if(bDbEncrypted && !privateLocal.Valid())
{
ASSERT(mpData->mbLatePassphrase);
cTWUtil::CreatePrivateKey(privateLocal, localKeyfile, mpData->mLocalProvided ? mpData->mLocalPassphrase.c_str() : 0, cTWUtil::KEY_LOCAL);
}
// backup the file we are about to overwrite
cFileUtil::BackupFile(mpData->mPolFile);
//
// write the policy file
//
cTWUtil::UpdatePolicyFile(mpData->mPolFile.c_str(), mpData->mTextPolFile.c_str(), bPolEncrypted, bPolEncrypted ? privateSite.GetKey() : 0);
//
// update the header info
//
// Note: We DO NOT call cFCODatabaseUtil::CalculateHeader() here. We only
// want to update these entries.
dbFile.GetHeader().SetCreator(cTWUtil::GetCurrentUser());
dbFile.GetHeader().SetPolicyFilename(mpData->mPolFile);
dbFile.GetHeader().SetConfigFilename(mstrConfigFile);
dbFile.GetHeader().SetLastDBUpdateTime( cSystemInfo::GetExeStartTime() );
// backup the file we are about to overwrite
cFileUtil::BackupFile(mpData->mDbFile);
//
// write the db to disk...
//
cTWUtil::WriteDatabase(mpData->mDbFile.c_str(), dbFile, bDbEncrypted, bDbEncrypted ? privateLocal.GetKey() : 0);
}
catch(eError& e)
{
if (mpData->mbLogToSyslog)
{
TSTRING msg = TSS_GetString( cTripwire, tripwire::STR_SYSLOG_POLUP_FAIL_MSG );
msg.append(cErrorTable::GetInstance()->Get( e.GetID() ));
cSyslog::Log(TSS_GetString( cTripwire, tripwire::STR_SYSLOG_IDENT ).c_str(), cSyslog::LOG_ERROR, msg.c_str());
}
cTWUtil::PrintErrorMsg(e);
return 8;
}
if (mpData->mbLogToSyslog)
{
TSTRING msg = TSS_GetString( cTripwire, tripwire::STR_SYSLOG_POLUP_MSG );
msg.append(mpData->mDbFile);
cSyslog::Log(TSS_GetString( cTripwire, tripwire::STR_SYSLOG_IDENT ).c_str(), cSyslog::LOG_SUCCESS, msg.c_str());
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// GetModeUsage
///////////////////////////////////////////////////////////////////////////////
TSTRING cTWModePolUpdate::GetModeUsage( void )
{
return ( TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_UPDATE_POLICY ) );
}
//#############################################################################
// cTWModeTest
//#############################################################################
class cTWModeTest_i : public cTWModeCommon
{
public:
cTWModeTest_i() : cTWModeCommon() { mMode=TEST_INVALID; }
enum TestMode { TEST_INVALID, TEST_EMAIL };
TestMode mMode;
TSTRING mEmailAddress;
};
///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeTest::cTWModeTest()
{
mpData = new cTWModeTest_i;
}
cTWModeTest::~cTWModeTest()
{
delete mpData;
}
///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeTest::InitCmdLineParser(cCmdLineParser& cmdLine)
{
// initialize the switches common to all modes...
InitCmdLineCommon(cmdLine);
// The only allowed test mode syntax so far is
// "tripwire --test --email name@domain.com" or "tripwire -t -e name@domain.com".
cmdLine.AddArg(cTWCmdLine::MODE_TEST, TSTRING(_T("")), TSTRING(_T("test")), cCmdLineParser::PARAM_NONE);
cmdLine.AddArg(cTWCmdLine::TEST_EMAIL, TSTRING(_T("e")), TSTRING(_T("email")), cCmdLineParser::PARAM_ONE);
cmdLine.AddDependency( cTWCmdLine::MODE_TEST, cTWCmdLine::TEST_EMAIL );
}
///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeTest::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
// first, fill out everything with the config file info...
FillOutConfigInfo(mpData, cf);
// now, parse the command line...
// this takes care of the common stuff...
FillOutCmdLineInfo(mpData, cmdLine);
cCmdLineIter iter(cmdLine);
for(iter.SeekBegin(); ! iter.Done(); iter.Next())
{
switch(iter.ArgId())
{
case cTWCmdLine::TEST_EMAIL:
mpData->mMode = cTWModeTest_i::TEST_EMAIL;
mpData->mEmailAddress = iter.ParamAt(0);
break;
default:
// should I do anything, or just ignore this?
;
}
}
// make sure that we have a valid operation
TEST_INIT_REQUIREMENT( ( mpData->mMode != cTWModeTest_i::TEST_INVALID ), cTripwire, tripwire::STR_ERR_NO_TEST_MODE );
if( mpData->mMode == cTWModeTest_i::TEST_EMAIL )
{
// make sure that we have a valid mail method
TEST_INIT_REQUIREMENT( ( cMailMessage::NO_METHOD != mpData->mMailMethod ), cTripwire, tripwire::STR_ERR_NO_MAIL_METHOD );
TEST_INIT_REQUIREMENT( ( cMailMessage::INVALID_METHOD != mpData->mMailMethod ), cTripwire, tripwire::STR_ERR_INVALID_MAIL_METHOD );
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWModeTest::Execute(cErrorQueue* pQueue)
{
cDebug d("cTWModeTest::Execute");
switch (mpData->mMode)
{
case cTWModeTest_i::TEST_EMAIL:
{
cTWCmdLineUtil::SendEmailTestMessage(mpData->mEmailAddress, mpData);
}
default:
// should I do anything, or just ignore this?
;
}
return 0;
}
TSTRING cTWModeTest::GetModeUsage( )
{
return ( TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_TEST ) );
}
//#############################################################################
// cTWModeHelp
//#############################################################################
class cTWModeHelp_i : public cTWModeCommon
{
public:
cTWModeHelp_i() {}
~cTWModeHelp_i() {}
std::set< TSTRING > mModes;
// A set of modes to output usage statements for a specific mode.
std::set< TSTRING > mPrinted;
// A set for making sure we don't print the same help twice.
};
///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeHelp::cTWModeHelp()
{
mpData = new cTWModeHelp_i();
}
cTWModeHelp::~cTWModeHelp()
{
delete mpData;
}
///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeHelp::InitCmdLineParser(cCmdLineParser& cmdLine)
{
// We're only interested in one parameter, that being help. Anything else
// passed to this mode should be a cmdlineparser error.
cmdLine.AddArg(cTWCmdLine::MODE, TSTRING(_T("m")), TSTRING(_T("")), cCmdLineParser::PARAM_MANY, true);
cmdLine.AddArg(cTWCmdLine::MODE_HELP, TSTRING(_T("?")), TSTRING(_T("help")), cCmdLineParser::PARAM_MANY);
cmdLine.AddArg(cTWCmdLine::MODE_HELP_ALL, TSTRING(_T("")), TSTRING(_T("all")), cCmdLineParser::PARAM_MANY);
cmdLine.AddArg(cTWCmdLine::MODE_INIT, TSTRING(_T("i")), TSTRING(_T("init")), cCmdLineParser::PARAM_MANY);
cmdLine.AddArg(cTWCmdLine::MODE_CHECK, TSTRING(_T("c")), TSTRING(_T("check")), cCmdLineParser::PARAM_MANY);
cmdLine.AddArg(cTWCmdLine::MODE_UPDATE_DB, TSTRING(_T("u")), TSTRING(_T("update")), cCmdLineParser::PARAM_MANY);
cmdLine.AddArg(cTWCmdLine::MODE_UPDATE_POL, TSTRING(_T("p")), TSTRING(_T("update-policy")), cCmdLineParser::PARAM_MANY);
cmdLine.AddArg(cTWCmdLine::MODE_TEST, TSTRING(_T("t")), TSTRING(_T("test")), cCmdLineParser::PARAM_MANY);
cmdLine.AddArg(cTWCmdLine::MODE_EXPLORE, TSTRING(_T("")), TSTRING(_T("explore")), cCmdLineParser::PARAM_MANY);
cmdLine.AddArg(cTWCmdLine::MODE_DEBUG, TSTRING(_T("")), TSTRING(_T("verify")), cCmdLineParser::PARAM_MANY);
}
///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeHelp::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
cCmdLineIter iter(cmdLine);
// Grab the arguments from the help parameter:
for(iter.SeekBegin(); ! iter.Done(); iter.Next())
{
switch(iter.ArgId())
{
case cTWCmdLine::MODE_HELP:
{
int i;
for( i = 0; i < iter.NumParams(); ++i )
{
mpData->mModes.insert( iter.ParamAt(i) );
}
}
break;
case cTWCmdLine::MODE:
{
int i;
for( i = 0; i < iter.NumParams(); ++i )
{
mpData->mModes.insert( iter.ParamAt(i) );
}
}
break;
// allow users to enter the mode
// names with "--" prepended. We have to do this, since
// the cmdlineparser treats them as switches.
case cTWCmdLine::MODE_HELP_ALL: // fall through
case cTWCmdLine::MODE_INIT: // fall through
case cTWCmdLine::MODE_CHECK: // fall through
case cTWCmdLine::MODE_UPDATE_DB: // fall through
case cTWCmdLine::MODE_UPDATE_POL: // fall through
case cTWCmdLine::MODE_TEST: // fall through
case cTWCmdLine::MODE_EXPLORE: // fall through
case cTWCmdLine::MODE_DEBUG:
{
int i;
// push back the parameter that was actually passed.
TSTRING str = iter.ActualParam();
// Kill off the initial "--" or "-"
str.erase( 0, 1 );
if( str.length() != 1)
str.erase( 0, 1 );
mpData->mModes.insert( str );
for( i = 0; i < iter.NumParams(); ++i )
mpData->mModes.insert( iter.ParamAt(i) );
}
break;
default:
// should I do anything, or just ignore this?
// cmdlineparser will catch the bad args.
;
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWModeHelp::Execute(cErrorQueue* pQueue)
{
cDebug d("cTWModeHelp::Execute");
// The iterator we will use to traverse the list of arguments:
std::set<TSTRING>::iterator it = mpData->mModes.begin();
// We'll want to output the version information, regardless:
TCOUT << TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_VERSION) << std::endl;
TCOUT << TSS_GetString( cTW, tw::STR_VERSION) << std::endl;
if( it == mpData->mModes.end() ) // all that was passed was --help
{
// Output a short usage summary for each mode.
TCOUT << TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_USAGE_SUMMARY );
//
// That's it, return.
return 8;
}
for( ; it != mpData->mModes.end(); ++it )
{
if( _tcscmp( (*it).c_str(), _T("all")) == 0 )
{
//Since --help all was passed, emit all help messages and return.
TCOUT<< TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_INIT );
TCOUT<< TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_CHECK );
TCOUT<< TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_UPDATE );
TCOUT<< TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_UPDATE_POLICY );
TCOUT<< TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_TEST );
#ifdef DEBUG
// TODO: Do we need help messages for these modes? DRA
#endif
//We're done, return
return 8;
}
}
//We need some subset of the usage statements. Figure out which modes have
//been specified:
it = mpData->mModes.begin();
for( ; it != mpData->mModes.end(); ++it )
{
// This is going to be ugly code...
if ( _tcscmp( (*it).c_str(), _T("init") ) == 0 || _tcscmp( (*it).c_str(), _T("i") ) == 0 )
{
//make sure we don't print the same help twice...
if( mpData->mPrinted.find( _T("init") ) == mpData->mPrinted.end() )
{
TCOUT<< TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_INIT );
mpData->mPrinted.insert( _T("init") );
}
}
else if( _tcscmp( (*it).c_str(), _T("check") ) == 0 || _tcscmp( (*it).c_str(), _T("c") ) == 0 )
{
if( mpData->mPrinted.find( _T("check") ) == mpData->mPrinted.end() )
{
TCOUT<< TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_CHECK );
mpData->mPrinted.insert( _T("check") );
}
}
else if( _tcscmp( (*it).c_str(), _T("update") ) == 0 || _tcscmp( (*it).c_str(), _T("u") ) == 0 )
{
if( mpData->mPrinted.find( _T("update") ) == mpData->mPrinted.end() )
{
TCOUT<< TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_UPDATE );
mpData->mPrinted.insert( _T("update") );
}
}
else if( _tcscmp( (*it).c_str(), _T("update-policy") ) == 0 || _tcscmp( (*it).c_str(), _T("p") ) == 0 )
{
if( mpData->mPrinted.find( _T("update-policy") ) == mpData->mPrinted.end() )
{
TCOUT<< TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_UPDATE_POLICY );
mpData->mPrinted.insert( _T("update-policy") );
}
}
else if( _tcscmp( (*it).c_str(), _T("test") ) == 0 || _tcscmp( (*it).c_str(), _T("t") ) == 0 )
{
if( mpData->mPrinted.find( _T("test") ) == mpData->mPrinted.end() )
{
TCOUT<< TSS_GetString( cTripwire, tripwire::STR_TRIPWIRE_HELP_TEST );
mpData->mPrinted.insert( _T("test") );
}
}
else if( _tcscmp( (*it).c_str(), _T("explore") ) == 0 )
TCOUT<< _T(""); //we currently don't have usage statements for this mode.
else if( _tcscmp( (*it).c_str(), _T("verify") ) == 0 )
TCOUT<< _T(""); //we currently don't have usage statements for this mode.
else
{
cTWUtil::PrintErrorMsg( eTWHelpInvalidModeSwitch((*it), eError::NON_FATAL) );
TCOUT << std::endl;
// emit error string, this mode does not exist
}
}
//Everything went okay
return 8;
}
//================================================================
// UTIL FUNCTIONS
//================================================================
bool util_GetEditor( TSTRING& strEd )
{
// see if VISUAL environment var is set
TCHAR* pchEd = _tgetenv( _T("VISUAL") );
if( pchEd != NULL && pchEd[0] != _T('\0') )
{
strEd = pchEd;
return true;
}
// see if EDITOR environment var is set
pchEd = _tgetenv( _T("EDITOR") );
if( pchEd != NULL && pchEd[0] != _T('\0') )
{
strEd = pchEd;
return true;
}
return false;
}
//Encase the explore and debug modes in this ifdef, since they are for internal
//use only.
#ifdef DEBUG
//#############################################################################
// cTWModeExploreDb
//#############################################################################
#include "tw/dbexplore.h"
class cTWModeExploreDb_i : public cTWModeCommon
{
public:
cTWModeExploreDb_i() : cTWModeCommon() {}
};
///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeExploreDb::cTWModeExploreDb()
{
mpData = new cTWModeExploreDb_i;
}
cTWModeExploreDb::~cTWModeExploreDb()
{
delete mpData;
}
///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeExploreDb::InitCmdLineParser(cCmdLineParser& cmdLine)
{
// initialize the switches common to all modes...
InitCmdLineCommon(cmdLine);
cmdLine.AddArg(cTWCmdLine::MODE_EXPLORE, TSTRING(_T("")), TSTRING(_T("explore")), cCmdLineParser::PARAM_NONE);
}
///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeExploreDb::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
// first, fill out everything with the config file info...
FillOutConfigInfo(mpData, cf);
// now, parse the command line...
// this takes care of the common stuff...
FillOutCmdLineInfo(mpData, cmdLine);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWModeExploreDb::Execute(cErrorQueue* pQueue)
{
cDebug d("cTWModeExploreDb::Execute");
cFCODatabaseFile dbFile;
cFCODatabaseFile::iterator dbIter( dbFile );
try
{
cKeyFile localKeyfile;
cTWUtil::OpenKeyFile(localKeyfile, mpData->mLocalKeyFile);
bool bEncrypted;
cTWUtil::ReadDatabase(mpData->mDbFile.c_str(), dbFile, localKeyfile.GetPublicKey(), bEncrypted);
//
// seek to the first genre in the db...
//
dbIter.SeekBegin();
if( dbIter.Done() )
{
//TODO -- what is the right thing to do here?
//
ASSERT( false );
throw eTWDbHasNoGenres();
}
//
// ok, time to explore the database!
//
cDbExplore::Execute( dbIter );
}
catch(eError& e)
{
cTWUtil::PrintErrorMsg(e);
return 8;
}
return 0;
}
//#############################################################################
// cTWModeDebugDb
//#############################################################################
#include "tw/dbdebug.h"
class cTWModeDebugDb_i : public cTWModeCommon
{
public:
cTWModeDebugDb_i() : cTWModeCommon() {}
};
///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cTWModeDebugDb::cTWModeDebugDb()
{
mpData = new cTWModeDebugDb_i;
}
cTWModeDebugDb::~cTWModeDebugDb()
{
delete mpData;
}
///////////////////////////////////////////////////////////////////////////////
// InitCmdLineParser
///////////////////////////////////////////////////////////////////////////////
void cTWModeDebugDb::InitCmdLineParser(cCmdLineParser& cmdLine)
{
// initialize the switches common to all modes...
InitCmdLineCommon(cmdLine);
cmdLine.AddArg(cTWCmdLine::MODE_DEBUG, TSTRING(_T("")), TSTRING(_T("verifydb")), cCmdLineParser::PARAM_NONE);
}
///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
bool cTWModeDebugDb::Init(const cConfigFile& cf, const cCmdLineParser& cmdLine)
{
// first, fill out everything with the config file info...
FillOutConfigInfo(mpData, cf);
// now, parse the command line...
// this takes care of the common stuff...
FillOutCmdLineInfo(mpData, cmdLine);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Execute
///////////////////////////////////////////////////////////////////////////////
int cTWModeDebugDb::Execute(cErrorQueue* pQueue)
{
cDebug d("cTWModeDebugDb::Execute");
cFCODatabaseFile dbFile;
cFCODatabaseFile::iterator dbIter( dbFile );
try
{
cKeyFile localKeyfile;
cTWUtil::OpenKeyFile(localKeyfile, mpData->mLocalKeyFile);
bool bEncrypted;
cTWUtil::ReadDatabase(mpData->mDbFile.c_str(), dbFile, localKeyfile.GetPublicKey(), bEncrypted);
//
// seek to the first genre in the db...
//
dbIter.SeekBegin();
if( dbIter.Done() )
{
//TODO -- what is the right thing to do here?
//
ASSERT( false );
throw eTWDbHasNoGenres();
}
//
// debug the database.
//
cDbDebug::Execute( dbIter, mpData->mDbFile );
}
catch(eError& e)
{
cTWUtil::PrintErrorMsg(e);
return 8;
}
return 0;
}
#endif //DEBUG