You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.cpp 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. // main.cpp
  2. // SNF MDaemon Plugin
  3. // Copyright (C) 2007-2008, ARM Research Labs, LLC.
  4. #include <tchar.h>
  5. #include <winerror.h>
  6. #include <wx/app.h>
  7. #include <wx/window.h>
  8. #include <wx/dialog.h>
  9. #include <wx/toplevel.h>
  10. #include "dialogapp.h"
  11. #include "configdialog.h"
  12. #include <winsock2.h>
  13. #include <windows.h>
  14. #include "mdconfiguration.hpp"
  15. #include "SNFMulti/SNFMulti.hpp"
  16. #ifndef _UNICODE
  17. #define _UNICODE
  18. #endif
  19. #ifndef UNICODE
  20. #define UNICODE
  21. #endif
  22. //IMPLEMENT_APP_NO_MAIN(dialogapp);
  23. //IMPLEMENT_WX_THEME_SUPPORT;
  24. ////////////////////////////////////////////////////////////////////////////////
  25. // Constants And Tweaks
  26. ////////////////////////////////////////////////////////////////////////////////
  27. const int MDPLUGIN_MSG = 22000;
  28. const int MDPLUGIN_DISPLAY = 22001;
  29. const char* PLUGIN_VERSION_INFO = "SNF MDaemon Plugin Version 4.0 Build: " __DATE__ " " __TIME__;
  30. BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { // Thread attach/detach functions.
  31. switch (fdwReason) { // Switch on the reason for the call.
  32. case DLL_PROCESS_ATTACH:
  33. { // Attach to process.
  34. /*** Startup function moved to API ***/
  35. } // We will assume it worked.
  36. break;
  37. case DLL_PROCESS_DETACH: // Detach from process.
  38. /*** Shutdown function moved to API ***/
  39. return true; // We will assume that worked.
  40. break;
  41. case DLL_THREAD_ATTACH: // Attach to thread
  42. break; // Just be happy.
  43. case DLL_THREAD_DETACH: // Detach from thread
  44. break; // Just be happy.
  45. }
  46. return TRUE; // Be happy by default.
  47. }
  48. ////////////////////////////////////////////////////////////////////////////////
  49. // SNF MDaemon Plugin Engine Code
  50. ////////////////////////////////////////////////////////////////////////////////
  51. // Handy Debug Functions. This is how we can display critical events on screen
  52. // and emit entries into the MDaemon logs.
  53. HWND LatestParent = 0; // Handle MDaemon log operations.
  54. void sayToScreen(const string StuffToSay) { // Display a modal system message.
  55. MessageBox ( // Use a message box.
  56. GetFocus(), // Take the user's focus.
  57. (LPCWSTR) StuffToSay.c_str(), // This is what to say.
  58. (LPCWSTR) _T("Message Sniffer Plug-In"), // This is how to title the box.
  59. MB_OK|MB_SYSTEMMODAL ); // This makes it modal with a button.
  60. }
  61. void sayToLog(const string Msg) { // Emit message into MDaemon log.
  62. if(0 != LatestParent) { // If we have a handle:
  63. COPYDATASTRUCT Packet; // Create a packet for the log.
  64. Packet.dwData = MDPLUGIN_DISPLAY;
  65. Packet.cbData = Msg.length();
  66. Packet.lpData = reinterpret_cast<void*>(
  67. const_cast<char*>(Msg.c_str()));
  68. SendMessage(LatestParent, MDPLUGIN_MSG, MDPLUGIN_DISPLAY, // Send the packet.
  69. (LPARAM)(PCOPYDATASTRUCT)&Packet);
  70. }
  71. }
  72. // The configuration file path is theoretically in a fixed location based on
  73. // the installation directory of MDaemon. We read the installation directory
  74. // from the registry and derive the path from that. If we get what we want
  75. // then we return the full path to the SNFMDPlugin.xml file - which is derived
  76. // from snf_engine.xml. If we don't get what we want then we return nothing.
  77. string ConfigurationFilePath() { // Get the config file path.
  78. std::wstring install_pathW;
  79. HKEY hKey;
  80. WCHAR szBuffer[512];
  81. DWORD dwBufferSize = sizeof(szBuffer);
  82. std::wstring regValue = L"";
  83. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Alt-N Technologies\\MDaemon", 0, KEY_READ, &hKey)){
  84. if(ERROR_SUCCESS == RegQueryValueEx(hKey, (LPCWSTR) L"AppPath", NULL, NULL, (LPBYTE) &szBuffer, &dwBufferSize)){
  85. RegCloseKey(hKey);
  86. regValue = std::wstring(szBuffer);
  87. std::wstring termCheck;
  88. for(auto val: regValue){
  89. if(val != L'\0'){
  90. termCheck += val;
  91. }
  92. }
  93. if(termCheck.size() == 512){
  94. regValue = L""; // RegValue wasn't null terminated
  95. }
  96. else{
  97. int PathEnd = regValue.length(); // Grab the length.
  98. if(PathEnd > 0) { regValue.erase(PathEnd-1,1); } // Eat the stroke at the end.
  99. PathEnd = regValue.find_last_of(L"\\"); // Find the next stroke in.
  100. int PathLength = regValue.length(); // Grab the path length.
  101. int EraseSpan = PathLength - (PathEnd+1); // Calculate the amount to erase.
  102. regValue.erase(PathEnd+1,EraseSpan); // Erase it to get the root path.
  103. regValue.append(L"SNF\\snfmdplugin.xml");
  104. }
  105. }
  106. }
  107. //convert wstring to utf8
  108. size_t buffer_size = WideCharToMultiByte(CP_UTF8, 0, &regValue[0], (int)regValue.size(), NULL, 0, NULL, NULL);
  109. std::string strFilePath(buffer_size, '\0');
  110. WideCharToMultiByte(CP_UTF8, 0, &regValue[0], (int)regValue.size(), &strFilePath[0], buffer_size, NULL, NULL);
  111. // Return either the empty string or the configuration file path.
  112. return strFilePath; // Return what we have.
  113. }
  114. // Here are out engine components, startup, and shutdown logic. The actual
  115. // engine components are built as the DLL is loaded. They are "lit up" by the
  116. // startup() function - which is called when an application attaches to this
  117. // DLL; and they are closed down when an application detaches from this DLL.
  118. // 20080311 _M Added configuration interpreter for <platform/> and linked it
  119. // to the ConfigFunc() and MessageIPFunc() functions.
  120. volatile bool EngineIsGood = false; // True when things are ready to go.
  121. snf_RulebaseHandler* Rulebase = 0; // Our Rulebase Handler.
  122. snf_EngineHandler* ScanEngine = 0; // Our Scan Engine.
  123. int Generation = -1; // Configuration generation tag.
  124. string Configuration; // Configuration file path.
  125. MDConfiguration* PlatformConfig = 0; // Platform config interpreter.
  126. extern "C" __declspec(dllexport)void _stdcall StartupFunc(HWND Parent){ // How to get all this started.
  127. LatestParent = Parent;
  128. EngineIsGood = false; // Start off pessimistically.
  129. try { // Be sure to catch any exceptions.
  130. Rulebase = new snf_RulebaseHandler(); // Create our rulebase handler.
  131. ScanEngine = new snf_EngineHandler(); // Create our engine scanner.
  132. Configuration = ConfigurationFilePath(); // Get the configuration path.
  133. PlatformConfig = new MDConfiguration(*Rulebase, Configuration); // Set up our configuration monitor.
  134. Rulebase->open(Configuration.c_str(), "", ""); // Open a configured rulebase.
  135. Rulebase->PlatformVersion(PLUGIN_VERSION_INFO); // Set the Platform version string.
  136. ScanEngine->open(Rulebase); // Open the scanning engine.
  137. EngineIsGood = true; // If all went well, we're up!
  138. sayToLog(Rulebase->EngineVersion()); // version info to show we're ok.
  139. sayToLog(Rulebase->PlatformVersion()); // Log our platform and engine
  140. string ConfigInfo = "SNF Config: "; // Build a configuration info
  141. ConfigInfo.append(Configuration); // message and display that
  142. sayToLog(ConfigInfo); // too.
  143. }
  144. catch(snf_RulebaseHandler::ConfigurationError) { // Can't work with config file!
  145. string ErrorMessage = "Unable to Configure with: "; // Tell them about it and give
  146. ErrorMessage.append(Configuration); // them a hint where we looked.
  147. sayToScreen(ErrorMessage);
  148. }
  149. catch(snf_RulebaseHandler::FileError) { // Can't load the rulebase file!
  150. string ErrorMessage = "Unable to Load Rulebase in: "; // Tell them about it and give
  151. ErrorMessage.append(Configuration); // them a hint where we looked.
  152. sayToScreen(ErrorMessage);
  153. }
  154. catch(snf_RulebaseHandler::AllocationError) { // Can't allocate memory!
  155. sayToScreen("Unable to Allocate Enough Memory!"); // Tell them about it.
  156. }
  157. catch(snf_RulebaseHandler::IgnoreListError) { // Can't load ignore list!
  158. sayToScreen("Unable to Load Ingore List!"); // Tell them about it.
  159. }
  160. catch(snf_RulebaseHandler::AuthenticationError) { // Can't authenticate!
  161. sayToScreen("Unable to Authenticate Rulebase!"); // Tell them about it.
  162. }
  163. catch(snf_RulebaseHandler::Busy) { // Busy?!! That's weird.
  164. sayToScreen("Busy Exception?!!"); // Tell them about it.
  165. }
  166. catch(snf_RulebaseHandler::Panic) { // Panic?!! That's weird.
  167. sayToScreen("Panic Exception?!!"); // Tell them about it.
  168. }
  169. catch(exception& e) { // Some other runtime error?
  170. sayToScreen(e.what()); // Tell them what it was.
  171. }
  172. catch(...) { // Catch the unknown exception.
  173. sayToScreen("Unexpected Exception!"); // Tell them things didn't work.
  174. }
  175. return; // All done.
  176. }
  177. /****** THE FOLLOWING IS DEFUNCT, BUT USEFUL INFO SO WE KEEP IT! ******/
  178. /**** *Note: Why do we always return true even when we fail???
  179. ***** Because, in a DLL that is being loaded, none of the threads that
  180. ***** get created in the rulebase object will get any cycles until the
  181. ***** DLL load sequence is complete _AND_SUCCESSFUL_!
  182. *****
  183. ***** The same is true when we return false -- because then the OS KNOWS
  184. ***** that it's not safe to run any of the threads we've created.
  185. *****
  186. ***** As a result, if we try to destroy our rulebase manager after an
  187. ***** unsuccessful load then all of the stop() calls to our threads will
  188. ***** block waiting for the initialized threads to see their stop bits and
  189. ***** end. Since none of these threads actually get any love until the
  190. ***** DLL is successfully loaded, we end up waiting happily for ever!
  191. *****
  192. ***** Instead of stepping into this world of hurt, we've decided to leave
  193. ***** the internal flag set to indicate that the engine is Not-Good so that
  194. ***** the scanning functions remain inert, and then we wait patiently for
  195. ***** the DLL to be unloaded.
  196. *****
  197. ***** When that happens, since the OS KNOWS the DLL was loaded happily
  198. ***** before, the objects can go through their normal destruction without
  199. ***** deadlocking on their stop() functions (join()).
  200. ****/
  201. extern "C" __declspec(dllexport)void _stdcall ShutdownFunc(HWND Parent){ // How to get all this stopped.
  202. LatestParent = Parent;
  203. if(EngineIsGood) { // If the engine is good:
  204. try {
  205. EngineIsGood = false; // Stop using the engine now.
  206. LatestParent = 0; // No more logging calls.
  207. if(ScanEngine) { // Close the scanner if it exists:
  208. ScanEngine->close(); // Close it.
  209. delete ScanEngine; // Delete it.
  210. ScanEngine = 0; // Forget it.
  211. }
  212. if(Rulebase) { // Close the rulebase if it exists.
  213. Rulebase->close(); // Close it.
  214. delete Rulebase; // Delete it.
  215. Rulebase = 0; // Forget it.
  216. }
  217. if(PlatformConfig) { // Drop the PlatformConfig if it be.
  218. delete PlatformConfig; // Delete it.
  219. PlatformConfig = 0; // Forget it.
  220. }
  221. Generation = -1; // Reset our config gen tag.
  222. sayToLog("SNF: Plugin Shutdown."); // Tell them we're shut down.
  223. }
  224. catch(exception& e) { // If we have a runtime exception
  225. string InShutdown = "SNF, Shutdown: "; // make a human friendly message
  226. InShutdown.append(e.what()); // and package the exception for
  227. sayToScreen(InShutdown); // a screen pop-up.
  228. }
  229. catch(...) { // If we see some other kind of
  230. sayToScreen("SNF, Shutdown: Unknown Exception"); // exception then show that.
  231. }
  232. }
  233. return; // All done.
  234. }
  235. // Now we get to the business end of the plugin. Three functions are exported.
  236. // ConfigFunc() launches an editor for the configuration file.
  237. // 20080311 _M Adjusted ConfigFunc to use Platform Configuration Parameters.
  238. //DLL_EXPORT void __stdcall ConfigFunc(HWND Parent) { // Configuration function.
  239. extern "C" __declspec(dllexport)void _stdcall ConfigFunc(HWND Parent) {
  240. // Initialize application.
  241. wxApp::SetInstance(new dialogapp());
  242. wxEntryStart(0, NULL);
  243. wxTheApp->CallOnInit();
  244. wxTheApp->OnRun();
  245. wxTheApp->OnExit();
  246. wxEntryCleanup();
  247. }
  248. // MessageFunc() scans a message file and injects headers.
  249. extern "C" __declspec(dllexport)void _stdcall PostMessageFunc(HWND Parent, const char* File){ // Message Scan Function.
  250. LatestParent = Parent; // Capture the parent for logging.
  251. string LogMessage = "SNF MessageScan: "; // Start a plugin log entry.
  252. LogMessage.append(File); // add the file name.
  253. if(false == EngineIsGood) { // If the engine is not up then
  254. LogMessage.append(", Engine Not Ready!"); // report that in the plug-in log.
  255. sayToLog(LogMessage); // Post our entry to the plug-in log.
  256. return; // We're done.
  257. }
  258. // We are ready to begin our scan.
  259. int ResultCode = 0; // Keep track of the scan result.
  260. try { // Be sure to catch any exceptions.
  261. ResultCode = ScanEngine->scanMessageFile(File);
  262. }
  263. catch(snf_EngineHandler::FileError& e) { // Exception when a file won't open.
  264. LogMessage.append(", File Error!");
  265. sayToLog(LogMessage);
  266. string DebugData = "SNF Debug: ";
  267. DebugData.append(e.what());
  268. sayToLog(DebugData);
  269. return;
  270. }
  271. catch(snf_EngineHandler::XHDRError& e) { // Exception when XHDR Inject/File fails.
  272. LogMessage.append(", X-Header Error!");
  273. sayToLog(LogMessage);
  274. string DebugData = "SNF Debug: ";
  275. DebugData.append(e.what());
  276. sayToLog(DebugData);
  277. return;
  278. }
  279. catch(snf_EngineHandler::BadMatrix& e) { // Exception out of bounds of matrix.
  280. LogMessage.append(", Bad Matrix!");
  281. sayToLog(LogMessage);
  282. string DebugData = "SNF Debug: ";
  283. DebugData.append(e.what());
  284. sayToLog(DebugData);
  285. return;
  286. }
  287. catch(snf_EngineHandler::MaxEvals& e) { // Exception too many evaluators.
  288. LogMessage.append(", Too Many Evaluators!");
  289. sayToLog(LogMessage);
  290. string DebugData = "SNF Debug: ";
  291. DebugData.append(e.what());
  292. sayToLog(DebugData);
  293. return;
  294. }
  295. catch(snf_EngineHandler::AllocationError& e) { // Exception when we can't allocate something.
  296. LogMessage.append(", Allocation Error!");
  297. sayToLog(LogMessage);
  298. string DebugData = "SNF Debug: ";
  299. DebugData.append(e.what());
  300. sayToLog(DebugData);
  301. return;
  302. }
  303. catch(snf_EngineHandler::Busy& e) { // Exception when there is a collision.
  304. LogMessage.append(", Engine Busy!");
  305. sayToLog(LogMessage);
  306. string DebugData = "SNF Debug: ";
  307. DebugData.append(e.what());
  308. sayToLog(DebugData);
  309. return;
  310. }
  311. catch(snf_EngineHandler::Panic& e) { // Exception when something else happens.
  312. LogMessage.append(", Engine Panic!");
  313. sayToLog(LogMessage);
  314. string DebugData = "SNF Debug: ";
  315. DebugData.append(e.what());
  316. sayToLog(DebugData);
  317. return;
  318. }
  319. catch(exception& e) { // Some unexpected exception.
  320. LogMessage.append(", Unexpected Exception!");
  321. sayToLog(LogMessage);
  322. string DebugData = "SNF Debug: ";
  323. DebugData.append(e.what());
  324. sayToLog(DebugData);
  325. return;
  326. }
  327. catch(...) { // We should never get one of these, but
  328. LogMessage.append(", Non-Std Exception!"); // if we do we have something to say.
  329. sayToLog(LogMessage);
  330. return;
  331. }
  332. // At this point we know our scan worked ok. So, let's post the result.
  333. ostringstream O; // A stringstream makes it easy to
  334. O << LogMessage << ", Result=" << ResultCode; // format our plug-in log entry.
  335. sayToLog(O.str()); // Then we post it to the plug-in log.
  336. }
  337. // ControlFilePath(MessageFilePath) Converts .msg path to .ctl path.
  338. string ControlFilePath(string MessageFilePath) { // Convert .msg/.tmp to .ctl path.
  339. const string ControlFileExt(".ctl"); // Control file extension.
  340. const string EmptyString(""); // The empty string.
  341. string ControlFilePath(MessageFilePath); // Start with the message path.
  342. string::size_type CFExtPosition = ControlFilePath.find_last_of('.'); // Find the extension.
  343. if(string::npos == CFExtPosition) return EmptyString; // If we didn't find it return ""
  344. ControlFilePath.replace( // Replace the message file
  345. CFExtPosition, ControlFileExt.length(), // extension with the control
  346. ControlFileExt // file extension.
  347. );
  348. return ControlFilePath;
  349. }
  350. // MessageIPFunc() looks at the control file for a message, extracts the
  351. // source IP, and deletes the message if the IP is in the truncate range.
  352. extern "C" __declspec(dllexport) void _stdcall SMTPMessageFunc(HWND Parent, const char* File){ // IP Scan Function.
  353. //LatestParent = Parent; // Capture the parent for logging.
  354. if(false == PlatformConfig->MessageIPFuncOn()) { // If the IP func is off:
  355. return; // We're done.
  356. }
  357. string LogMessage = "SNF IPScan: "; // Start a plugin log entry.
  358. LogMessage.append(File); // add the file name.
  359. if(false == EngineIsGood) { // If the engine is not up then
  360. LogMessage.append(", Engine Not Ready!"); // report that in the plug-in log.
  361. sayToLog(LogMessage); // Post our entry to the plug-in log.
  362. return; // We're done.
  363. }
  364. // Open the control file and find the RemoteIP
  365. const string CtlRemoteIP("RemoteIP="); // This is what we're looking for.
  366. string RemoteIP = ""; // This is where it will go.
  367. try { // Do this safely.
  368. ifstream ControlFile(ControlFilePath(File).c_str()); // Open the control file.
  369. while(ControlFile) { // While the file is good...
  370. string Line; // Read one line at a time
  371. getline(ControlFile, Line); // into a string.
  372. string::size_type TagPos = Line.find(CtlRemoteIP); // Look for the RemoteIP tag.
  373. if(string::npos != TagPos) { // If we find the RemoteIP tag:
  374. RemoteIP = Line.substr(TagPos + CtlRemoteIP.length()); // Get the string after the tag
  375. break; // We're done with the ctl file!
  376. }
  377. }
  378. ControlFile.close(); // Be nice and close our file.
  379. }
  380. catch(...) {} // Eat any exceptions.
  381. if(0 == RemoteIP.length()) { // If we didn't get the IP then
  382. LogMessage.append(", Didn't Get Remote IP!"); // make that the rest of our log
  383. sayToLog(LogMessage); // entry and post it.
  384. return; // We're done.
  385. }
  386. // At this point we have the remote IP to check.
  387. LogMessage.append(", "); LogMessage.append(RemoteIP); // Add the IP to the log entry.
  388. IPTestRecord IPAnalysis(RemoteIP); // Set up a test record.
  389. try { // Safely,
  390. Rulebase->performIPTest(IPAnalysis); // check the IP w/ GBUdb.
  391. }
  392. catch(...) { // If we encountered a problem
  393. LogMessage.append(", Analysis Failed!"); // then we need to report that
  394. sayToLog(LogMessage); // our analysis failed.
  395. return; // We're done.
  396. }
  397. // At this point we've got a good analysis, so we take action (if needed)
  398. // and produce a helpful log entry.
  399. ostringstream Happy; // A stringstream for easy formatting.
  400. Happy << LogMessage << ", {"; // Start the analysis report.
  401. switch(IPAnalysis.G.Flag()) { // Identify the flag data for this IP.
  402. case Good: Happy << "Good, "; break;
  403. case Bad: Happy << "Bad, "; break;
  404. case Ugly: Happy << "Ugly, "; break;
  405. case Ignore: Happy << "Ignore, "; break;
  406. }
  407. Happy << "p=" << IPAnalysis.G.Probability() << ", " // Probability and Confidence.
  408. << "c=" << IPAnalysis.G.Confidence() << ", ";
  409. switch(IPAnalysis.R) { // The Range analysis.
  410. case snfIPRange::Unknown: { Happy << " Unknown}"; break; } // Unknown - not defined.
  411. case snfIPRange::White: { Happy << " White}"; break; } // This is a good guy.
  412. case snfIPRange::Normal: { Happy << " Normal}"; break; } // Benefit of the doubt.
  413. case snfIPRange::New: { Happy << " New}"; break; } // It is new to us.
  414. case snfIPRange::Caution: { Happy << " Caution}"; break; } // This is suspicious.
  415. case snfIPRange::Black: { Happy << " Black}"; break; } // This is bad.
  416. case snfIPRange::Truncate: { Happy << " Truncate}"; break; } // This is bad unless we ignore it.
  417. }
  418. string WhatWeDid; // So, what do we do?
  419. if( // Are we in Truncate land?
  420. (snfIPRange::Truncate == IPAnalysis.R && Ugly == IPAnalysis.G.Flag()) || // If the IP is Truncate & Ugly, or
  421. Bad == IPAnalysis.G.Flag() // If the IP is just plain bad then
  422. ) { // we are in truncate land so we
  423. Happy << " Rejected!"; WhatWeDid = "Rejected"; // mark the message Rejected! and
  424. remove(File); // remove the file to reject it!
  425. } else { // If we are not rejecting the
  426. Happy << " Allowed."; WhatWeDid = "Allowed"; // message tell them it's allowed.
  427. }
  428. Rulebase->logThisIPTest(IPAnalysis, WhatWeDid); // Log the event.
  429. sayToLog(Happy.str()); // Post to the plug-in log.
  430. }