Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

main.cpp 30KB

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