1099 lines
35 KiB
C++
1099 lines
35 KiB
C++
|
|
// Copyright (c) 2014-2015 IONU Security, Inc. All rights reserved.
|
||
|
|
//
|
||
|
|
// Logging and auditing services
|
||
|
|
|
||
|
|
#ifndef WIN32
|
||
|
|
#include <syslog.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef __APPLE__
|
||
|
|
#include "TargetConditionals.h"
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef ANDROID
|
||
|
|
#include <android/log.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef WIN32
|
||
|
|
#include <winsock2.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include <sys/stat.h>
|
||
|
|
#include <pthread.h>
|
||
|
|
|
||
|
|
#include "eyelog.h"
|
||
|
|
#include "eyetime.h"
|
||
|
|
#include "eyeutils.h"
|
||
|
|
|
||
|
|
#ifdef WIN32
|
||
|
|
#define LOG_TO_UDP_514_LOCALHOST // supports UDP trace log viewer
|
||
|
|
#endif
|
||
|
|
|
||
|
|
using namespace sequencelogic;
|
||
|
|
using namespace std;
|
||
|
|
|
||
|
|
namespace sequencelogic {
|
||
|
|
static const char* _logLevels[] = {"Audit", "Fatal", "Error", "Warning", "Info", "Debug", "Trace"};
|
||
|
|
static const char* _logLevelsU[] = {"AUDIT", "FATAL", "ERROR", "WARNING", "INFO", "DEBUG", "TRACE"};
|
||
|
|
// static const char* _defaultFormat = "[%L]|%c|%t|%m"; // This is the old libionu style
|
||
|
|
static const char* _defaultFormat = "%l:%c[%t] %m";
|
||
|
|
};
|
||
|
|
|
||
|
|
pthread_mutex_t ionu_logger_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||
|
|
pthread_mutex_t ionu_audit_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||
|
|
|
||
|
|
namespace sequencelogic {
|
||
|
|
EyeLog* EyeLog::_instance = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
void sequencelogic::logsprintf(std::string& msg, const char *s)
|
||
|
|
{
|
||
|
|
if (s)
|
||
|
|
msg += s;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef LOG_USE_VA_STYLE
|
||
|
|
// VS 2012 does not support variadic templates so use the old dangerous vargs style code
|
||
|
|
void sequencelogic::logsprintf (std::string& msg, const char *s, ...)
|
||
|
|
{
|
||
|
|
if (s == NULL) return;
|
||
|
|
va_list args;
|
||
|
|
va_start (args, s);
|
||
|
|
logvasprintf (msg, s, args);
|
||
|
|
va_end (args);
|
||
|
|
}
|
||
|
|
|
||
|
|
void sequencelogic::LogWrapper (EyeLog::LOG_LEVEL level, const char *s, ...)
|
||
|
|
{
|
||
|
|
if (s == NULL) return;
|
||
|
|
va_list args;
|
||
|
|
va_start (args, s);
|
||
|
|
std::string msg = "";
|
||
|
|
logvasprintf (msg, s, args);
|
||
|
|
va_end (args);
|
||
|
|
|
||
|
|
EyeLog::GetInstance()->Log (level, msg);
|
||
|
|
}
|
||
|
|
|
||
|
|
void sequencelogic::AuditWrapper (EyeLog::AUDIT_CODE code, const char *s, ...)
|
||
|
|
{
|
||
|
|
if (s == NULL) return;
|
||
|
|
va_list args;
|
||
|
|
va_start (args, s);
|
||
|
|
std::string msg = "";
|
||
|
|
logvasprintf (msg, s, args);
|
||
|
|
va_end (args);
|
||
|
|
|
||
|
|
EyeLog::GetInstance()->Audit (code, msg);
|
||
|
|
}
|
||
|
|
|
||
|
|
void sequencelogic::logvasprintf (std::string& msg, const char *fmt, va_list args)
|
||
|
|
{
|
||
|
|
char buffer[1024];
|
||
|
|
// Windows wants the buffer initialized to NULL
|
||
|
|
memset(buffer, 0, sizeof(buffer));
|
||
|
|
int n = vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
|
||
|
|
#ifdef WIN32
|
||
|
|
// On Windows, n is less than zero if the number of characters
|
||
|
|
// to write is greater than count (1024). The return value does
|
||
|
|
// not include the terminating null.
|
||
|
|
if (n < 0) {
|
||
|
|
buffer[1023] = '\0';
|
||
|
|
}
|
||
|
|
n = 1023;
|
||
|
|
// On other platforms, n is less than zero when an encoding error
|
||
|
|
// occurs.
|
||
|
|
#endif
|
||
|
|
if (n > 0 && n < 1024)
|
||
|
|
msg += buffer;
|
||
|
|
else if (n > 0) { // not enough space
|
||
|
|
char* bigbuf = new char[n+1];
|
||
|
|
if (bigbuf) {
|
||
|
|
int bn = vsnprintf (buffer, (size_t)n, fmt, args);
|
||
|
|
if (bn == n)
|
||
|
|
msg += bigbuf;
|
||
|
|
delete[] bigbuf;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
EyeLog* EyeLog::GetInstance ()
|
||
|
|
{
|
||
|
|
if (_instance == NULL) {
|
||
|
|
pthread_mutex_lock (&ionu_logger_mutex);
|
||
|
|
if (_instance == NULL) // Check again as race may have had thread waiting on mutex
|
||
|
|
_instance = new EyeLog ();
|
||
|
|
pthread_mutex_unlock (&ionu_logger_mutex);
|
||
|
|
}
|
||
|
|
return _instance;
|
||
|
|
}
|
||
|
|
|
||
|
|
void EyeLog::ProcessFormat (const std::string& fmt)
|
||
|
|
{
|
||
|
|
if (fmt.size() == 0)
|
||
|
|
format = _defaultFormat;
|
||
|
|
else
|
||
|
|
format = fmt;
|
||
|
|
|
||
|
|
int a = 0;
|
||
|
|
std::string::iterator fmt_pos (format.begin());
|
||
|
|
std::string::iterator const fmt_end (format.end());
|
||
|
|
for (; fmt_pos != fmt_end; ++fmt_pos) {
|
||
|
|
if (*fmt_pos == '%' && *(++fmt_pos) != '%') {
|
||
|
|
switch (*fmt_pos) {
|
||
|
|
case 'l' : formatargs[a++] = IONU_LEVEL; break;
|
||
|
|
case 'L' : formatargs[a++] = IONU_ULEVEL; break;
|
||
|
|
case 'c' : formatargs[a++] = IONU_CLIENT; break;
|
||
|
|
case 'e' : formatargs[a++] = IONU_ETIME; break;
|
||
|
|
case 't' : formatargs[a++] = IONU_TIME; break;
|
||
|
|
case 'm' : formatargs[a++] = IONU_MESSAGE; break;
|
||
|
|
default : formatargs[a++] = IONU_TERMINAL; break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
formatargs[a] = IONU_TERMINAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
const char* EyeLog::AuditCodeString (int auditcode)
|
||
|
|
{
|
||
|
|
switch (auditcode) {
|
||
|
|
#define X(code,value,description) \
|
||
|
|
case value : return #description;
|
||
|
|
IONU_AUDIT_CODES
|
||
|
|
#undef X
|
||
|
|
default : return "UnknownAuditCode";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeLog::ConfigLogger (const std::string& client, const std::string& device, LOG_LEVEL level, LOG_SINK sink, const std::string& fmt)
|
||
|
|
{
|
||
|
|
// Must specify a valid client id for the log
|
||
|
|
if (client.size() == 0)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
// This is so an application's settings do not get overridden
|
||
|
|
if (clientId.size() > 0 && clientId.compare (client) != 0)
|
||
|
|
return true;
|
||
|
|
|
||
|
|
bool rc = true;
|
||
|
|
pthread_mutex_lock (&ionu_logger_mutex);
|
||
|
|
|
||
|
|
ProcessFormat (fmt);
|
||
|
|
clientId = client;
|
||
|
|
deviceId = device;
|
||
|
|
logLevel = level;
|
||
|
|
localSink = sink;
|
||
|
|
#if defined(ANDROID)
|
||
|
|
|
||
|
|
#elif defined (LOG_TO_SYSLOG)
|
||
|
|
if (localSink == IONU_SYSLOG)
|
||
|
|
openlog (client.c_str(), LOG_PID, LOG_USER); // Writes to /var/log/syslog
|
||
|
|
#elif defined(__APPLE__)
|
||
|
|
if (aslClientHandle == 0)
|
||
|
|
aslClientHandle = asl_open (clientId.c_str(), "com.ionu.IOnU", 0);
|
||
|
|
if (aslClientHandle) {
|
||
|
|
int priority = AIONU_LEVEL_INFO;
|
||
|
|
switch (level){
|
||
|
|
case IONU_FATAL: priority = AIONU_LEVEL_CRIT; break;
|
||
|
|
case IONU_ERROR: priority = AIONU_LEVEL_ERR; break;
|
||
|
|
case IONU_WARN: priority = AIONU_LEVEL_WARNING; break;
|
||
|
|
case IONU_INFO: priority = AIONU_LEVEL_INFO; break;
|
||
|
|
case IONU_DEBUG:
|
||
|
|
case IONU_TRACE:
|
||
|
|
default: priority = AIONU_LEVEL_DEBUG; break;
|
||
|
|
}
|
||
|
|
asl_set_filter (aslClientHandle, AIONU_FILTER_MASK_UPTO (priority));
|
||
|
|
}
|
||
|
|
#elif defined(WIN32)
|
||
|
|
if (sysLogFilename.size() <= 1) {
|
||
|
|
char* homePath = NULL;
|
||
|
|
homePath = getenv("USERPROFILE"); // Windows 7
|
||
|
|
if (!homePath)
|
||
|
|
homePath = getenv("HOMEPATH"); // WindowsXP
|
||
|
|
if (!homePath)
|
||
|
|
homePath = getenv("TEMP"); // Fallback
|
||
|
|
if (!homePath)
|
||
|
|
homePath = getenv ("TMP"); // Fall further back
|
||
|
|
if (!homePath)
|
||
|
|
homePath = "";
|
||
|
|
sysLogFilename = homePath;
|
||
|
|
if (sysLogFilename.size() > 1)
|
||
|
|
sysLogFilename += "/";
|
||
|
|
sysLogFilename += clientId;
|
||
|
|
sysLogFilename += ".log";
|
||
|
|
sysLogFile.open (sysLogFilename, ios::out | ios::binary);
|
||
|
|
if (!sysLogFile.is_open()) {
|
||
|
|
IONUERROR ("Unable to open system logfile (%s)", sysLogFilename.c_str());
|
||
|
|
rc = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
pthread_mutex_unlock (&ionu_logger_mutex);
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeLog::ConfigFileLogger (const std::string& client, const std::string& dev, LOG_LEVEL level,
|
||
|
|
const std::string& log, LOG_SINK sink, const std::string& fmt)
|
||
|
|
{
|
||
|
|
if (!sequencelogic::IsValidFilename (log))
|
||
|
|
return false;
|
||
|
|
|
||
|
|
bool rc = true;
|
||
|
|
pthread_mutex_lock (&ionu_logger_mutex);
|
||
|
|
|
||
|
|
ProcessFormat (fmt);
|
||
|
|
clientId = client;
|
||
|
|
deviceId = dev;
|
||
|
|
logLevel = level;
|
||
|
|
localSink = sink;
|
||
|
|
|
||
|
|
// If changing log file, close old, open new
|
||
|
|
if (logFilename.compare (log) != 0) {
|
||
|
|
logFilename = log;
|
||
|
|
if (logFile.is_open())
|
||
|
|
logFile.close();
|
||
|
|
|
||
|
|
// Empty name closes file logging, otherwise open
|
||
|
|
if (logFilename.size() > 0) {
|
||
|
|
struct stat buf;
|
||
|
|
|
||
|
|
// Open existing logfile for append
|
||
|
|
if (stat (logFilename.c_str(), &buf) != -1) {
|
||
|
|
logFile.open (logFilename, ios::out | ios::binary | ios::app | ios::ate);
|
||
|
|
if (!logFile.is_open()) {
|
||
|
|
pthread_mutex_unlock (&ionu_logger_mutex);
|
||
|
|
IONUERROR ("Unable to open logfile (%s)", logFilename.c_str());
|
||
|
|
rc = false;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
logFileSize = (size_t)logFile.tellp();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// Create the first logfile
|
||
|
|
else {
|
||
|
|
logFile.open (logFilename, ios::out | ios::binary);
|
||
|
|
if (!logFile.is_open()) {
|
||
|
|
pthread_mutex_unlock (&ionu_logger_mutex);
|
||
|
|
IONUERROR ("Unable to create logfile (%s)", logFilename.c_str());
|
||
|
|
rc = false;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
if (deviceId.size() > 0) {
|
||
|
|
EyeTime now;
|
||
|
|
string header;
|
||
|
|
header = clientId;
|
||
|
|
header += ":[";
|
||
|
|
header += now.GetISO8601Time();
|
||
|
|
header += "] ";
|
||
|
|
header += deviceId;
|
||
|
|
logFile << header << endl;
|
||
|
|
logFileSize = header.size() + SL_ENDL_LEN;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
logFileSize = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (rc)
|
||
|
|
pthread_mutex_unlock (&ionu_logger_mutex);
|
||
|
|
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeLog::ConfigFileLogger (const char* client, const char* dev, LOG_LEVEL level,
|
||
|
|
const char* log, LOG_SINK sink, const char* fmt)
|
||
|
|
{
|
||
|
|
std::string s_client(client);
|
||
|
|
std::string s_dev(dev);
|
||
|
|
std::string s_log(log);
|
||
|
|
std::string s_fmt(fmt);
|
||
|
|
return ConfigFileLogger(s_client, s_dev, level, s_log, sink, s_fmt);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeLog::CloseLogger (const std::string& logfile)
|
||
|
|
{
|
||
|
|
if (_instance) {
|
||
|
|
if (_instance->logFile.is_open())
|
||
|
|
_instance->logFile.close();
|
||
|
|
// Close the system lo
|
||
|
|
if (logfile.size() > 0) {
|
||
|
|
if (_instance->auditFile.is_open())
|
||
|
|
_instance->auditFile.close();
|
||
|
|
#ifdef WIN32
|
||
|
|
if (_instance->sysLogFile.is_open())
|
||
|
|
_instance->sysLogFile.close();
|
||
|
|
#elif defined (ANDROID)
|
||
|
|
#elif defined (LOG_TO_SYSLOG)
|
||
|
|
if (_instance->localSink == IONU_SYSLOG)
|
||
|
|
closelog();
|
||
|
|
#elif defined(__APPLE__)
|
||
|
|
if (_instance->aslClientHandle) {
|
||
|
|
asl_close (_instance->aslClientHandle);
|
||
|
|
_instance->aslClientHandle = 0;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
delete _instance;
|
||
|
|
_instance = NULL;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeLog::CloseLogger (void)
|
||
|
|
{
|
||
|
|
std::string logfile = "";
|
||
|
|
return CloseLogger(logfile);
|
||
|
|
}
|
||
|
|
|
||
|
|
EyeLog::EyeLog ()
|
||
|
|
{
|
||
|
|
_instance = nullptr;
|
||
|
|
#ifdef DEBUG
|
||
|
|
logLevel = IONU_DEBUG;
|
||
|
|
localSink = IONU_CONSOLE;
|
||
|
|
#else
|
||
|
|
logLevel = IONU_WARN;
|
||
|
|
localSink = IONU_CONSOLE;
|
||
|
|
#endif
|
||
|
|
format = _defaultFormat;
|
||
|
|
formatargs[0] = IONU_LEVEL;
|
||
|
|
formatargs[1] = IONU_CLIENT;
|
||
|
|
formatargs[2] = IONU_TIME;
|
||
|
|
formatargs[3] = IONU_MESSAGE;
|
||
|
|
formatargs[4] = IONU_TERMINAL;
|
||
|
|
formatargs[5] = IONU_TERMINAL;
|
||
|
|
clientId = "";
|
||
|
|
deviceId = "";
|
||
|
|
logFileSize = 0;
|
||
|
|
maxLogFiles = IONU_MAX_LOG_FILES;
|
||
|
|
maxLogFileSize = IONU_MAX_LOG_FILE_SIZE;
|
||
|
|
auditId = 1;
|
||
|
|
unsigned char buff[2*SL_AES_KEY_LEN];
|
||
|
|
sequencelogic::MemSet (buff, 2*SL_AES_KEY_LEN, 0);
|
||
|
|
auditKey = new Key ("hmkey", "Audit log key", 2*SL_AES_KEY_LEN, buff, Key::AESHMAC);
|
||
|
|
auditKeyring = new Keyring ("Auditor", "PK and AES keys");
|
||
|
|
auditKeyring->AddKey (auditKey);
|
||
|
|
logFilename = "";
|
||
|
|
auditCachePath = "";
|
||
|
|
EyeTime now;
|
||
|
|
startTime = now.GetTimeMs();
|
||
|
|
#ifdef __APPLE__
|
||
|
|
aslClientHandle = 0;
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
EyeLog::~EyeLog()
|
||
|
|
{
|
||
|
|
if (auditKeyring) {
|
||
|
|
delete auditKeyring;
|
||
|
|
auditKeyring = nullptr;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef LOG_TO_SYSLOG
|
||
|
|
// Safe version that truncates messages if too long, and no varargs
|
||
|
|
void EyeLog::SafeSyslog (int priority, std::string msg)
|
||
|
|
{
|
||
|
|
if (msg.size() >= 256) {
|
||
|
|
std::string smsg (msg, 0, 252);
|
||
|
|
smsg += "...";
|
||
|
|
syslog (priority, "%s", smsg.c_str());
|
||
|
|
}
|
||
|
|
else
|
||
|
|
syslog (priority, "%s", msg.c_str());
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
bool EyeLog::BeginAudit (const std::string& auditorPublicKey, const std::string& auditCacheDir /*, uploadcallback() */)
|
||
|
|
{
|
||
|
|
Key pk ("PK", "auditor", "", Key::GENERIC);
|
||
|
|
pk.Import (auditorPublicKey.c_str(), auditorPublicKey.size(), NULL, Key::PEM);
|
||
|
|
if (pk.GetType() == Key::GENERIC) {
|
||
|
|
IONUERROR ("EyeLog::BeginAudit(): Invalid auditor key");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
bool rc = BeginAudit (pk, auditCacheDir);
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeLog::BeginAudit (const Key& auditorPublicKey, const std::string& auditCacheDir /*, uploadcallback() */)
|
||
|
|
{
|
||
|
|
auditCachePath = auditCacheDir;
|
||
|
|
if (auditCachePath.size() > 0 && auditCachePath[auditCachePath.size()-1] != '/')
|
||
|
|
auditCachePath += '/';
|
||
|
|
|
||
|
|
if (clientId.size() == 0)
|
||
|
|
clientId = "unknown";
|
||
|
|
|
||
|
|
size_t bytes = 0;
|
||
|
|
unsigned char* buff = NULL;
|
||
|
|
|
||
|
|
if (auditorPublicKey.GetType() == Key::RSA_PUBLIC || auditorPublicKey.GetType() == Key::RSA) {
|
||
|
|
if (! auditKeyring->GenerateAESHMACKey ("hmkey", "session"))
|
||
|
|
return false;
|
||
|
|
else
|
||
|
|
auditKey = const_cast<Key*>(auditKeyring->GetKey ("hmkey"));
|
||
|
|
}
|
||
|
|
else if (auditorPublicKey.GetType() == Key::EC_PUBLIC || auditorPublicKey.GetType() == Key::EC) {
|
||
|
|
auditKeyring->GenerateECKey ("EC", "Ephemeral");
|
||
|
|
const Key* ekey = auditKeyring->GetKey ("EC");
|
||
|
|
buff = ekey->GenerateECDHSecret (&auditorPublicKey, &bytes);
|
||
|
|
auditKey = const_cast<Key*>(auditKeyring->GetKey ("hmkey"));
|
||
|
|
auditKey->SetKey (buff, bytes);
|
||
|
|
delete[] buff;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string audf = auditCachePath + clientId + ".ioa";
|
||
|
|
auditFile.open (audf, ios::out | ios::binary | ios::trunc);
|
||
|
|
if (!auditFile.is_open()) {
|
||
|
|
IONUERROR ("EyeLog::BeginAudit (%s) Unable to open audit file", audf.c_str());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Initial starting sequence number with a random value
|
||
|
|
sequencelogic::RandomBytes (2, (unsigned char*)(&auditId));
|
||
|
|
|
||
|
|
// For RSA encrypt the random AESHMAC key with public key
|
||
|
|
if (auditorPublicKey.GetType() == Key::RSA_PUBLIC || auditorPublicKey.GetType() == Key::RSA) {
|
||
|
|
buff = auditorPublicKey.PublicKeyEncrypt (auditKey->GetKey(), SL_AES_KEY_LEN*2, &bytes);
|
||
|
|
char* b64 = new char [bytes * 4 / 3 + 4];
|
||
|
|
Base64Encode (buff, bytes, b64);
|
||
|
|
pthread_mutex_lock (&ionu_audit_mutex);
|
||
|
|
auditFile << b64 << endl;
|
||
|
|
pthread_mutex_unlock (&ionu_audit_mutex);
|
||
|
|
delete[] buff;
|
||
|
|
delete[] b64;
|
||
|
|
}
|
||
|
|
// For EC output the ephemeral public key
|
||
|
|
else {
|
||
|
|
char* ephemeral = auditKeyring->GetPubKey ("EC");
|
||
|
|
pthread_mutex_lock (&ionu_audit_mutex);
|
||
|
|
auditFile << ephemeral << endl;
|
||
|
|
pthread_mutex_unlock (&ionu_audit_mutex);
|
||
|
|
delete[] ephemeral;
|
||
|
|
}
|
||
|
|
Audit (AUDIT_START, deviceId);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef LOG_TO_UDP_514_LOCALHOST
|
||
|
|
struct CLoggerSocket
|
||
|
|
{
|
||
|
|
SOCKET sockfd;
|
||
|
|
struct sockaddr_in addr;
|
||
|
|
|
||
|
|
CLoggerSocket() : sockfd(INVALID_SOCKET)
|
||
|
|
{
|
||
|
|
WORD wVersionRequested = MAKEWORD(2, 2);
|
||
|
|
WSADATA wsaData;
|
||
|
|
int err = ::WSAStartup (wVersionRequested, &wsaData);
|
||
|
|
if (err != 0) {
|
||
|
|
printf("WSAStartup failed with error: %d\n", err);
|
||
|
|
}
|
||
|
|
else if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
|
||
|
|
printf("Could not find version of Winsock.dll that supports 2.2\n");
|
||
|
|
::WSACleanup();
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||
|
|
memset(&addr, 0, sizeof(addr));
|
||
|
|
addr.sin_family = AF_INET;
|
||
|
|
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||
|
|
addr.sin_port = htons(514);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
void Write(const char *buf, int len)
|
||
|
|
{
|
||
|
|
if (INVALID_SOCKET != sockfd) {
|
||
|
|
sendto(sockfd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
void Close()
|
||
|
|
{
|
||
|
|
if (INVALID_SOCKET != sockfd) {
|
||
|
|
closesocket (sockfd);
|
||
|
|
::WSACleanup();
|
||
|
|
sockfd = INVALID_SOCKET;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
static CLoggerSocket loggerSocket;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef LOG_TO_UDP_514_LOCALHOST
|
||
|
|
static void udpPrintf(const char *fmt, ...)
|
||
|
|
{
|
||
|
|
char buf[1280];
|
||
|
|
va_list marker;
|
||
|
|
va_start(marker, fmt);
|
||
|
|
int n = vsnprintf_s(buf, sizeof(buf), _TRUNCATE, fmt, marker);
|
||
|
|
va_end(marker);
|
||
|
|
n = (n < 0) ? (sizeof(buf) -1) : n;
|
||
|
|
loggerSocket.Write(buf, n);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef LOG_TO_UDP_514_LOCALHOST
|
||
|
|
static const char* levStr(sequencelogic::EyeLog::LOG_LEVEL level)
|
||
|
|
{
|
||
|
|
switch (level)
|
||
|
|
{
|
||
|
|
case sequencelogic::EyeLog::IONU_AUDIT: return "AUD";
|
||
|
|
case sequencelogic::EyeLog::IONU_FATAL: return "FAT";
|
||
|
|
case sequencelogic::EyeLog::IONU_ERROR: return "ERR";
|
||
|
|
case sequencelogic::EyeLog::IONU_WARN: return "WRN";
|
||
|
|
case sequencelogic::EyeLog::IONU_INFO: return "INF";
|
||
|
|
case sequencelogic::EyeLog::IONU_DEBUG: return "DBG";
|
||
|
|
case sequencelogic::EyeLog::IONU_TRACE: return "TRA";
|
||
|
|
default: return "UNK";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
bool EyeLog::Log (LOG_LEVEL level, string& msg)
|
||
|
|
{
|
||
|
|
bool rc = true;
|
||
|
|
|
||
|
|
// copy all log messages to UDP localhost:514 regardless of filter level
|
||
|
|
// this supports a trace log viewer tool for developers
|
||
|
|
#ifdef LOG_TO_UDP_514_LOCALHOST
|
||
|
|
udpPrintf("%.5X %.5X %s::%s", GetCurrentProcessId(), GetCurrentThreadId(), levStr(level), msg.c_str());
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if (logLevel >= level) {
|
||
|
|
std::string logmsg;
|
||
|
|
std::stringstream etimestr;
|
||
|
|
std::string etime;
|
||
|
|
EyeTime now;
|
||
|
|
#ifdef LOG_USE_VA_STYLE
|
||
|
|
// VS 2012 does not support variadic templates so use the old style message
|
||
|
|
logmsg = _logLevels[level];
|
||
|
|
logmsg += ":";
|
||
|
|
logmsg += clientId.c_str();
|
||
|
|
logmsg += "[" ;
|
||
|
|
logmsg += now.GetISO8601Time() ;
|
||
|
|
logmsg += "] ";
|
||
|
|
logmsg += msg.c_str();
|
||
|
|
#else
|
||
|
|
const char* args[4];
|
||
|
|
for (int i = 0; i < 4; ++i) {
|
||
|
|
switch (formatargs[i]) {
|
||
|
|
case IONU_LEVEL : args[i] = _logLevels[level]; break;
|
||
|
|
case IONU_ULEVEL : args[i] = _logLevelsU[level]; break;
|
||
|
|
case IONU_CLIENT : args[i] = clientId.c_str(); break;
|
||
|
|
case IONU_ETIME : etimestr << (now.GetTimeMs() - startTime); etime = etimestr.str(); args[i] = etime.c_str(); break;
|
||
|
|
case IONU_TIME : args[i] = now.GetISO8601Time(); break;
|
||
|
|
case IONU_MESSAGE : args[i] = msg.c_str(); break;
|
||
|
|
case IONU_TERMINAL: args[i] = ""; break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
logsprintf (logmsg, format.c_str(), args[0], args[1], args[2], args[3]);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
pthread_mutex_lock (&ionu_logger_mutex);
|
||
|
|
if (logFile.is_open()) {
|
||
|
|
if (logFile.bad() || logFileSize + logmsg.size() + SL_ENDL_LEN > maxLogFileSize) { // Roll the log files
|
||
|
|
logFile.close();
|
||
|
|
std::stringstream logfile;
|
||
|
|
std::string current;
|
||
|
|
std::string next;
|
||
|
|
logfile << logFilename << "." << maxLogFiles - 1;
|
||
|
|
next = logfile.str();
|
||
|
|
for (int i = maxLogFiles -2; i >= 0; --i) {
|
||
|
|
logfile.str("");
|
||
|
|
if (i > 0) {
|
||
|
|
logfile << logFilename << "." << i;
|
||
|
|
current = logfile.str();
|
||
|
|
}
|
||
|
|
else
|
||
|
|
current = logFilename;
|
||
|
|
|
||
|
|
struct stat buf;
|
||
|
|
if (stat (current.c_str(), &buf) != -1) {
|
||
|
|
if (!sequencelogic::RenameFile (current, next)) {
|
||
|
|
std::string logerr ("EyeLog::Log (");
|
||
|
|
logerr += logmsg;
|
||
|
|
logerr += ") - Unable to roll logfile (";
|
||
|
|
logerr += current;
|
||
|
|
logerr += " to ";
|
||
|
|
logerr += next;
|
||
|
|
logerr += ")";
|
||
|
|
//cout << logerr << endl;
|
||
|
|
#ifdef LOG_TO_SYSLOG
|
||
|
|
SafeSyslog (LOG_ERR, logerr);
|
||
|
|
#endif
|
||
|
|
rc = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
next = current;
|
||
|
|
}
|
||
|
|
if (rc) {
|
||
|
|
// Open logfile and truncate
|
||
|
|
logFile.open (logFilename, ios::out | ios::binary | ios::trunc);
|
||
|
|
if (!logFile.is_open()) {
|
||
|
|
std::string logerr ("EyeLog::Log (");
|
||
|
|
logerr += logmsg;
|
||
|
|
logerr += ") - Unable to open logfile (";
|
||
|
|
logerr += logFilename;
|
||
|
|
logerr += ")";
|
||
|
|
//cout << logerr << endl;
|
||
|
|
#ifdef LOG_TO_SYSLOG
|
||
|
|
SafeSyslog (LOG_ERR, logerr);
|
||
|
|
#endif
|
||
|
|
rc = false;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
if (deviceId.size() > 0) {
|
||
|
|
EyeTime now;
|
||
|
|
string header;
|
||
|
|
header = "[";
|
||
|
|
header += now.GetISO8601Time();
|
||
|
|
header += "] ";
|
||
|
|
header += deviceId;
|
||
|
|
logFile << header << endl;
|
||
|
|
logFileSize = header.size() + SL_ENDL_LEN;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
logFileSize = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (rc) {
|
||
|
|
logFile << logmsg << endl;
|
||
|
|
logFileSize += logmsg.size() + SL_ENDL_LEN;
|
||
|
|
if (logFile.bad()) {
|
||
|
|
std::string logerr ("EyeLog::Log (");
|
||
|
|
logerr += logmsg;
|
||
|
|
logerr += ") - Error writing to logfile (";
|
||
|
|
logerr += logFilename;
|
||
|
|
logerr += ")";
|
||
|
|
//cout << logerr << endl;
|
||
|
|
#ifdef LOG_TO_SYSLOG
|
||
|
|
SafeSyslog (LOG_ERR, logerr);
|
||
|
|
#endif
|
||
|
|
rc = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (localSink == IONU_CONSOLE)
|
||
|
|
cout << logmsg << endl;
|
||
|
|
else if (localSink == IONU_SYSLOG) {
|
||
|
|
#ifdef WIN32
|
||
|
|
if (sysLogFile.is_open())
|
||
|
|
sysLogFile << logmsg << endl;
|
||
|
|
#elif defined(LOG_TO_SYSLOG)
|
||
|
|
int priority = LOG_INFO;
|
||
|
|
switch (level){
|
||
|
|
case IONU_FATAL: priority = LOG_CRIT; break;
|
||
|
|
case IONU_ERROR: priority = LOG_ERR; break;
|
||
|
|
case IONU_WARN: priority = LOG_WARNING; break;
|
||
|
|
case IONU_INFO: priority = LOG_INFO; break;
|
||
|
|
case IONU_DEBUG:
|
||
|
|
case IONU_TRACE:
|
||
|
|
default: priority = LOG_DEBUG; break;
|
||
|
|
}
|
||
|
|
SafeSyslog (priority, msg);
|
||
|
|
#elif defined(ANDROID)
|
||
|
|
int priority = ANDROID_LOG_INFO;
|
||
|
|
switch (level){
|
||
|
|
case IONU_FATAL: priority = ANDROID_LOG_ERROR; break;
|
||
|
|
case IONU_ERROR: priority = ANDROID_LOG_ERROR; break;
|
||
|
|
case IONU_WARN: priority = ANDROID_LOG_WARN; break;
|
||
|
|
case IONU_INFO:
|
||
|
|
case IONU_DEBUG: priority = ANDROID_LOG_INFO; break;
|
||
|
|
case IONU_TRACE:
|
||
|
|
default: priority = ANDROID_LOG_VERBOSE; break;
|
||
|
|
}
|
||
|
|
__android_log_print (priority, clientId.c_str(), "%s", msg.c_str());
|
||
|
|
#elif defined(__APPLE__)
|
||
|
|
int priority = AIONU_LEVEL_INFO;
|
||
|
|
switch (level){
|
||
|
|
case IONU_FATAL: priority = AIONU_LEVEL_CRIT; break;
|
||
|
|
case IONU_ERROR: priority = AIONU_LEVEL_ERR; break;
|
||
|
|
case IONU_WARN: priority = AIONU_LEVEL_WARNING; break;
|
||
|
|
case IONU_INFO: priority = AIONU_LEVEL_INFO; break;
|
||
|
|
case IONU_DEBUG:
|
||
|
|
case IONU_TRACE:
|
||
|
|
default: priority = AIONU_LEVEL_DEBUG; break;
|
||
|
|
}
|
||
|
|
asl_log (aslClientHandle, NULL, priority, "[%s]|%s", _logLevels[level], msg.c_str());
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
pthread_mutex_unlock (&ionu_logger_mutex);
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeLog::Audit (AUDIT_CODE code, string& msg)
|
||
|
|
{
|
||
|
|
bool rc = true;
|
||
|
|
if (!auditKey) {
|
||
|
|
IONUDEBUG ("EyeLog::Audit (%s) Audit not initialized", msg.c_str());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
size_t bytes = 0;
|
||
|
|
string auditmsg;
|
||
|
|
stringstream auditstream;
|
||
|
|
EyeTime now;
|
||
|
|
if (clientId.size() > 0) {
|
||
|
|
auditmsg += ":";
|
||
|
|
auditmsg += clientId;
|
||
|
|
}
|
||
|
|
auditmsg += ":[";
|
||
|
|
auditmsg += now.GetISO8601Time();
|
||
|
|
auditmsg += "] ";
|
||
|
|
auditmsg += msg;
|
||
|
|
auditstream << auditId++ << ":" << code << auditmsg;
|
||
|
|
unsigned char* buff = auditKey->HMACEncrypt ((unsigned char*)(auditstream.str().data()), auditstream.str().size(), &bytes);
|
||
|
|
char* b64 = new char [bytes * 4 / 3 + 4];
|
||
|
|
Base64Encode (buff, bytes, b64);
|
||
|
|
pthread_mutex_lock (&ionu_audit_mutex);
|
||
|
|
auditFile << b64 << endl;
|
||
|
|
if (auditFile.bad()) {
|
||
|
|
std::string logerr ("EyeLog::Audit (");
|
||
|
|
logerr += auditmsg;
|
||
|
|
logerr += ") - Error writing to auditfile (";
|
||
|
|
logerr += auditCachePath + clientId + ".ioa";
|
||
|
|
logerr += ")";
|
||
|
|
//cout << logerr << endl;
|
||
|
|
#ifdef LOG_TO_SYSLOG
|
||
|
|
SafeSyslog (LOG_ERR, logerr);
|
||
|
|
#endif
|
||
|
|
rc = false;
|
||
|
|
}
|
||
|
|
pthread_mutex_unlock (&ionu_audit_mutex);
|
||
|
|
delete[] b64;
|
||
|
|
delete[] buff;
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get the audit key from an audit file (RSA is encrypted EC is generated)
|
||
|
|
bool EyeLog::GetAuditKey (const Key& pk, const std::string& auditFilename)
|
||
|
|
{
|
||
|
|
string line;
|
||
|
|
size_t bytes = 0;
|
||
|
|
unsigned char* buff;
|
||
|
|
unsigned char cbuff[1024];
|
||
|
|
|
||
|
|
if (getline (auditFile, line)) {
|
||
|
|
// Ephemeral public key, use ECDH to generate AES+HMAC key
|
||
|
|
if (line.compare (0, 10, "-----BEGIN") == 0 || line.compare (0, 3, "MII") == 0) {
|
||
|
|
string ephemeral = line;
|
||
|
|
ephemeral += "\n";
|
||
|
|
while (getline (auditFile, line)) {
|
||
|
|
ephemeral += line;
|
||
|
|
ephemeral += "\n";
|
||
|
|
if (line.compare (0, 8, "-----END") == 0) {
|
||
|
|
Key* ekey = new Key ("EC", "Ephemeral", "", Key::GENERIC);
|
||
|
|
ekey->Import (ephemeral.c_str(), ephemeral.size()+1, "", Key::PEM);
|
||
|
|
buff = pk.GenerateECDHSecret (ekey, &bytes);
|
||
|
|
auditKey->SetKey (buff, bytes);
|
||
|
|
delete ekey;
|
||
|
|
delete[] buff;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
bytes = Base64Decode (line.c_str(), cbuff);
|
||
|
|
buff = pk.PrivateKeyDecrypt (cbuff, bytes, &bytes);
|
||
|
|
if (buff && bytes == SL_AES_KEY_LEN*2) {
|
||
|
|
auditKey->SetKey (buff, bytes);
|
||
|
|
delete[] buff;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
IONUERROR ("EyeLog::GetAuditKey (%s): Invalid audit file, or wrong key", auditFilename.c_str());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
IONUERROR ("EyeLog::GetAuditKey (%s): Invalid or empty audit file", auditFilename.c_str());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeLog::ReviewAudit (const std::string& auditorPrivateKey, const std::string& auditFilename)
|
||
|
|
{
|
||
|
|
// Check for garbage input
|
||
|
|
if (sequencelogic::StrLen (auditorPrivateKey.c_str(), 1024) == 0 || !sequencelogic::IsValidFilename (auditFilename)) {
|
||
|
|
IONUDEBUG ("EyeLog::ReviewAudit(): garbage input detected");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
Key pk ("PK", "auditor", "", Key::GENERIC);
|
||
|
|
pk.Import (auditorPrivateKey.c_str(), auditorPrivateKey.size(), "", Key::PEM);
|
||
|
|
if (pk.GetType() != Key::RSA && pk.GetType() != Key::EC) {
|
||
|
|
IONUERROR ("EyeLog::ReviewAudit(): Invalid auditor key");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
bool rc = ReviewAudit (pk, auditFilename);
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeLog::ReviewAudit (const Key& auditorPrivateKey, const std::string& auditFilename)
|
||
|
|
{
|
||
|
|
// Check for garbage input
|
||
|
|
if (!sequencelogic::IsValidFilename (auditFilename)) {
|
||
|
|
IONUDEBUG ("EyeLog::ReviewAudit(): garbage input detected");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (auditFile.is_open())
|
||
|
|
auditFile.close();
|
||
|
|
|
||
|
|
auditFile.open (auditFilename, ios::in | ios::binary);
|
||
|
|
if (!auditFile.is_open()) {
|
||
|
|
IONUERROR ("EyeLog::ReviewAudit (%s): Unable to open audit file", auditFilename.c_str());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get the audit AES+HMAC key
|
||
|
|
if (!GetAuditKey (auditorPrivateKey, auditFilename)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool rc = true;
|
||
|
|
size_t bytes = 0;
|
||
|
|
int errcnt = 0;
|
||
|
|
string line;
|
||
|
|
unsigned char* buff;
|
||
|
|
unsigned char cbuff[1024];
|
||
|
|
|
||
|
|
// Process the individual audit log entries
|
||
|
|
while (rc && auditFile >> line) {
|
||
|
|
bytes = Base64Decode (line.c_str(), cbuff);
|
||
|
|
buff = auditKey->HMACDecrypt (cbuff, bytes, &bytes);
|
||
|
|
if (buff) {
|
||
|
|
string msg ((char*)buff, bytes);
|
||
|
|
int id = 0;
|
||
|
|
size_t pos = 0;
|
||
|
|
for (; ; ++pos)
|
||
|
|
if (msg[pos] == ':')
|
||
|
|
break;
|
||
|
|
else
|
||
|
|
id = id * 10 + (msg[pos] - '0');
|
||
|
|
int code = 0;
|
||
|
|
pos++;
|
||
|
|
for (; ; ++pos)
|
||
|
|
if (msg[pos] == ':')
|
||
|
|
break;
|
||
|
|
else
|
||
|
|
code = code * 10 + (msg[pos] - '0');
|
||
|
|
// AUDIT_START = 0, get the starting random sequence number from this entry
|
||
|
|
if (code == 0)
|
||
|
|
auditId = id;
|
||
|
|
else if (id != auditId && errcnt <= IONU_MAX_AUDIT_ERRORS) {
|
||
|
|
IONUERROR ("Invalid sequence found, expecting %d found %d", auditId, id);
|
||
|
|
errcnt++;
|
||
|
|
}
|
||
|
|
cout << id << ":" << AuditCodeString (code) << msg.substr(pos) << endl;
|
||
|
|
delete[] buff;
|
||
|
|
}
|
||
|
|
else if (errcnt <= IONU_MAX_AUDIT_ERRORS) {
|
||
|
|
IONUERROR ("Invalid entry at position %d", auditId);
|
||
|
|
errcnt++;
|
||
|
|
}
|
||
|
|
if (errcnt == IONU_MAX_AUDIT_ERRORS) {
|
||
|
|
IONUERROR ("Error limit of %d reached, further errors suppressed", IONU_MAX_AUDIT_ERRORS);
|
||
|
|
rc = false;
|
||
|
|
}
|
||
|
|
auditId++;
|
||
|
|
}
|
||
|
|
|
||
|
|
// No errors and read all of the file successfully
|
||
|
|
if (errcnt > 0 || auditFile.bad())
|
||
|
|
rc = false;
|
||
|
|
auditFile.close();
|
||
|
|
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Consolidate two audit files into one
|
||
|
|
bool EyeLog::ConsolidateAudit (const Key& auditorPrivateKey, const std::string& auditFile1, const std::string& auditFile2)
|
||
|
|
{
|
||
|
|
// Check for garbage input
|
||
|
|
if (auditorPrivateKey.GetType() != Key::RSA && auditorPrivateKey.GetType() != Key::EC) {
|
||
|
|
IONUERROR ("EyeLog::ConsolidateAudit(): Invalid auditor key");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (!sequencelogic::IsValidFilename (auditFile1) || !sequencelogic::IsValidFilename (auditFile2)) {
|
||
|
|
IONUDEBUG ("EyeLog::ConsolidateAudit(): invalid audit file(s)");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (auditFile.is_open())
|
||
|
|
auditFile.close();
|
||
|
|
|
||
|
|
auditFile.open (auditFile1, ios::in | ios::out | ios::binary);
|
||
|
|
if (!auditFile.is_open()) {
|
||
|
|
IONUERROR ("EyeLog::ConsolidateAudit (%s): Unable to open audit file", auditFile1.c_str());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::fstream af2 (auditFile2, ios::in | ios::binary);
|
||
|
|
if (!af2.is_open()) {
|
||
|
|
IONUERROR ("EyeLog::ConsolidateAudit (%s): Unable to open audit file", auditFile2.c_str());
|
||
|
|
auditFile.close();
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get the audit AES+HMAC key
|
||
|
|
if (!GetAuditKey (auditorPrivateKey, auditFile1)) {
|
||
|
|
IONUERROR ("EyeLog::ConsolidateAudit (%s): Unable to get AES+HMAC key", auditFile2.c_str());
|
||
|
|
auditFile.close();
|
||
|
|
af2.close();
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool rc = true;
|
||
|
|
int id = 0;
|
||
|
|
size_t bytes = 0;
|
||
|
|
size_t lineNum = 0;
|
||
|
|
int errcnt = 0;
|
||
|
|
string line;
|
||
|
|
unsigned char cbuff[1024];
|
||
|
|
unsigned char* back;
|
||
|
|
|
||
|
|
// Validate the first audit file
|
||
|
|
while (rc && auditFile >> line) {
|
||
|
|
bytes = Base64Decode (line.c_str(), cbuff);
|
||
|
|
back = auditKey->HMACDecrypt (cbuff, bytes, &bytes);
|
||
|
|
if (bytes == 0 || back == NULL) {
|
||
|
|
IONUERROR ("EyeLog::ConsolidateAudit (%s) Invalid entry at position %d", auditFile1.c_str(), (int)lineNum);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
string msg ((char*)back, bytes);
|
||
|
|
int id = 0;
|
||
|
|
size_t pos = 0;
|
||
|
|
for (; ; ++pos)
|
||
|
|
if (msg[pos] == ':')
|
||
|
|
break;
|
||
|
|
else
|
||
|
|
id = id * 10 + (msg[pos] - '0');
|
||
|
|
int code = 0;
|
||
|
|
pos++;
|
||
|
|
for (; ; ++pos)
|
||
|
|
if (msg[pos] == ':')
|
||
|
|
break;
|
||
|
|
else
|
||
|
|
code = code * 10 + (msg[pos] - '0');
|
||
|
|
// AUDIT_START = 0, get the starting random sequence number from this entry
|
||
|
|
if (code == 0)
|
||
|
|
auditId = id;
|
||
|
|
else
|
||
|
|
auditId++;
|
||
|
|
|
||
|
|
if (id != auditId && errcnt <= IONU_MAX_AUDIT_ERRORS) {
|
||
|
|
IONUERROR ("EyeLog::ConsolidateAudit (%s) Invalid sequence at %d, expecting %d found %d", auditFile1.c_str(), (int)lineNum, auditId, id);
|
||
|
|
cout << msg << endl;
|
||
|
|
errcnt++;
|
||
|
|
}
|
||
|
|
delete[] back;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// No errors and read all of the file successfully
|
||
|
|
if (errcnt > 0 || auditFile.bad())
|
||
|
|
rc = false;
|
||
|
|
auditFile.clear(); // Clear EOF flag to allow append
|
||
|
|
|
||
|
|
// Validate and append the second file, recrypt with first audit file HMAC key and resequence as needed
|
||
|
|
lineNum = 0;
|
||
|
|
Key* af2Key = NULL;
|
||
|
|
while (rc && af2 >> line) {
|
||
|
|
|
||
|
|
// Check the first line, could be either the key or just an audit message
|
||
|
|
if (lineNum == 0) {
|
||
|
|
if (line.compare (0, 10, "-----BEGIN") == 0 || line.compare (0, 3, "MII") == 0) {
|
||
|
|
string ephemeral = line;
|
||
|
|
while (auditFile >> line) {
|
||
|
|
ephemeral += line;
|
||
|
|
if (line.compare (0, 8, "-----END") == 0) {
|
||
|
|
Key* ekey = new Key ("EC", "Ephemeral", "", Key::GENERIC);
|
||
|
|
ekey->Import (ephemeral.c_str(), ephemeral.size()+1, "", Key::PEM);
|
||
|
|
back = auditorPrivateKey.GenerateECDHSecret (ekey, &bytes);
|
||
|
|
auditKey = new Key ("hmkey", "session", bytes, back, Key::AESHMAC);
|
||
|
|
delete[] back;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
bytes = Base64Decode (line.c_str(), cbuff);
|
||
|
|
back = auditorPrivateKey.PrivateKeyDecrypt (cbuff, bytes, &bytes);
|
||
|
|
if (back) {
|
||
|
|
af2Key = new Key ("hmkey", "", 2*SL_AES_KEY_LEN, back, Key::AESHMAC);
|
||
|
|
delete[] back;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
unsigned char* back = auditKey->HMACDecrypt (cbuff, bytes, &bytes);
|
||
|
|
if (bytes == 0 || back == NULL) {
|
||
|
|
IONUERROR ("Invalid entry in %s at position %d", auditFile2.c_str(), (int)lineNum);
|
||
|
|
errcnt++;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
string msg ((char*)back, bytes);
|
||
|
|
delete[] back;
|
||
|
|
for (int i = 0; ; ++i) {
|
||
|
|
if (msg[i] == ':')
|
||
|
|
break;
|
||
|
|
else
|
||
|
|
id = id * 10 + (msg[i] - '0');
|
||
|
|
}
|
||
|
|
if (id != auditId) {
|
||
|
|
IONUERROR ("Invalid sequence in %s, expecting %d found %d", auditFile2.c_str(), auditId, id);
|
||
|
|
cout << msg << endl;
|
||
|
|
errcnt++;
|
||
|
|
}
|
||
|
|
else // All good, just append
|
||
|
|
auditFile << line << endl;
|
||
|
|
auditId++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// Append file is just audit messages, validation of first has already occured above
|
||
|
|
else if (af2Key == NULL) {
|
||
|
|
auditFile << line << endl;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Different keys, so decrypt, resequence and encrypt with first key
|
||
|
|
else {
|
||
|
|
back = af2Key->HMACDecrypt (cbuff, bytes, &bytes);
|
||
|
|
if (bytes == 0 || back == NULL) {
|
||
|
|
IONUERROR ("Invalid entry in append file at position %d", lineNum);
|
||
|
|
errcnt++;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
string msg ((char*)back, bytes);
|
||
|
|
size_t found = msg.find (':');
|
||
|
|
if (found != std::string::npos) {
|
||
|
|
stringstream a;
|
||
|
|
a << auditId++ << msg.substr (found);
|
||
|
|
unsigned char* buff = auditKey->HMACEncrypt ((unsigned char*)(a.str().data()), a.str().size(), &bytes);
|
||
|
|
char* b64 = new char [bytes * 4 / 3 + 4];
|
||
|
|
Base64Encode (buff, bytes, b64);
|
||
|
|
cout << "requenced line:" << a.str() << endl;;
|
||
|
|
auditFile << b64 << endl;
|
||
|
|
delete[] buff;
|
||
|
|
delete[] b64;
|
||
|
|
}
|
||
|
|
delete[] back;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
lineNum++;
|
||
|
|
}
|
||
|
|
|
||
|
|
// No errors and read all of the file successfully
|
||
|
|
if (errcnt > 0 || auditFile.bad() || af2.bad())
|
||
|
|
rc = false;
|
||
|
|
|
||
|
|
auditFile.close();
|
||
|
|
af2.close();
|
||
|
|
if (af2Key) delete af2Key;
|
||
|
|
|
||
|
|
return rc;
|
||
|
|
}
|