| 1 | // CheckEventLog.cpp : Defines the entry point for the DLL application. |
|---|
| 2 | // |
|---|
| 3 | |
|---|
| 4 | #include "stdafx.h" |
|---|
| 5 | #include "CheckEventLog.h" |
|---|
| 6 | #include <strEx.h> |
|---|
| 7 | #include <time.h> |
|---|
| 8 | |
|---|
| 9 | CheckEventLog gCheckEventLog; |
|---|
| 10 | |
|---|
| 11 | BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) |
|---|
| 12 | { |
|---|
| 13 | NSCModuleWrapper::wrapDllMain(hModule, ul_reason_for_call); |
|---|
| 14 | return TRUE; |
|---|
| 15 | } |
|---|
| 16 | |
|---|
| 17 | CheckEventLog::CheckEventLog() { |
|---|
| 18 | } |
|---|
| 19 | CheckEventLog::~CheckEventLog() { |
|---|
| 20 | } |
|---|
| 21 | |
|---|
| 22 | |
|---|
| 23 | bool CheckEventLog::loadModule() { |
|---|
| 24 | return true; |
|---|
| 25 | } |
|---|
| 26 | bool CheckEventLog::unloadModule() { |
|---|
| 27 | return true; |
|---|
| 28 | } |
|---|
| 29 | |
|---|
| 30 | std::string CheckEventLog::getModuleName() { |
|---|
| 31 | return "NSClient compatibility Module."; |
|---|
| 32 | } |
|---|
| 33 | NSCModuleWrapper::module_version CheckEventLog::getModuleVersion() { |
|---|
| 34 | NSCModuleWrapper::module_version version = {0, 0, 1 }; |
|---|
| 35 | return version; |
|---|
| 36 | } |
|---|
| 37 | |
|---|
| 38 | bool CheckEventLog::hasCommandHandler() { |
|---|
| 39 | return true; |
|---|
| 40 | } |
|---|
| 41 | bool CheckEventLog::hasMessageHandler() { |
|---|
| 42 | return false; |
|---|
| 43 | } |
|---|
| 44 | |
|---|
| 45 | |
|---|
| 46 | class EventLogRecord { |
|---|
| 47 | EVENTLOGRECORD *pevlr_; |
|---|
| 48 | public: |
|---|
| 49 | EventLogRecord(EVENTLOGRECORD *pevlr) : pevlr_(pevlr) { |
|---|
| 50 | } |
|---|
| 51 | inline DWORD timeGenerated() { |
|---|
| 52 | return pevlr_->TimeGenerated; |
|---|
| 53 | } |
|---|
| 54 | inline DWORD timeWritten() { |
|---|
| 55 | return pevlr_->TimeWritten; |
|---|
| 56 | } |
|---|
| 57 | inline std::string eventSource() { |
|---|
| 58 | return reinterpret_cast<LPSTR>(reinterpret_cast<LPBYTE>(pevlr_) + sizeof(EVENTLOGRECORD)); |
|---|
| 59 | } |
|---|
| 60 | |
|---|
| 61 | inline DWORD eventType() { |
|---|
| 62 | return pevlr_->EventType; |
|---|
| 63 | } |
|---|
| 64 | |
|---|
| 65 | std::string enumStrings() { |
|---|
| 66 | std::string ret; |
|---|
| 67 | LPSTR p = reinterpret_cast<LPSTR>(reinterpret_cast<LPBYTE>(pevlr_) + pevlr_->StringOffset); |
|---|
| 68 | for (unsigned int i =0;i<pevlr_->NumStrings;i++) { |
|---|
| 69 | std::string s = p; |
|---|
| 70 | if (!s.empty()) |
|---|
| 71 | s += ", "; |
|---|
| 72 | ret += s; |
|---|
| 73 | p+= strlen(p)+1; |
|---|
| 74 | } |
|---|
| 75 | return ret; |
|---|
| 76 | } |
|---|
| 77 | |
|---|
| 78 | static DWORD appendType(DWORD dwType, std::string sType) { |
|---|
| 79 | return dwType | translateType(sType); |
|---|
| 80 | } |
|---|
| 81 | static DWORD subtractType(DWORD dwType, std::string sType) { |
|---|
| 82 | return dwType & (!translateType(sType)); |
|---|
| 83 | } |
|---|
| 84 | static DWORD translateType(std::string sType) { |
|---|
| 85 | if (sType == "error") |
|---|
| 86 | return EVENTLOG_ERROR_TYPE; |
|---|
| 87 | if (sType == "warning") |
|---|
| 88 | return EVENTLOG_WARNING_TYPE; |
|---|
| 89 | if (sType == "info") |
|---|
| 90 | return EVENTLOG_INFORMATION_TYPE; |
|---|
| 91 | if (sType == "auditSuccess") |
|---|
| 92 | return EVENTLOG_AUDIT_SUCCESS; |
|---|
| 93 | if (sType == "auditFailure") |
|---|
| 94 | return EVENTLOG_AUDIT_FAILURE; |
|---|
| 95 | return 0; |
|---|
| 96 | } |
|---|
| 97 | |
|---|
| 98 | }; |
|---|
| 99 | |
|---|
| 100 | |
|---|
| 101 | struct searchQuery { |
|---|
| 102 | struct searchQueryBundle { |
|---|
| 103 | struct searchQueryItem { |
|---|
| 104 | DWORD eventType_; |
|---|
| 105 | std::string eventSource_; |
|---|
| 106 | bool notSetValue_; |
|---|
| 107 | DWORD writtenBeforeDelta_ ; |
|---|
| 108 | DWORD writtenAfterDelta_ ; |
|---|
| 109 | DWORD generatedBeforeDelta_; |
|---|
| 110 | DWORD generatedAfterDelta_; |
|---|
| 111 | |
|---|
| 112 | searchQueryItem(bool notSetValue) |
|---|
| 113 | : eventType_(0), notSetValue_(notSetValue), |
|---|
| 114 | writtenBeforeDelta_(0), writtenAfterDelta_(0) , |
|---|
| 115 | generatedBeforeDelta_(0), generatedAfterDelta_(0) |
|---|
| 116 | {} |
|---|
| 117 | |
|---|
| 118 | inline bool matchDateWritten(DWORD now, DWORD written) const { |
|---|
| 119 | if ((writtenAfterDelta_ == 0)&&(writtenBeforeDelta_ == 0)) |
|---|
| 120 | return notSetValue_; |
|---|
| 121 | bool ret = true; |
|---|
| 122 | if (writtenAfterDelta_ != 0) { |
|---|
| 123 | if (writtenAfterDelta_+written <= now) |
|---|
| 124 | ret = false; |
|---|
| 125 | } |
|---|
| 126 | if (writtenBeforeDelta_ != 0) { |
|---|
| 127 | if (writtenBeforeDelta_+written > now) |
|---|
| 128 | ret = false; |
|---|
| 129 | } |
|---|
| 130 | return ret; |
|---|
| 131 | } |
|---|
| 132 | inline bool matchDateGenerated(DWORD now, DWORD written) const { |
|---|
| 133 | if ((generatedAfterDelta_ == 0)&&(generatedBeforeDelta_ == 0)) |
|---|
| 134 | return notSetValue_; |
|---|
| 135 | bool ret = true; |
|---|
| 136 | if (generatedAfterDelta_ != 0) { |
|---|
| 137 | if (generatedAfterDelta_+written <= now) |
|---|
| 138 | ret = false; |
|---|
| 139 | } |
|---|
| 140 | if (generatedBeforeDelta_ != 0) { |
|---|
| 141 | if (generatedBeforeDelta_+written > now) |
|---|
| 142 | ret = false; |
|---|
| 143 | } |
|---|
| 144 | return ret; |
|---|
| 145 | } |
|---|
| 146 | inline bool matchType(DWORD eventType) const { |
|---|
| 147 | if (eventType_ == 0) |
|---|
| 148 | return notSetValue_; |
|---|
| 149 | return eventType_ & eventType; |
|---|
| 150 | } |
|---|
| 151 | inline bool matchSource(std::string eventSource) const { |
|---|
| 152 | if (eventSource_.empty()) |
|---|
| 153 | return notSetValue_; |
|---|
| 154 | return eventSource_ == eventSource; |
|---|
| 155 | } |
|---|
| 156 | std::string toString() const { |
|---|
| 157 | std::stringstream ss; |
|---|
| 158 | ss << " Event type: " << eventType_ << std::endl; |
|---|
| 159 | ss << " Event source: " << eventSource_ << std::endl; |
|---|
| 160 | ss << " Written delta: " << writtenAfterDelta_ << " > " << writtenBeforeDelta_ << std::endl; |
|---|
| 161 | ss << " Generated delta: " << generatedAfterDelta_ << " > " << generatedBeforeDelta_ << std::endl; |
|---|
| 162 | return ss.str(); |
|---|
| 163 | } |
|---|
| 164 | }; |
|---|
| 165 | struct searchQueryItem require; |
|---|
| 166 | struct searchQueryItem exclude; |
|---|
| 167 | searchQueryBundle() : require(true), exclude(false) {} |
|---|
| 168 | std::string toString() { |
|---|
| 169 | return " Required:\n" + require.toString() + "\n Exlude:\n" + exclude.toString(); |
|---|
| 170 | } |
|---|
| 171 | }; |
|---|
| 172 | |
|---|
| 173 | searchQueryBundle warn; |
|---|
| 174 | searchQueryBundle critical; |
|---|
| 175 | unsigned int truncate; |
|---|
| 176 | bool descriptions; |
|---|
| 177 | searchQuery() : truncate(0), descriptions(false) {} |
|---|
| 178 | |
|---|
| 179 | std::string toString() { |
|---|
| 180 | return "Warn:\n" + warn.toString() + "\nCritical:\n" + critical.toString(); |
|---|
| 181 | } |
|---|
| 182 | |
|---|
| 183 | }; |
|---|
| 184 | |
|---|
| 185 | void addToQueryItem(searchQuery::searchQueryBundle::searchQueryItem &item, std::string arg) { |
|---|
| 186 | std::pair<std::string,std::string> p = strEx::split(arg, "="); |
|---|
| 187 | if (p.first == "eventType") |
|---|
| 188 | item.eventType_ = EventLogRecord::appendType(item.eventType_, p.second); |
|---|
| 189 | else if (p.first == "eventSource") |
|---|
| 190 | item.eventSource_ = p.second; |
|---|
| 191 | } |
|---|
| 192 | void addToQueryBundle(searchQuery::searchQueryBundle &bundle, std::string arg) { |
|---|
| 193 | std::pair<std::string,std::string> p = strEx::split(arg, "."); |
|---|
| 194 | if (p.first == "require") |
|---|
| 195 | addToQueryItem(bundle.require, p.second); |
|---|
| 196 | else if (p.first == "exclude") |
|---|
| 197 | addToQueryItem(bundle.exclude, p.second); |
|---|
| 198 | } |
|---|
| 199 | void addToQuery(searchQuery &q, std::string arg) { |
|---|
| 200 | std::pair<std::string,std::string> p = strEx::split(arg, "."); |
|---|
| 201 | if (p.first == "warn") |
|---|
| 202 | addToQueryBundle(q.warn, p.second); |
|---|
| 203 | else if (p.first == "critical") |
|---|
| 204 | addToQueryBundle(q.critical, p.second); |
|---|
| 205 | else if (p.first == "all") { |
|---|
| 206 | addToQueryBundle(q.warn, p.second); |
|---|
| 207 | addToQueryBundle(q.critical, p.second); |
|---|
| 208 | } else { |
|---|
| 209 | std::pair<std::string,std::string> p = strEx::split(arg, "="); |
|---|
| 210 | if (p.first == "truncate") |
|---|
| 211 | q.truncate = strEx::stoi(p.second); |
|---|
| 212 | else if (p.first == "descriptions") |
|---|
| 213 | q.descriptions = true; |
|---|
| 214 | } |
|---|
| 215 | } |
|---|
| 216 | |
|---|
| 217 | searchQuery buildQury(std::list<std::string> args) { |
|---|
| 218 | searchQuery ret; |
|---|
| 219 | for (std::list<std::string>::const_iterator it = args.begin(); it!=args.end(); it++) { |
|---|
| 220 | addToQuery(ret, *it); |
|---|
| 221 | } |
|---|
| 222 | return ret; |
|---|
| 223 | } |
|---|
| 224 | // huffa&CheckEventLog&Application&1&<type>&<query>&huffa... |
|---|
| 225 | // request: CheckEventLog&<logfile>&<Query strings> |
|---|
| 226 | // Return: <return state>&<log entry 1> - <log entry 2>... |
|---|
| 227 | // <return state> 0 - No errors |
|---|
| 228 | // 1 - Unknown |
|---|
| 229 | // 2 - Errors |
|---|
| 230 | #define BUFFER_SIZE 1024*64 |
|---|
| 231 | |
|---|
| 232 | std::string CheckEventLog::handleCommand(const std::string command, const unsigned int argLen, char **char_args) { |
|---|
| 233 | if (command != "CheckEventLog") |
|---|
| 234 | return ""; |
|---|
| 235 | std::list<std::string> args = NSCHelper::makelist(argLen, char_args); |
|---|
| 236 | if (args.size() < 2) |
|---|
| 237 | return "Missing argument"; |
|---|
| 238 | std::string ret; |
|---|
| 239 | bool critical = false; |
|---|
| 240 | std::string logFile = args.front(); args.pop_front(); |
|---|
| 241 | searchQuery query = buildQury(args); |
|---|
| 242 | NSC_DEBUG_MSG_STD("Base query: " + query.toString()); |
|---|
| 243 | |
|---|
| 244 | HANDLE hLog = OpenEventLog(NULL, logFile.c_str()); |
|---|
| 245 | if (hLog == NULL) |
|---|
| 246 | return "Could not open the Application event log."; |
|---|
| 247 | |
|---|
| 248 | DWORD dwThisRecord, dwRead, dwNeeded; |
|---|
| 249 | EVENTLOGRECORD *pevlr; |
|---|
| 250 | BYTE bBuffer[BUFFER_SIZE]; |
|---|
| 251 | |
|---|
| 252 | pevlr = reinterpret_cast<EVENTLOGRECORD*>(&bBuffer); |
|---|
| 253 | |
|---|
| 254 | // get time now !!! |
|---|
| 255 | __time64_t ltime; |
|---|
| 256 | _time64(<ime); |
|---|
| 257 | DWORD currentTime = ltime; |
|---|
| 258 | |
|---|
| 259 | GetOldestEventLogRecord(hLog, &dwThisRecord); |
|---|
| 260 | |
|---|
| 261 | while (ReadEventLog(hLog, EVENTLOG_FORWARDS_READ|EVENTLOG_SEQUENTIAL_READ, |
|---|
| 262 | 0, pevlr, BUFFER_SIZE, &dwRead, &dwNeeded)) |
|---|
| 263 | { |
|---|
| 264 | while (dwRead > 0) |
|---|
| 265 | { |
|---|
| 266 | bool match = false; |
|---|
| 267 | bool c = false; |
|---|
| 268 | EventLogRecord record(pevlr); |
|---|
| 269 | |
|---|
| 270 | if ( query.critical.require.matchType(record.eventType()) && |
|---|
| 271 | query.critical.require.matchSource(record.eventSource()) && |
|---|
| 272 | query.critical.require.matchDateGenerated(currentTime, record.timeGenerated()) && |
|---|
| 273 | query.critical.require.matchDateWritten(currentTime, record.timeWritten()) |
|---|
| 274 | ) { |
|---|
| 275 | match = true; |
|---|
| 276 | c = true; |
|---|
| 277 | } |
|---|
| 278 | if ( query.critical.exclude.matchType(record.eventType()) || |
|---|
| 279 | query.critical.exclude.matchSource(record.eventSource()) || |
|---|
| 280 | query.critical.require.matchDateGenerated(currentTime, record.timeGenerated()) || |
|---|
| 281 | query.critical.require.matchDateWritten(currentTime, record.timeWritten()) |
|---|
| 282 | ) { |
|---|
| 283 | match = false; |
|---|
| 284 | c = false; |
|---|
| 285 | } |
|---|
| 286 | |
|---|
| 287 | if ( query.warn.require.matchType(record.eventType()) && |
|---|
| 288 | query.warn.require.matchSource(record.eventSource()) && |
|---|
| 289 | query.critical.require.matchDateGenerated(currentTime, record.timeGenerated()) && |
|---|
| 290 | query.critical.require.matchDateWritten(currentTime, record.timeWritten()) |
|---|
| 291 | ) |
|---|
| 292 | match = true; |
|---|
| 293 | if ( query.warn.exclude.matchType(record.eventType()) || |
|---|
| 294 | query.warn.exclude.matchSource(record.eventSource()) || |
|---|
| 295 | query.critical.require.matchDateGenerated(currentTime, record.timeGenerated()) || |
|---|
| 296 | query.critical.require.matchDateWritten(currentTime, record.timeWritten()) |
|---|
| 297 | ) |
|---|
| 298 | match = false; |
|---|
| 299 | |
|---|
| 300 | if (match) { |
|---|
| 301 | if (c) |
|---|
| 302 | critical = true; |
|---|
| 303 | if (!ret.empty()) |
|---|
| 304 | ret += " - "; |
|---|
| 305 | ret += record.eventSource(); |
|---|
| 306 | if (query.descriptions) { |
|---|
| 307 | std::string s = record.enumStrings(); |
|---|
| 308 | if (!s.empty()) |
|---|
| 309 | ret += " [" + s + "]" ; |
|---|
| 310 | } |
|---|
| 311 | } |
|---|
| 312 | dwRead -= pevlr->Length; |
|---|
| 313 | pevlr = (EVENTLOGRECORD *) |
|---|
| 314 | ((LPBYTE) pevlr + pevlr->Length); |
|---|
| 315 | } |
|---|
| 316 | |
|---|
| 317 | pevlr = (EVENTLOGRECORD *) &bBuffer; |
|---|
| 318 | } |
|---|
| 319 | |
|---|
| 320 | CloseEventLog(hLog); |
|---|
| 321 | if (critical) |
|---|
| 322 | ret = "CRITICAL: " + ret; |
|---|
| 323 | else if (!ret.empty()) |
|---|
| 324 | ret = "WARNING: " + ret; |
|---|
| 325 | else |
|---|
| 326 | ret = "OK: No errors/warnings in eventlog."; |
|---|
| 327 | if (query.truncate != 0) |
|---|
| 328 | ret = ret.substr(0, query.truncate); |
|---|
| 329 | return ret; |
|---|
| 330 | } |
|---|
| 331 | |
|---|
| 332 | |
|---|
| 333 | NSC_WRAPPERS_MAIN_DEF(gCheckEventLog); |
|---|
| 334 | NSC_WRAPPERS_IGNORE_MSG_DEF(); |
|---|
| 335 | NSC_WRAPPERS_HANDLE_CMD_DEF(gCheckEventLog); |
|---|