2080 lines
62 KiB
C++
2080 lines
62 KiB
C++
|
|
// Copyright (c) 2013-2015 IONU Security, Inc. All rights reserved.
|
||
|
|
// Copyright (c) 2016 Sequence Logic, Inc. All rights reserved.
|
||
|
|
//
|
||
|
|
// Utility functions and classes
|
||
|
|
|
||
|
|
#include <ctype.h>
|
||
|
|
#include <cstring>
|
||
|
|
#include <iostream>
|
||
|
|
#include <fstream>
|
||
|
|
#include <sstream>
|
||
|
|
#include <algorithm>
|
||
|
|
#ifdef WIN32
|
||
|
|
#include <io.h>
|
||
|
|
#include <Windows.h>
|
||
|
|
#include <psapi.h>
|
||
|
|
#include "zlib/win32/zconf.h"
|
||
|
|
#ifdef WIN64
|
||
|
|
typedef __int64 ssize_t;
|
||
|
|
#else
|
||
|
|
typedef __int32 ssize_t;
|
||
|
|
#endif
|
||
|
|
#else
|
||
|
|
#include <sys/time.h>
|
||
|
|
#include <errno.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef LIN64
|
||
|
|
#include <pwd.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include <openssl/evp.h>
|
||
|
|
#include <openssl/pem.h>
|
||
|
|
#include <openssl/hmac.h>
|
||
|
|
#include <openssl/rand.h>
|
||
|
|
|
||
|
|
#include "zlib/zlib.h"
|
||
|
|
|
||
|
|
#include "eyeconstants.h"
|
||
|
|
#include "eyejson.h"
|
||
|
|
#include "eyelog.h"
|
||
|
|
#include "eyelock.h"
|
||
|
|
#ifndef WIN32
|
||
|
|
#include "eyemimemap.h"
|
||
|
|
#endif
|
||
|
|
#include "eyetime.h"
|
||
|
|
#include "eyeutils.h"
|
||
|
|
|
||
|
|
using namespace std;
|
||
|
|
using namespace sequencelogic;
|
||
|
|
|
||
|
|
//static unsigned char SL_KEY_SALT[] = {'I', 's', 'O', 'a', 'n', 'l', 'U', 't'};
|
||
|
|
static char SL_KEY_SALT[] = "49734f616e6c5574";
|
||
|
|
#define SL_KEY_SALT_LEN 8
|
||
|
|
#define SL_CHUNK_SIZE 4096 // Chunk size for file read/0write
|
||
|
|
|
||
|
|
unsigned char* sequencelogic::New (size_t bytes)
|
||
|
|
{
|
||
|
|
unsigned char* data = nullptr;
|
||
|
|
#ifdef ANDROID
|
||
|
|
data = new unsigned char[bytes];
|
||
|
|
if (!data)
|
||
|
|
return nullptr;
|
||
|
|
#else
|
||
|
|
try {
|
||
|
|
data = new unsigned char[bytes];
|
||
|
|
}
|
||
|
|
catch (std::bad_alloc) {
|
||
|
|
IONUDEBUG ("sequencelogic::New() - unable to allocate %d bytes", bytes);
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
return data;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Strips the path from a filename
|
||
|
|
std::string sequencelogic::StripPath (const std::string& filename)
|
||
|
|
{
|
||
|
|
const size_t filename_idx = filename.find_last_of("\\/");
|
||
|
|
std::string entryname;
|
||
|
|
|
||
|
|
if (std::string::npos != filename_idx)
|
||
|
|
entryname = filename.substr (filename_idx + 1, filename.size() - filename_idx - 1);
|
||
|
|
else
|
||
|
|
entryname = filename;
|
||
|
|
return entryname;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
std::string sequencelogic::Canonicalise (const std::string& path, bool lowercase)
|
||
|
|
#ifdef WIN32
|
||
|
|
// Code adapted from http://pdh11.blogspot.com/2009/05/pathcanonicalize-versus-what-it-says-on.html
|
||
|
|
{
|
||
|
|
std::wstring utf16 = sequencelogic::toWString(path);
|
||
|
|
|
||
|
|
wchar_t canon[MAX_PATH];
|
||
|
|
|
||
|
|
/** Note that PathCanonicalize does NOT do what we want here -- it's a
|
||
|
|
* purely textual operation that eliminates /./ and /../ only.
|
||
|
|
*/
|
||
|
|
DWORD rc = ::GetFullPathNameW(utf16.c_str(), MAX_PATH, canon, NULL);
|
||
|
|
if (!rc) {
|
||
|
|
std::string utf8;
|
||
|
|
utf8.resize(path.size());
|
||
|
|
std::transform(path.begin(), path.end(), utf8.begin(), ::tolower);
|
||
|
|
std::replace(utf8.begin(), utf8.end(), '\\', '/');
|
||
|
|
return utf8;
|
||
|
|
}
|
||
|
|
|
||
|
|
utf16 = canon;
|
||
|
|
|
||
|
|
if (utf16.length() >= 6) {
|
||
|
|
/** Get rid of \\?\ and \\.\ prefixes on drive-letter paths */
|
||
|
|
if (!wcsncmp(utf16.c_str(), L"\\\\?\\", 4) && utf16[5] == L':')
|
||
|
|
utf16.erase(0,4);
|
||
|
|
else if (!wcsncmp(utf16.c_str(), L"\\\\.\\", 4) && utf16[5] == L':')
|
||
|
|
utf16.erase(0,4);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (utf16.length() >= 10) {
|
||
|
|
/** Get rid of \\?\UNC on drive-letter and UNC paths */
|
||
|
|
if (!wcsncmp(utf16.c_str(), L"\\\\?\\UNC\\", 8))
|
||
|
|
{
|
||
|
|
if (utf16[9] == L':' && utf16[10] == L'\\')
|
||
|
|
utf16.erase(0,8);
|
||
|
|
else
|
||
|
|
{
|
||
|
|
utf16.erase(0,7);
|
||
|
|
utf16 = L"\\" + utf16;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Anything other than UNC and drive-letter is something we don't
|
||
|
|
* understand
|
||
|
|
*/
|
||
|
|
if (utf16[0] == L'\\' && utf16[1] == L'\\') {
|
||
|
|
if (utf16[2] == '?' || utf16[2] == '.')
|
||
|
|
return path; // Not understood
|
||
|
|
|
||
|
|
/** OK -- UNC */
|
||
|
|
}
|
||
|
|
else if (((utf16[0] >= 'A' && utf16[0] <= 'Z') || (utf16[0] >= 'a' && utf16[0] <= 'z')) && utf16[1] == ':') {
|
||
|
|
/** OK -- drive letter -- unwind subst'ing */
|
||
|
|
for (;;) {
|
||
|
|
wchar_t drive[3];
|
||
|
|
drive[0] = (wchar_t)toupper(utf16[0]);
|
||
|
|
drive[1] = L':';
|
||
|
|
drive[2] = L'\0';
|
||
|
|
canon[0] = L'\0';
|
||
|
|
rc = ::QueryDosDeviceW(drive, canon, MAX_PATH);
|
||
|
|
if (!rc)
|
||
|
|
break;
|
||
|
|
if (!wcsncmp(canon, L"\\??\\", 4))
|
||
|
|
{
|
||
|
|
utf16 = std::wstring(canon+4) + std::wstring(utf16, 2);
|
||
|
|
}
|
||
|
|
else // Not subst'd
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
wchar_t drive[4];
|
||
|
|
drive[0] = (wchar_t)toupper(utf16[0]);
|
||
|
|
drive[1] = ':';
|
||
|
|
drive[2] = '\\';
|
||
|
|
drive[3] = '\0';
|
||
|
|
|
||
|
|
#ifdef LATER
|
||
|
|
rc = ::GetDriveTypeW(drive);
|
||
|
|
if (rc == DRIVE_REMOTE) {
|
||
|
|
DWORD bufsize = MAX_PATH;
|
||
|
|
|
||
|
|
/* QueryDosDevice and WNetGetConnection FORBID the
|
||
|
|
* trailing slash; GetDriveType REQUIRES it.
|
||
|
|
*/
|
||
|
|
drive[2] = '\0';
|
||
|
|
|
||
|
|
rc = ::WNetGetConnectionW(drive, canon, &bufsize);
|
||
|
|
if (rc == NO_ERROR)
|
||
|
|
utf16 = wstring(canon) + std::wstring(utf16, 2);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
// Not understood
|
||
|
|
return path;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Canonicalise case and 8.3-ness */
|
||
|
|
rc = ::GetLongPathNameW(utf16.c_str(), canon, MAX_PATH);
|
||
|
|
if (rc)
|
||
|
|
utf16 = canon;
|
||
|
|
|
||
|
|
std::string utf8 = sequencelogic::toAString(utf16.c_str());
|
||
|
|
std::replace(utf8.begin(), utf8.end(), '\\', '/');
|
||
|
|
if (lowercase)
|
||
|
|
std::transform(utf8.begin(), utf8.end(), utf8.begin(), ::tolower);
|
||
|
|
return utf8;
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
{
|
||
|
|
#ifdef LIN64
|
||
|
|
char* cpath = realpath (path.c_str(), nullptr);
|
||
|
|
if (cpath) {
|
||
|
|
std::string fullpath (cpath);
|
||
|
|
free (cpath);
|
||
|
|
return fullpath;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
return path;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
std::string sequencelogic::TempFilename (const std::string& filename, const std::string& suffix)
|
||
|
|
{
|
||
|
|
#ifdef WIN32
|
||
|
|
// If no suffix (e.g. .lk) then use Windows code to generate a unique temp filename in
|
||
|
|
// local temp directory, avoids creation on flash drive or other slow external drive
|
||
|
|
if (suffix.size() == 0) {
|
||
|
|
TCHAR lpTempPathBuffer[MAX_PATH];
|
||
|
|
TCHAR szTempFileName[MAX_PATH];
|
||
|
|
DWORD dwRetVal = GetTempPath(MAX_PATH, lpTempPathBuffer);
|
||
|
|
if (dwRetVal <= MAX_PATH && dwRetVal > 0) {
|
||
|
|
if (GetTempFileName(lpTempPathBuffer, TEXT("IONU"), 0, szTempFileName) != 0) {
|
||
|
|
std::string tmpname(szTempFileName);
|
||
|
|
return tmpname;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
std::string tmpname(filename);
|
||
|
|
const size_t filename_idx = tmpname.find_last_of("\\/");
|
||
|
|
if (std::string::npos != filename_idx) {
|
||
|
|
if (tmpname[filename_idx + 1] != '.')
|
||
|
|
tmpname.insert(filename_idx + 1, ".");
|
||
|
|
}
|
||
|
|
else if (tmpname[0] != '.')
|
||
|
|
tmpname.insert(0, ".");
|
||
|
|
if (suffix.size() > 0)
|
||
|
|
tmpname.append(suffix);
|
||
|
|
else
|
||
|
|
tmpname.append(".tmp");
|
||
|
|
return tmpname;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool sequencelogic::Compress (unsigned char* dest, size_t* dest_len, const unsigned char* src, size_t src_len)
|
||
|
|
{
|
||
|
|
if (!dest || !src || src_len == 0)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
int rc = Z_OK;
|
||
|
|
uLong dlen = (uLong)(*dest_len);
|
||
|
|
rc = compress (dest, &dlen, src, (uLong)src_len);
|
||
|
|
*dest_len = (size_t)dlen;
|
||
|
|
if (rc == Z_OK) {
|
||
|
|
//IONUDEBUG ("sequencelogic::Compress() - %d bytes in, %d out", (int)src_len, (int)*dest_len);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool sequencelogic::Uncompress (unsigned char* dest, size_t* dest_len, const unsigned char* src, size_t src_len)
|
||
|
|
{
|
||
|
|
if (!dest || !src || src_len == 0)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
int rc = Z_OK;
|
||
|
|
uLong dlen = (uLong)(*dest_len);
|
||
|
|
rc = uncompress (dest, &dlen, src, (uLong)src_len);
|
||
|
|
*dest_len = (size_t)dlen;
|
||
|
|
if (rc == Z_OK) {
|
||
|
|
//IONUDEBUG ("sequencelogic::Uncompress() - %d bytes in, %d out", (int)src_len, (int)*dest_len);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Convert an integer value to a string of octal digits with leading 0's.
|
||
|
|
bool sequencelogic::IntToOctal (size_t value, char* buffer, size_t digits)
|
||
|
|
{
|
||
|
|
if (!buffer) return false;
|
||
|
|
char* ptr = buffer;
|
||
|
|
size_t i;
|
||
|
|
for (i = 0; i < digits; ++i)
|
||
|
|
*ptr++ = '0';
|
||
|
|
*ptr = '\0';
|
||
|
|
while (value > 0) {
|
||
|
|
*--ptr = (char) (value & 0x07) + '0';
|
||
|
|
value >>= 3;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
// Convert an Octal string to an integer value
|
||
|
|
size_t sequencelogic::OctalToInt (const char* value, size_t len)
|
||
|
|
{
|
||
|
|
if (!value) return 0;
|
||
|
|
size_t output = 0;
|
||
|
|
while (*value && len > 0) {
|
||
|
|
unsigned char digit = *value;
|
||
|
|
if (digit != ' ' && digit != '\0') {
|
||
|
|
output = output*8 + (digit - '0');
|
||
|
|
}
|
||
|
|
value++;
|
||
|
|
len--;
|
||
|
|
}
|
||
|
|
return output;
|
||
|
|
}
|
||
|
|
|
||
|
|
static const char* lut = "0123456789abcdef";
|
||
|
|
|
||
|
|
// Convert binary data to hex string, caller has allocated len*2 + 1 for hex;
|
||
|
|
bool sequencelogic::BinaryToHex (const unsigned char *data, size_t len, char* hex)
|
||
|
|
{
|
||
|
|
if (!hex) return false;
|
||
|
|
if (!data || len == 0) {
|
||
|
|
*hex = '\0';
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
char* p = hex;
|
||
|
|
for (size_t i = 0; i < len; ++i) {
|
||
|
|
const unsigned char c = data[i];
|
||
|
|
*p++ = lut[c >> 4]; // High order nyble
|
||
|
|
*p++ = lut[c & 15]; // Low order nyble
|
||
|
|
}
|
||
|
|
*p = '\0';
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Returns numeric value of hex digit
|
||
|
|
unsigned char sequencelogic::HexDigitToValue (const char digit)
|
||
|
|
{
|
||
|
|
unsigned char value = 255;
|
||
|
|
if (digit >= '0' && digit <= '9') {
|
||
|
|
value = digit - '0';
|
||
|
|
}
|
||
|
|
else if (digit >= 'a' && digit <= 'f') {
|
||
|
|
value = 10 + digit - 'a';
|
||
|
|
}
|
||
|
|
else if (digit >= 'A' && digit <= 'F') {
|
||
|
|
value = 10 + digit - 'A';
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
IONUDEBUG("HexDigitToValue: invalid hex digit <%c>", digit);
|
||
|
|
}
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Convert hex string to binary data, caller has allocated strlen()/2 for data
|
||
|
|
bool sequencelogic::HexToBinary (const char* hex, unsigned char *data)
|
||
|
|
{
|
||
|
|
if (!hex || !data) return false;
|
||
|
|
size_t len = strlen (hex);
|
||
|
|
return sequencelogic::HexToBinary (hex, len, data);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Convert hex string to binary data, caller has allocated len/2 for data
|
||
|
|
bool sequencelogic::HexToBinary (const char* hex, size_t len, unsigned char *data)
|
||
|
|
{
|
||
|
|
if (!hex || !data) return false;
|
||
|
|
// Is an even multiple of 2?
|
||
|
|
if ((len & 0x01) != 0) {
|
||
|
|
IONUDEBUG("sequencelogic::HexToBinary: one nyble shy of a byte <%s>", hex);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
len /= 2;
|
||
|
|
size_t i, in =0;
|
||
|
|
unsigned char h;
|
||
|
|
for (i = 0; i < len; ++i) {
|
||
|
|
h = HexDigitToValue (hex[in++]);
|
||
|
|
if (h == 255) return false;
|
||
|
|
data[i] = h << 4;
|
||
|
|
h = HexDigitToValue (hex[in++]);
|
||
|
|
if (h == 255) return false;
|
||
|
|
data[i] += h;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Base32 encoding lookup table
|
||
|
|
static const char* base32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Encode binary data as base32, caller has allocated (5/3 * input_len + 2) for output
|
||
|
|
* One or two '=' are used for padding as required
|
||
|
|
*
|
||
|
|
*@param input - binary data to be encoded
|
||
|
|
*@param len - number of bytes to be encodes
|
||
|
|
*@param output - base32 encoded string
|
||
|
|
*/
|
||
|
|
bool sequencelogic::Base32Encode (const unsigned char *input, size_t len, char *output)
|
||
|
|
{
|
||
|
|
if (!output) return false;
|
||
|
|
if (!input || len == 0) {
|
||
|
|
*output = '\0';
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
do {
|
||
|
|
*output++ = base32[(input[0] & 0xF8) >> 3];
|
||
|
|
|
||
|
|
if (len == 1 ) {
|
||
|
|
*output++ = base32[(input[0] & 0x07) << 2];
|
||
|
|
*output++ = '=';
|
||
|
|
*output++ = '=';
|
||
|
|
*output++ = '=';
|
||
|
|
*output++ = '=';
|
||
|
|
*output++ = '=';
|
||
|
|
*output++ = '=';
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
*output++ = base32[((input[0] & 0x07) << 2) | (input[1] & 0xC0) >> 6];
|
||
|
|
|
||
|
|
if (len == 2 ) {
|
||
|
|
*output++ = base32[(input[1] & 0x3E) >> 1];
|
||
|
|
*output++ = base32[(input[1] & 0x01) << 4];
|
||
|
|
*output++ = '=';
|
||
|
|
*output++ = '=';
|
||
|
|
*output++ = '=';
|
||
|
|
*output++ = '=';
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
*output++ = base32[(input[1] & 0x3E) >> 1];
|
||
|
|
*output++ = base32[((input[1] & 0x01) << 4) | ((input[2] & 0xF0) >> 4)];
|
||
|
|
if (len == 3 ) {
|
||
|
|
*output++ = base32[(input[2] & 0x0F) << 1];
|
||
|
|
*output++ = '=';
|
||
|
|
*output++ = '=';
|
||
|
|
*output++ = '=';
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
*output++ = base32[((input[2] & 0x0F) << 1) | ((input[3] & 0x80) >> 7)];
|
||
|
|
*output++ = base32[(input[3] & 0x7C) >> 2];
|
||
|
|
if (len == 4 ) {
|
||
|
|
*output++ = base32[(input[3] & 0x03) << 3];
|
||
|
|
*output++ = '=';
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
*output++ = base32[((input[3] & 0x03) << 3) | ((input[4] & 0xE0) >> 5)];
|
||
|
|
*output++ = base32[input[4] & 0x1F];
|
||
|
|
input += 5;
|
||
|
|
len -= 5;
|
||
|
|
}
|
||
|
|
while (len > 0);
|
||
|
|
|
||
|
|
// Terminate the base32 string
|
||
|
|
*output = '\0';
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Returns numeric value of base32 digit
|
||
|
|
unsigned char Base32DigitToValue (const char digit)
|
||
|
|
{
|
||
|
|
unsigned char value = 0;
|
||
|
|
if (digit >= 'A' && digit <= 'Z') {
|
||
|
|
value = digit - 'A';
|
||
|
|
}
|
||
|
|
else if (digit >= '2' && digit <= '7') {
|
||
|
|
value = 26 + digit - '2';
|
||
|
|
}
|
||
|
|
else if (digit != '=') {
|
||
|
|
IONUDEBUG("Base32DigitToValue: invalid base32 digit <%c>", digit);
|
||
|
|
}
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Convert base32 string to binary data, caller has allocated strlen()/2 for data
|
||
|
|
size_t sequencelogic::Base32Decode (const char* base32, unsigned char *data)
|
||
|
|
{
|
||
|
|
if (!base32 || !data) return 0;
|
||
|
|
size_t len = strlen (base32);
|
||
|
|
|
||
|
|
size_t i = 0, pos = 0;
|
||
|
|
int digit = 0;
|
||
|
|
unsigned char byte = 0;
|
||
|
|
while (i < len) {
|
||
|
|
if (base32[i] == '=') {
|
||
|
|
++i;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
// unsigned char bb = Base32DigitToValue(base32[i]);
|
||
|
|
// sequencelogic::DumpAsBinary (&bb, 1);
|
||
|
|
switch (digit) {
|
||
|
|
case 0 :
|
||
|
|
byte = Base32DigitToValue (base32[i++]) << 3;
|
||
|
|
break;
|
||
|
|
case 1 :
|
||
|
|
byte |= Base32DigitToValue (base32[i]) >> 2;
|
||
|
|
data[pos++] = byte;
|
||
|
|
byte = Base32DigitToValue (base32[i++]) << 6;
|
||
|
|
break;
|
||
|
|
case 2 :
|
||
|
|
byte |= Base32DigitToValue (base32[i++]) << 1;
|
||
|
|
break;
|
||
|
|
case 3 :
|
||
|
|
byte |= Base32DigitToValue (base32[i]) >> 4;
|
||
|
|
data[pos++] = byte;
|
||
|
|
byte = Base32DigitToValue (base32[i++]) << 4;
|
||
|
|
break;
|
||
|
|
case 4 :
|
||
|
|
byte |= Base32DigitToValue (base32[i]) >> 1;
|
||
|
|
data[pos++] = byte;
|
||
|
|
byte = Base32DigitToValue (base32[i++]) << 7;
|
||
|
|
break;
|
||
|
|
case 5 :
|
||
|
|
byte |= Base32DigitToValue (base32[i++]) << 2;
|
||
|
|
break;
|
||
|
|
case 6 :
|
||
|
|
byte |= Base32DigitToValue (base32[i]) >> 3;
|
||
|
|
data[pos++] = byte;
|
||
|
|
byte = Base32DigitToValue (base32[i++]) << 5;
|
||
|
|
break;
|
||
|
|
case 7 :
|
||
|
|
byte |= Base32DigitToValue (base32[i++]);
|
||
|
|
data[pos++] = byte;
|
||
|
|
digit = -1;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
digit++;
|
||
|
|
}
|
||
|
|
return pos;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
// Base64 encoding lookup table
|
||
|
|
static const char* base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Encode binary data as base64, caller has allocated (4/3 * input_len + 4) for output
|
||
|
|
* One or two '=' are used for padding as required
|
||
|
|
*
|
||
|
|
*@param input - binary data to be encoded
|
||
|
|
*@param len - number of bytes to be encodes
|
||
|
|
*@param output - base64 encoded string
|
||
|
|
*/
|
||
|
|
bool sequencelogic::Base64Encode (const unsigned char *input, size_t len, char *output )
|
||
|
|
{
|
||
|
|
if (!output) return false;
|
||
|
|
if (!input || len == 0) {
|
||
|
|
*output = '\0';
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
do {
|
||
|
|
*output++ = base64[(input[0] & 0xFC) >> 2];
|
||
|
|
|
||
|
|
if ( len == 1 ) {
|
||
|
|
*output++ = base64[((input[0] & 0x03) << 4)];
|
||
|
|
*output++ = '=';
|
||
|
|
*output++ = '=';
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
*output++ = base64[((input[0] & 0x03) << 4) | ((input[1] & 0xF0) >> 4)];
|
||
|
|
|
||
|
|
if ( len == 2 ) {
|
||
|
|
*output++ = base64[((input[1] & 0x0F) << 2)];
|
||
|
|
*output++ = '=';
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
*output++ = base64[((input[1] & 0x0F) << 2 ) | ((input[2] & 0xC0) >> 6)];
|
||
|
|
*output++ = base64[(input[2] & 0x3F)];
|
||
|
|
input += 3;
|
||
|
|
}
|
||
|
|
while (len -= 3);
|
||
|
|
|
||
|
|
// Terminate the base64 string
|
||
|
|
*output = '\0';
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Base64 decoding lookup table, base64url '-' and '_' are equivalent to '+' and '/' when decoding
|
||
|
|
static int unbase64[] =
|
||
|
|
{
|
||
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52,
|
||
|
|
53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, -1,
|
||
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||
|
|
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -1,
|
||
|
|
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
|
||
|
|
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Decode base64 string to binary, caller has allocated (3/4 * input_len) for output
|
||
|
|
* One or two '=' are used for padding as required
|
||
|
|
*
|
||
|
|
*@param input - base64 encoded string
|
||
|
|
*@param output - decoded binary data
|
||
|
|
*@returns len - number of bytes in binary data, -1 if error
|
||
|
|
*/
|
||
|
|
size_t sequencelogic::Base64Decode (const char *input, unsigned char *output)
|
||
|
|
{
|
||
|
|
if (!input || !output)
|
||
|
|
return 0;
|
||
|
|
size_t len = strlen (input);
|
||
|
|
if (len ==0 )
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
size_t out_len = 0;
|
||
|
|
size_t i = 0;
|
||
|
|
char *base64 = new char [len + 1];
|
||
|
|
if (!base64)
|
||
|
|
return out_len;
|
||
|
|
char *b64 = base64;
|
||
|
|
|
||
|
|
// Check for invalid characters and remove any new lines or extraneous backslashes
|
||
|
|
while (*input) {
|
||
|
|
char c = *input++;
|
||
|
|
if (c == '\n' || c == '\\') {
|
||
|
|
len--;
|
||
|
|
}
|
||
|
|
else if ((c & 0x80) != 0 || unbase64[(int)c] == -1) {
|
||
|
|
IONUDEBUG ("invalid character for base64 encoding: <%c>", c);
|
||
|
|
delete[] base64;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
b64[i++] = c;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
b64[i] = '\0';
|
||
|
|
|
||
|
|
// Is an even multiple of 4
|
||
|
|
if (len < 4 || (len & 0x03) != 0) {
|
||
|
|
delete[] base64;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
do {
|
||
|
|
*output++ = (unsigned char)unbase64[(int)b64[0]] << 2 | (unbase64[(int)b64[1]] & 0x30) >> 4;
|
||
|
|
out_len++;
|
||
|
|
if (b64[2] != '=') {
|
||
|
|
*output++ = (unbase64[(int)b64[1]] & 0x0F) << 4 | (unbase64[(int)b64[2]] & 0x3C) >> 2;
|
||
|
|
out_len++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (b64[3] != '=') {
|
||
|
|
*output++ = (unbase64[(int)b64[2]] & 0x03) << 6 | (unsigned char)unbase64[(int)b64[3]];
|
||
|
|
out_len++;
|
||
|
|
}
|
||
|
|
b64 += 4;
|
||
|
|
}
|
||
|
|
while (len -= 4);
|
||
|
|
delete [] base64;
|
||
|
|
|
||
|
|
return out_len;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Format a time_t struct as ISO 8601 formatted string (20 characters)
|
||
|
|
std::string sequencelogic::GetISO8601Time (time_t t)
|
||
|
|
{
|
||
|
|
struct tm tm;
|
||
|
|
#ifdef WIN32
|
||
|
|
::gmtime_s(&tm, &t);
|
||
|
|
#else
|
||
|
|
gmtime_r(&t, &tm);
|
||
|
|
#endif
|
||
|
|
char isodate[32];
|
||
|
|
sprintf(isodate, "%04d-%02d-%02dT%02d:%02d:%02dZ",
|
||
|
|
tm.tm_year+1900,
|
||
|
|
tm.tm_mon+1,
|
||
|
|
tm.tm_mday,
|
||
|
|
tm.tm_hour,
|
||
|
|
tm.tm_min,
|
||
|
|
tm.tm_sec);
|
||
|
|
std::string datestr (isodate);
|
||
|
|
return datestr;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get the current time as a long in milliseconds for timing and performance work
|
||
|
|
long sequencelogic::GetSystemTime ()
|
||
|
|
{
|
||
|
|
long ms;
|
||
|
|
#ifdef WIN32
|
||
|
|
FILETIME ft;
|
||
|
|
LARGE_INTEGER li;
|
||
|
|
|
||
|
|
/* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
|
||
|
|
* to a LARGE_INTEGER structure. */
|
||
|
|
GetSystemTimeAsFileTime (&ft);
|
||
|
|
li.LowPart = ft.dwLowDateTime;
|
||
|
|
li.HighPart = ft.dwHighDateTime;
|
||
|
|
|
||
|
|
li.QuadPart /= 10000; // 100 nano seconds (10^-7) to milli seconds (10^-3)
|
||
|
|
li.QuadPart -= 11644473600000l; // Convert from file time to UNIX epoch time
|
||
|
|
ms = (long)li.QuadPart;
|
||
|
|
#else
|
||
|
|
struct timeval t;
|
||
|
|
gettimeofday (&t, 0);
|
||
|
|
ms = t.tv_sec * 1000 + t.tv_usec / 1000;
|
||
|
|
#endif
|
||
|
|
return ms;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Merge 2 half keys using xor to create a new key (works with any number of bytes)
|
||
|
|
bool sequencelogic::XorKeys (const unsigned char* k1, const unsigned char* k2, unsigned char* ok, size_t len)
|
||
|
|
{
|
||
|
|
if (!k1 || !k2 || !ok || len == 0)
|
||
|
|
return false;
|
||
|
|
for (size_t i = 0; i < len; ++i) {
|
||
|
|
*ok++ = *k1++ ^ *k2++;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// sequencelogic::StrDup implementation using new[]delete memory allocation, exception handling and NULL check
|
||
|
|
char* sequencelogic::StrDup (const char* str)
|
||
|
|
{
|
||
|
|
// Check for NULL, allow empty string, check for binary
|
||
|
|
char* dup = NULL;
|
||
|
|
if (str && (*str == '\0' || sequencelogic::StrLen (str, 8) > 0)) {
|
||
|
|
size_t len = strlen (str);
|
||
|
|
#ifdef ANDROID
|
||
|
|
dup = new char [len + 1];
|
||
|
|
if (!dup)
|
||
|
|
return NULL;
|
||
|
|
#else
|
||
|
|
try {
|
||
|
|
dup = new char [len + 1];
|
||
|
|
}
|
||
|
|
catch (std::bad_alloc)
|
||
|
|
{
|
||
|
|
IONUDEBUG ("sequencelogic::StrDup() - unable to allocate %d bytes", static_cast<int>(len));
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
memcpy (dup, str, len + 1);
|
||
|
|
}
|
||
|
|
return dup;
|
||
|
|
}
|
||
|
|
|
||
|
|
// StrCmp implementation with pointer checking
|
||
|
|
int sequencelogic::StrCmp (const char* str1, const char* str2)
|
||
|
|
{
|
||
|
|
int rc = 0;
|
||
|
|
if (str1 == str2 )
|
||
|
|
rc = 0;
|
||
|
|
else if (!str1)
|
||
|
|
rc = -1;
|
||
|
|
else if (!str2)
|
||
|
|
rc = 1;
|
||
|
|
else
|
||
|
|
rc = strcmp (str1, str2);
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
// StrnCmp implementation with pointer checking
|
||
|
|
int sequencelogic::StrnCmp (const char* str1, const char* str2, size_t len)
|
||
|
|
{
|
||
|
|
int rc = 0;
|
||
|
|
if (str1 == str2 || len == 0)
|
||
|
|
rc = 0;
|
||
|
|
else if (!str1)
|
||
|
|
rc = -1;
|
||
|
|
else if (!str2)
|
||
|
|
rc = 1;
|
||
|
|
else
|
||
|
|
rc = strncmp (str1, str2, len);
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
// StrCpy implementation with pointer checking
|
||
|
|
void sequencelogic::StrCpy (char* dst, const char* src)
|
||
|
|
{
|
||
|
|
if (dst && src) // Both strings are "ok"
|
||
|
|
strcpy (dst, src);
|
||
|
|
else if (dst && !src) // Source string is NULL
|
||
|
|
*dst = '\0';
|
||
|
|
}
|
||
|
|
|
||
|
|
// StrCat implementation with pointer checking
|
||
|
|
void sequencelogic::StrCat (char* dst, const char* src)
|
||
|
|
{
|
||
|
|
if (dst && src) // Both strings are "ok"
|
||
|
|
strcat (dst, src);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
// StrLen implementation with pointer, max length limit and isascii checking
|
||
|
|
size_t sequencelogic::StrLen (const char* str, size_t max)
|
||
|
|
{
|
||
|
|
if (str) {
|
||
|
|
for (size_t i = 0; i < max; ++i) {
|
||
|
|
unsigned char c = static_cast<unsigned char>(str[i]);
|
||
|
|
if (c == '\0')
|
||
|
|
return i;
|
||
|
|
else if (c != '\n' && c != '\r' && c != '\t' && (c > 0x7f || c < 0x20))
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
return max + 1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int sequencelogic::MemCmp (const void* mem1, const void* mem2, size_t len)
|
||
|
|
{
|
||
|
|
int rc = 0;
|
||
|
|
if (mem1 == mem2 )
|
||
|
|
rc = 0;
|
||
|
|
else if (!mem1)
|
||
|
|
rc = -1;
|
||
|
|
else if (!mem2)
|
||
|
|
rc = 1;
|
||
|
|
else
|
||
|
|
rc = memcmp (mem1, mem2, len);
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
void sequencelogic::MemCpy (void* dst, const void* src, size_t len)
|
||
|
|
{
|
||
|
|
if (dst && src)
|
||
|
|
memcpy (dst, src, len);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Keep the compiler from optimizing this as dead code, as this is used to clear keys
|
||
|
|
void sequencelogic::MemSet (volatile void* mem, unsigned char val, size_t len)
|
||
|
|
{
|
||
|
|
if (mem && len > 0) {
|
||
|
|
volatile unsigned char* buf = static_cast<volatile unsigned char*>(mem);
|
||
|
|
while (len--)
|
||
|
|
*buf++ = val;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check string data to see if it contains any invalid ascii characters
|
||
|
|
bool sequencelogic::IsStrAscii (const char* str, size_t len)
|
||
|
|
{
|
||
|
|
if (!str)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
for (size_t i = 0; i < len; ++i) {
|
||
|
|
unsigned char c = static_cast<unsigned char>(str[i]);
|
||
|
|
if (c == '\0' && i+1 >= len) // Null termination
|
||
|
|
return true;
|
||
|
|
else if (c != '\n' && c != '\r' && c != '\t' && (c > 0x7f || c < 0x20)) {
|
||
|
|
//IONUDEBUG ("sequencelogic::IsStrAscii() - non-ascii character <%x> found at %d of %d", (int)c, (int)i, (int)len);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check string data to see if it contains any invalid UTF-8 encodings
|
||
|
|
bool sequencelogic::IsStrUTF8 (const char* s, size_t len)
|
||
|
|
{
|
||
|
|
if (!s)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
for (size_t i = 0; i < len; ++i) {
|
||
|
|
unsigned char c = static_cast<unsigned char>(s[i]);
|
||
|
|
if (c <= 0x7f) // 1 byte encoding
|
||
|
|
continue;
|
||
|
|
else if ((c & 0xE0) == 0xC0) { // 2 byte encoding
|
||
|
|
if (i+1 < len && (s[i+1] & 0xC0) == 0x80)
|
||
|
|
++i;
|
||
|
|
else
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else if ((c & 0xF0) == 0xE0) { // 3 byte encoding
|
||
|
|
if (i+2 < len && (s[i+1] & 0xC0) == 0x80 && (s[i+2] & 0xC0) == 0x80)
|
||
|
|
i += 2;
|
||
|
|
else
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else if ((c & 0xF8) == 0xF0) { // 4 byte encoding
|
||
|
|
if (i+3 < len && (s[i+1] & 0xC0) == 0x80 && (s[i+2] & 0xC0) == 0x80 && (s[i+3] & 0xC0) == 0x80)
|
||
|
|
i += 3;
|
||
|
|
else
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check a filename to help ensure that it is valid
|
||
|
|
bool sequencelogic::IsValidFilename (const char* filename)
|
||
|
|
{
|
||
|
|
if (filename && *filename) {
|
||
|
|
for (size_t i = 0; i < SL_MAX_PATH; ++i) {
|
||
|
|
unsigned char c = static_cast<unsigned char>(filename[i]);
|
||
|
|
if (c == '\0' && i > 0)
|
||
|
|
return true;
|
||
|
|
else if (c > 0x7f || c < 0x20)
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool sequencelogic::IsValidFilename (const std::string& filename)
|
||
|
|
{
|
||
|
|
if (filename.size() > 0 && filename.size() <= SL_MAX_PATH) {
|
||
|
|
for (std::string::const_iterator itr = filename.begin(); itr != filename.end(); ++itr) {
|
||
|
|
unsigned char c = static_cast<unsigned char>(*itr);
|
||
|
|
if (c == '\0' && itr != filename.begin())
|
||
|
|
return true;
|
||
|
|
else if (c > 0x7f || c < 0x20)
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check a filename to ensure that it is valid and can be opened for read
|
||
|
|
bool sequencelogic::CanReadFile (const char* filename)
|
||
|
|
{
|
||
|
|
if (sequencelogic::IsValidFilename (filename)) {
|
||
|
|
ifstream file (filename, ios::in | ios::binary);
|
||
|
|
if (file.is_open()) {
|
||
|
|
file.close();
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
// Check a filename to ensure that it is valid and can be opened for read
|
||
|
|
bool sequencelogic::CanReadFile (const std::string& filename)
|
||
|
|
{
|
||
|
|
if (sequencelogic::IsValidFilename (filename)) {
|
||
|
|
ifstream file (filename, ios::in | ios::binary);
|
||
|
|
if (file.is_open()) {
|
||
|
|
file.close();
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string sequencelogic::EncryptFilename (const std::string& filename, const Key& key)
|
||
|
|
{
|
||
|
|
if (key.GetType() == Key::AES) {
|
||
|
|
size_t bytes = 0;
|
||
|
|
unsigned char* edata = key.SymmetricEncryptBuffer ((const unsigned char*)filename.data(), filename.size(), &bytes, key.GetIv());
|
||
|
|
//size_t b64len = 4 * bytes / 3 + 4;
|
||
|
|
char buff[512];
|
||
|
|
sequencelogic::Base64Encode (edata, bytes, buff);
|
||
|
|
std::string b64 = buff;
|
||
|
|
delete[] edata;
|
||
|
|
return b64;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
return filename;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string sequencelogic::DecryptFilename (const std::string& filename, const Key& key)
|
||
|
|
{
|
||
|
|
if (key.GetType() == Key::AES) {
|
||
|
|
size_t bytes = 0;
|
||
|
|
unsigned char edata[512]; // 3 * filename.size() / 4;
|
||
|
|
bytes = sequencelogic::Base64Decode (filename.data(), edata);
|
||
|
|
char* fname = (char*)key.SymmetricDecryptBuffer (edata, bytes, &bytes, key.GetIv());
|
||
|
|
std::string b64 (fname, bytes);
|
||
|
|
delete[] fname;
|
||
|
|
return b64;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
return filename;
|
||
|
|
}
|
||
|
|
|
||
|
|
int sequencelogic::CompareFilenames (const std::string& f1, const std::string& f2)
|
||
|
|
{
|
||
|
|
#ifdef WIN32
|
||
|
|
if (f1.size() > f2.size())
|
||
|
|
return 1;
|
||
|
|
else if (f1.size() < f2.size())
|
||
|
|
return -1;
|
||
|
|
else {
|
||
|
|
for (size_t i = 0; i < f1.size(); ++i) {
|
||
|
|
unsigned char c1 = (unsigned char)(isalpha (f1[i]) ? toupper (f1[i]) : f1[i]);
|
||
|
|
unsigned char c2 = (unsigned char)(isalpha (f2[i]) ? toupper (f2[i]) : f2[i]);
|
||
|
|
if (c1 > c2)
|
||
|
|
return 1;
|
||
|
|
else if (c1 < c2)
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
return f1.compare (f2);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check a filename to ensure that it is valid and if the file exists in gzip format
|
||
|
|
bool sequencelogic::IsGzipFile (const std::string& filename)
|
||
|
|
{
|
||
|
|
if (sequencelogic::IsValidFilename (filename)) {
|
||
|
|
ifstream file (filename, ios::in | ios::binary);
|
||
|
|
if (file.is_open()) {
|
||
|
|
char header[8];
|
||
|
|
sequencelogic::MemSet (header, 0, 8);
|
||
|
|
char hex[16];
|
||
|
|
file.read (header, 8);
|
||
|
|
sequencelogic::BinaryToHex ((unsigned char*)header, 3, hex);
|
||
|
|
file.close();
|
||
|
|
if (sequencelogic::StrnCmp (hex, SL_EYE_GZIP_HEADER, sizeof(SL_EYE_GZIP_HEADER)-1) == 0) { // gzip and deflate
|
||
|
|
//IONUDEBUG ("%s is gzip", filename);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool sequencelogic::IsLockFile (const std::string& filename)
|
||
|
|
{
|
||
|
|
std::string ext = "";
|
||
|
|
size_t dot = filename.find_last_of(".");
|
||
|
|
if (dot != std::string::npos && ++dot < filename.size())
|
||
|
|
ext = filename.substr(dot, filename.size() - dot);
|
||
|
|
#ifdef WIN32
|
||
|
|
if (ext.size() == 2 && (ext.compare (0, 2, "lk") == 0 || ext.compare (0, 4, "LK") == 0))
|
||
|
|
#else
|
||
|
|
if (ext.size() == 2 && ext.compare (0, 2, "lk") == 0)
|
||
|
|
#endif
|
||
|
|
return true;
|
||
|
|
else
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool sequencelogic::CopyFiles (const std::string& s, const std::string& d)
|
||
|
|
{
|
||
|
|
ifstream src (s, ios::binary);
|
||
|
|
if (! src.is_open())
|
||
|
|
return false;
|
||
|
|
|
||
|
|
ofstream dst (d, ios::binary);
|
||
|
|
if (! dst.is_open()) {
|
||
|
|
src.close();
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
dst << src.rdbuf();
|
||
|
|
src.close();
|
||
|
|
dst.close();
|
||
|
|
|
||
|
|
if (dst.fail()) {
|
||
|
|
sequencelogic::RemoveFile (d);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool sequencelogic::RenameFile (const std::string& src, const std::string& dst)
|
||
|
|
{
|
||
|
|
// Check if the case insensitive names are the same
|
||
|
|
if (sequencelogic::CompareFilenames (src, dst) == 0)
|
||
|
|
return true;
|
||
|
|
|
||
|
|
// Remove any old locks
|
||
|
|
sequencelogic::ReleaseEyeLock (src);
|
||
|
|
bool okay = true;
|
||
|
|
#ifdef WIN32
|
||
|
|
// There are times when Dropbox or other sync programs are syncing the file behind us. Retry a few times.
|
||
|
|
for (int i = 0; !(okay = !!MoveFileEx(src.c_str(), dst.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) && i < 3; i++)
|
||
|
|
Sleep(100 + i*200); // we *really* don't want to fail!
|
||
|
|
#else
|
||
|
|
//remove (dst.c_str()); // May be needed on non-Posix systems, failure expected when doesn't exist so don't check
|
||
|
|
okay = (rename (src.c_str(), dst.c_str()) == 0);
|
||
|
|
#endif
|
||
|
|
if (!okay) {
|
||
|
|
IONUDEBUG ("sequencelogic::RenameFile(%s, %s) failed: %s", src.c_str(), dst.c_str(), sequencelogic::GetLastErrorMessage());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool sequencelogic::RemoveFile (const std::string& src)
|
||
|
|
{
|
||
|
|
// Remove any old locks
|
||
|
|
sequencelogic::ReleaseEyeLock (src);
|
||
|
|
#ifdef WIN32
|
||
|
|
if (!DeleteFile (src.c_str())) {
|
||
|
|
#else
|
||
|
|
if (remove (src.c_str()) != 0) {
|
||
|
|
#endif
|
||
|
|
if (!sequencelogic::IsLockFile (src)) {
|
||
|
|
IONUDEBUG ("sequencelogic::RemoveFile(%s) failed: %s", src.c_str(), sequencelogic::GetLastErrorMessage());
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Split a string into tokens based on separator
|
||
|
|
std::vector<std::string> sequencelogic::SplitString (const std::string& value, const char separator)
|
||
|
|
{
|
||
|
|
std::vector<std::string> tokens;
|
||
|
|
std::string::size_type pos = 0;
|
||
|
|
std::string::size_type end = value.find (separator, pos);
|
||
|
|
while (end != std::string::npos) {
|
||
|
|
tokens.push_back (value.substr (pos, end - pos));
|
||
|
|
pos = end + 1;
|
||
|
|
end = value.find (separator, pos);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get the last token or return the string if no separators found
|
||
|
|
if (pos <= value.size())
|
||
|
|
tokens.push_back (value.substr (pos));
|
||
|
|
return tokens;
|
||
|
|
}
|
||
|
|
|
||
|
|
void sequencelogic::ReplaceStringInSitu(std::string& target, const std::string& search, const std::string& replace)
|
||
|
|
{
|
||
|
|
size_t pos = 0;
|
||
|
|
while ((pos = target.find(search, pos)) != std::string::npos) {
|
||
|
|
target.replace(pos, search.length(), replace);
|
||
|
|
pos += replace.length();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check to ensure the urn is valid
|
||
|
|
// urn:sl:VAN:PMO:device:document
|
||
|
|
bool sequencelogic::IsValidURN (const char* urn)
|
||
|
|
{
|
||
|
|
EyeURN* myurn = new EyeURN (urn);
|
||
|
|
bool rc = myurn->IsValid();
|
||
|
|
delete myurn;
|
||
|
|
return rc;
|
||
|
|
}
|
||
|
|
|
||
|
|
char* EyeURN::GetURN()
|
||
|
|
{
|
||
|
|
char urn[SL_URN_MAX_LEN + 1];
|
||
|
|
strcpy (urn, SL_URN_PREFIX);
|
||
|
|
strcat (urn, _van);
|
||
|
|
strcat (urn, ":");
|
||
|
|
strcat (urn, _pmo);
|
||
|
|
strcat (urn, ":");
|
||
|
|
strcat (urn, _dev);
|
||
|
|
strcat (urn, ":");
|
||
|
|
strcat (urn, _doc);
|
||
|
|
size_t len = strlen (urn) + 1;
|
||
|
|
char* u = new char [len];
|
||
|
|
memcpy (u, urn, len);
|
||
|
|
return u;
|
||
|
|
}
|
||
|
|
|
||
|
|
EyeURN::EyeURN (const char* urn)
|
||
|
|
{
|
||
|
|
if (sequencelogic::StrLen (urn, SL_URN_MAX_LEN) < SL_URN_MIN_LEN || strncmp (urn, SL_URN_PREFIX, 9) != 0) {
|
||
|
|
IONUDEBUG ("URN: invalid urn");
|
||
|
|
_isValid = false;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
_isValid = true;
|
||
|
|
|
||
|
|
int digits = 0;
|
||
|
|
URN_FIELD field = VAN;
|
||
|
|
const char* ptr = urn + 9; // skip "urn:sl:"
|
||
|
|
char* fptr = _van;
|
||
|
|
|
||
|
|
while (*ptr && _isValid) {
|
||
|
|
char c = *ptr++;
|
||
|
|
if (c == ':') {
|
||
|
|
*fptr = '\0';
|
||
|
|
digits = 0;
|
||
|
|
if (field == VAN) {
|
||
|
|
field = PMO;
|
||
|
|
fptr = _pmo;
|
||
|
|
}
|
||
|
|
else if (field == PMO) {
|
||
|
|
field = DEV;
|
||
|
|
fptr = _dev;
|
||
|
|
}
|
||
|
|
else if (field == DEV) {
|
||
|
|
field = DOC;
|
||
|
|
fptr = _doc;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
IONUDEBUG ("URN: invalid number of fields: %s", urn);
|
||
|
|
_isValid = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (!isxdigit (c)) {
|
||
|
|
// Invalid character
|
||
|
|
IONUDEBUG ("URN: invalid char <%c>: %s", c, urn);
|
||
|
|
_isValid = false;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
digits++;
|
||
|
|
if (field == VAN && digits > SL_URN_VAN_LEN) {
|
||
|
|
IONUDEBUG ("URN: VAN value out of range: %s", urn);
|
||
|
|
_isValid = false;
|
||
|
|
}
|
||
|
|
else if (field == PMO && digits > SL_URN_PMO_LEN) {
|
||
|
|
IONUDEBUG ("URN: PMO value out of range: %s", urn);
|
||
|
|
_isValid = false;
|
||
|
|
}
|
||
|
|
else if (field == DEV && digits > SL_URN_DEV_LEN) {
|
||
|
|
IONUDEBUG ("URN: DEV value out of range: %s", urn);
|
||
|
|
_isValid = false;
|
||
|
|
}
|
||
|
|
else if (field == DOC && digits > SL_URN_DOC_LEN) {
|
||
|
|
IONUDEBUG ("URN: DOC value out of range: %s", urn);
|
||
|
|
_isValid = false;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
*fptr++ = c;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
*fptr = '\0';
|
||
|
|
|
||
|
|
// Check to see if right number of fields and range check values based on number of allowed digits
|
||
|
|
if (_isValid && field != DOC) {
|
||
|
|
IONUDEBUG ("URN: invalid number of fields: %s", urn);
|
||
|
|
_isValid = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Guess at Internet media type (MIME) if not specified, map is defined in "eyemimemap.h"
|
||
|
|
const std::string sequencelogic::GuessMimeType (const std::string& filename)
|
||
|
|
{
|
||
|
|
std::string mime ("text/plain");
|
||
|
|
if (sequencelogic::IsValidFilename (filename)) {
|
||
|
|
// If mimetype not specified, guess by extension
|
||
|
|
std::string ext = "";
|
||
|
|
size_t dot = filename.find_last_of(".");
|
||
|
|
if (dot != std::string::npos && ++dot < filename.size())
|
||
|
|
ext = filename.substr(dot, filename.size() - dot);
|
||
|
|
if (ext.size() > 1) {
|
||
|
|
#ifndef WIN32
|
||
|
|
auto search = sequencelogic::eyemimemap.find (ext);
|
||
|
|
if (search != sequencelogic::eyemimemap.end ())
|
||
|
|
mime = search->second;
|
||
|
|
#else
|
||
|
|
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
|
||
|
|
if (ext.compare ("docx") == 0)
|
||
|
|
mime = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
||
|
|
else if (ext.compare ("pptx") == 0)
|
||
|
|
mime = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
||
|
|
else if (ext.compare ("xlsx") == 0)
|
||
|
|
mime = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||
|
|
else if (ext.compare (0, 3, "doc") == 0)
|
||
|
|
mime = "application/vnd.ms-word";
|
||
|
|
else if (ext.compare ("db") == 0)
|
||
|
|
mime = "application/x-sqlite3";
|
||
|
|
else if (ext.compare ("png") == 0)
|
||
|
|
mime = "image/png";
|
||
|
|
else if (ext.compare ("gif") == 0)
|
||
|
|
mime = "image/gif";
|
||
|
|
else if (ext.compare (0, 2, "gz") == 0)
|
||
|
|
mime = "application/zip";
|
||
|
|
else if (ext.compare ("zip") == 0)
|
||
|
|
mime = "application/zip";
|
||
|
|
else if (ext.compare ("bmp") == 0)
|
||
|
|
mime = "image/bmp";
|
||
|
|
else if (ext.compare ("ico") == 0)
|
||
|
|
mime = "image/x-icon";
|
||
|
|
else if (ext.compare ("pdf") == 0)
|
||
|
|
mime = "application/pdf";
|
||
|
|
else if (ext.compare (0, 2, "jp") == 0)
|
||
|
|
mime = "image/jpeg";
|
||
|
|
else if (ext.compare ("mp3") == 0)
|
||
|
|
mime = "audio/mpeg";
|
||
|
|
else if (ext.compare ("ogg") == 0)
|
||
|
|
mime = "audio/ogg";
|
||
|
|
else if (ext.compare ("mov") == 0 || ext.compare ("qt") == 0)
|
||
|
|
mime = "video/quicktime";
|
||
|
|
else if (ext.compare ("html") == 0 || ext.compare ("htm") == 0)
|
||
|
|
mime = "text/html";
|
||
|
|
else if (ext.compare (0, 2, "mp") == 0)
|
||
|
|
mime = "video/mpeg";
|
||
|
|
else if (ext.compare (0, 2, "xl") == 0)
|
||
|
|
mime = "application/vnd.ms-excel";
|
||
|
|
else if (ext.compare (0, 2, "pp") == 0)
|
||
|
|
mime = "application/vnd.ms-powerpoint";
|
||
|
|
else if (ext.compare ("json") == 0)
|
||
|
|
mime = "application/json";
|
||
|
|
else if (ext.compare ("sequencelogic") == 0)
|
||
|
|
mime = "application/ionu";
|
||
|
|
else if (ext.compare ("ionx") == 0)
|
||
|
|
mime = "application/ionu-interchange";
|
||
|
|
else if (ext.compare ("vcf") == 0)
|
||
|
|
mime = "text/x-vcard";
|
||
|
|
else if (ext.compare ("pgp") == 0)
|
||
|
|
mime = "application/pgp-keys";
|
||
|
|
#endif
|
||
|
|
else {
|
||
|
|
// Read in the first 32 bytes of the file and do some checking
|
||
|
|
ifstream file (filename, ios::in | ios::binary);
|
||
|
|
if (file.is_open()) {
|
||
|
|
char buff[32];
|
||
|
|
file.read (buff, 32);
|
||
|
|
buff[31] = '\0';
|
||
|
|
if (! sequencelogic::IsStrAscii (buff, 31))
|
||
|
|
mime = "application/octet-stream"; // Binary
|
||
|
|
else if (strstr (buff, "<html>"))
|
||
|
|
mime = "text/html";
|
||
|
|
else if (strstr (buff, "<?xml"))
|
||
|
|
mime = "application/xml";
|
||
|
|
file.close();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return mime;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Internal convenience routines for performance
|
||
|
|
size_t sequencelogic::Decryptor (EVP_CIPHER_CTX* ctx, size_t len, const unsigned char* ciphertext,
|
||
|
|
unsigned char* plaintext, const unsigned char* key,
|
||
|
|
const unsigned char* iv)
|
||
|
|
{
|
||
|
|
int bytes = 0;
|
||
|
|
int fbytes = 0;
|
||
|
|
EVP_DecryptInit_ex (ctx, EVP_aes_256_cbc(), NULL, key, iv);
|
||
|
|
if (!EVP_DecryptUpdate (ctx, plaintext, &bytes, ciphertext, static_cast<int>(len))) {
|
||
|
|
IONUDEBUG ("EVP_DecryptUpdate failed for %d bytes", (int)len);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!EVP_DecryptFinal_ex (ctx, plaintext + bytes, &fbytes)) {
|
||
|
|
IONUDEBUG ("EVP_DecryptFinal_ex failed");
|
||
|
|
}
|
||
|
|
return bytes + fbytes;
|
||
|
|
}
|
||
|
|
|
||
|
|
size_t sequencelogic::Encryptor (EVP_CIPHER_CTX* ctx, size_t len, const unsigned char* plaintext,
|
||
|
|
unsigned char* ciphertext, const unsigned char* key,
|
||
|
|
const unsigned char* iv)
|
||
|
|
{
|
||
|
|
int bytes = 0;
|
||
|
|
int fbytes = 0;
|
||
|
|
EVP_EncryptInit_ex (ctx, EVP_aes_256_cbc(), NULL, key, iv);
|
||
|
|
if (!EVP_EncryptUpdate (ctx, ciphertext, &bytes, plaintext, static_cast<int>(len))) {
|
||
|
|
IONUDEBUG ("EVP_EncryptUpdate failed for %d bytes", (int)len);
|
||
|
|
}
|
||
|
|
if (!EVP_EncryptFinal_ex (ctx, ciphertext + bytes, &fbytes)) {
|
||
|
|
IONUDEBUG ("EVP_EncryptFinal_ex failed");
|
||
|
|
}
|
||
|
|
return bytes + fbytes;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get a pointer to the function that implements the named message digest
|
||
|
|
const EVP_MD* sequencelogic::GetMessageDigester (const char *digestName) {
|
||
|
|
const EVP_MD* md = EVP_get_digestbyname(digestName);
|
||
|
|
if (!md) {
|
||
|
|
// In case init has not been called
|
||
|
|
if (strcmp (MESSAGE_DIGEST_MD5, digestName) == 0) {
|
||
|
|
md = EVP_md5();
|
||
|
|
}
|
||
|
|
else if (strcmp (MESSAGE_DIGEST_SHA1, digestName) == 0) {
|
||
|
|
md = EVP_sha1();
|
||
|
|
}
|
||
|
|
else if (strcmp (MESSAGE_DIGEST_SHA224, digestName) == 0) {
|
||
|
|
md = EVP_sha224();
|
||
|
|
}
|
||
|
|
else if (strcmp (MESSAGE_DIGEST_SHA256, digestName) == 0) {
|
||
|
|
md = EVP_sha256();
|
||
|
|
}
|
||
|
|
else if (strcmp (MESSAGE_DIGEST_SHA384, digestName) == 0) {
|
||
|
|
md = EVP_sha384();
|
||
|
|
}
|
||
|
|
else if (strcmp (MESSAGE_DIGEST_SHA512, digestName) == 0) {
|
||
|
|
md = EVP_sha512();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!md) {
|
||
|
|
IONUERROR ("Cannot get digest with name %s", digestName);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
return md;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get a pointer to the function that implements the named cipher
|
||
|
|
const EVP_CIPHER* sequencelogic::GetCipher (const char *cipherName) {
|
||
|
|
const EVP_CIPHER* cipher = EVP_get_cipherbyname(cipherName);
|
||
|
|
if (!cipher) {
|
||
|
|
// In case init has not been called
|
||
|
|
if (strcmp ("aes128cbc", cipherName) == 0) {
|
||
|
|
cipher = EVP_aes_128_cbc();
|
||
|
|
}
|
||
|
|
else if (strcmp ("aes256cbc", cipherName) == 0) {
|
||
|
|
cipher = EVP_aes_256_cbc();
|
||
|
|
}
|
||
|
|
#ifndef ANDROID
|
||
|
|
else if (strcmp ("aes256gcm", cipherName) == 0) {
|
||
|
|
cipher = EVP_aes_256_gcm();
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
if (!cipher) {
|
||
|
|
IONUERROR ("Cannot get cipher with name %s", cipherName);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
return cipher;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generic message digest function (in memory), works with any supported algorithm
|
||
|
|
* @param digest - name of the message digest algorithm (e.g. md5, sha1, sha256...)
|
||
|
|
* @param msg - message to digest
|
||
|
|
* @param len - bytes in message
|
||
|
|
* @returns - hex string containing digest
|
||
|
|
*/
|
||
|
|
std::string sequencelogic::DigestMessage (const char* digest, const unsigned char* msg, size_t len)
|
||
|
|
{
|
||
|
|
std::string hex = "";
|
||
|
|
// Check for garbage input
|
||
|
|
if (!digest || !msg || len == 0)
|
||
|
|
return hex;
|
||
|
|
|
||
|
|
unsigned char md_value[EVP_MAX_MD_SIZE];
|
||
|
|
unsigned int md_len;
|
||
|
|
EVP_MD_CTX *mdctx;
|
||
|
|
const EVP_MD *md;
|
||
|
|
|
||
|
|
md = sequencelogic::GetMessageDigester (digest);
|
||
|
|
mdctx = EVP_MD_CTX_create();
|
||
|
|
EVP_DigestInit_ex (mdctx, md, NULL);
|
||
|
|
EVP_DigestUpdate (mdctx, msg, len);
|
||
|
|
EVP_DigestFinal_ex (mdctx, md_value, &md_len);
|
||
|
|
EVP_MD_CTX_destroy (mdctx);
|
||
|
|
|
||
|
|
char hexd[EVP_MAX_MD_SIZE * 2 + 1];
|
||
|
|
BinaryToHex (md_value, md_len, hexd);
|
||
|
|
hex = hexd;
|
||
|
|
return hex;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generic message digest function (file), works with any supported algorithm
|
||
|
|
* @param digest - name of the message digest algorithm (e.g. md5, sha1, sha256...)
|
||
|
|
* @param filename - readable and non-zero length file to digest
|
||
|
|
* @returns - hex string containing digest
|
||
|
|
*/
|
||
|
|
std::string sequencelogic::DigestFile (const char* digest, const std::string& filename)
|
||
|
|
{
|
||
|
|
std::string hex = "";
|
||
|
|
|
||
|
|
// Check for garbage input
|
||
|
|
if (!digest || !sequencelogic::IsValidFilename (filename))
|
||
|
|
return hex;
|
||
|
|
|
||
|
|
unsigned char md_value[EVP_MAX_MD_SIZE];
|
||
|
|
unsigned int md_len;
|
||
|
|
EVP_MD_CTX* mdctx;
|
||
|
|
const EVP_MD* md;
|
||
|
|
size_t bytes;
|
||
|
|
|
||
|
|
md = GetMessageDigester (digest);
|
||
|
|
ifstream file (filename, ios::in | ios::binary | ios::ate);
|
||
|
|
if (file.is_open())
|
||
|
|
{
|
||
|
|
// Get the file size and reset to the beginning
|
||
|
|
bytes = (size_t)file.tellg();
|
||
|
|
file.seekg (0, ios::beg);
|
||
|
|
if (bytes == 0)
|
||
|
|
return hex;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
return hex;
|
||
|
|
}
|
||
|
|
mdctx = EVP_MD_CTX_create();
|
||
|
|
EVP_DigestInit_ex (mdctx, md, NULL);
|
||
|
|
|
||
|
|
// Process the file in chunks
|
||
|
|
char* data = new char[SL_CHUNK_SIZE];
|
||
|
|
size_t len = (bytes > SL_CHUNK_SIZE ? SL_CHUNK_SIZE : bytes);
|
||
|
|
do {
|
||
|
|
file.read (data, len);
|
||
|
|
if (file.fail()) {
|
||
|
|
//SL_SEC_LOG (SL_WARN, "read failed: %d", errno);
|
||
|
|
delete[] data;
|
||
|
|
return hex;
|
||
|
|
} else {
|
||
|
|
EVP_DigestUpdate (mdctx, data, len);
|
||
|
|
bytes -= len;
|
||
|
|
len = (bytes > SL_CHUNK_SIZE ? SL_CHUNK_SIZE : bytes);
|
||
|
|
}
|
||
|
|
} while (bytes > 0);
|
||
|
|
|
||
|
|
delete[] data;
|
||
|
|
file.close();
|
||
|
|
|
||
|
|
// Finalize the message digest, convert to hex and return
|
||
|
|
EVP_DigestFinal_ex (mdctx, md_value, &md_len);
|
||
|
|
EVP_MD_CTX_destroy (mdctx);
|
||
|
|
|
||
|
|
char hexd[EVP_MAX_MD_SIZE * 2 + 1];
|
||
|
|
BinaryToHex (md_value, md_len, hexd);
|
||
|
|
hex = hexd;
|
||
|
|
return hex;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Compute the HMAC (message authentication code) using keyed SHA1 hash
|
||
|
|
* @param key - key
|
||
|
|
* @param klen - bytes of key
|
||
|
|
* @param msg - message to digest
|
||
|
|
* @param mlen - bytes in message
|
||
|
|
* @returns - hex string containing HMAC
|
||
|
|
*/
|
||
|
|
std::string sequencelogic::HMACMessage (const unsigned char* key, size_t klen, const unsigned char* msg, size_t mlen)
|
||
|
|
{
|
||
|
|
std::string hex = "";
|
||
|
|
|
||
|
|
// Check for garbage input
|
||
|
|
if (!key || !msg || klen == 0 || mlen == 0)
|
||
|
|
return hex;
|
||
|
|
|
||
|
|
unsigned char md_value[EVP_MAX_MD_SIZE];
|
||
|
|
unsigned int md_len;
|
||
|
|
HMAC_CTX hmctx;
|
||
|
|
|
||
|
|
HMAC_CTX_init (&hmctx);
|
||
|
|
HMAC_Init (&hmctx, key, static_cast<int>(klen), EVP_sha1());
|
||
|
|
HMAC_Update (&hmctx, msg, mlen);
|
||
|
|
HMAC_Final (&hmctx, md_value, &md_len);
|
||
|
|
HMAC_CTX_cleanup (&hmctx);
|
||
|
|
HMAC_cleanup (&hmctx);
|
||
|
|
|
||
|
|
char hexd[EVP_MAX_MD_SIZE * 2 + 1];
|
||
|
|
BinaryToHex (md_value, md_len, hexd);
|
||
|
|
hex = hexd;
|
||
|
|
return hex;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get the number of rounds used in key generation, more for shorter passwords
|
||
|
|
// Number of rounds is 1000 - 100,000
|
||
|
|
int sequencelogic::GetRounds (const char* password)
|
||
|
|
{
|
||
|
|
if (password)
|
||
|
|
return 2500; // This is the max we can expect from the JavaScript implementation ~20 seconds
|
||
|
|
else
|
||
|
|
return 0;
|
||
|
|
#ifdef LATER
|
||
|
|
double strength = PassWordStrength (password);
|
||
|
|
int rounds = 10000;
|
||
|
|
if (strength > 0.1)
|
||
|
|
rounds = (int) (50.0 * ((8.0 / strength) * (8.0 / strength)));
|
||
|
|
return rounds;
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
// Algorithm to compute a relative password strength
|
||
|
|
// @param clearPassword
|
||
|
|
// @return 0.0 - 1.0 with 1.0 being the strongest password
|
||
|
|
double sequencelogic::PassWordStrength (const char* password)
|
||
|
|
{
|
||
|
|
// Check for garbage input
|
||
|
|
if (sequencelogic::StrLen (password, SL_PASSWORD_MAX_LEN) == 0)
|
||
|
|
return 0.0;
|
||
|
|
|
||
|
|
// Check for default and the worst passwords
|
||
|
|
if (sequencelogic::StrnCmp (password, "password", 8) == 0 ||
|
||
|
|
sequencelogic::StrnCmp (password, "admin", 5) == 0 ||
|
||
|
|
sequencelogic::StrnCmp (password, "qwerty", 6) == 0)
|
||
|
|
return 0.0;
|
||
|
|
|
||
|
|
char c, pc = '\0', apc = '\0';
|
||
|
|
unsigned int upper, lower, digit, special, alphaSeq, digitSeq, numericSeq, specialSeq;
|
||
|
|
upper = lower = digit = special = alphaSeq = digitSeq = numericSeq = specialSeq = 0;
|
||
|
|
size_t len = sequencelogic::StrLen (password, 64); // Limit to 64 characters
|
||
|
|
for (size_t i = 0; i < len; ++i) {
|
||
|
|
c = password[i];
|
||
|
|
if (islower (c)) {
|
||
|
|
lower++;
|
||
|
|
if (c == pc || c == apc) alphaSeq++;
|
||
|
|
pc = c;
|
||
|
|
if (c == 'l') apc = '1';
|
||
|
|
else if (c == 'o') apc = '0';
|
||
|
|
else if (c == 'a') apc = '@';
|
||
|
|
}
|
||
|
|
else if (isupper (c)) {
|
||
|
|
if (i != 0) upper++; // Don't count when first character
|
||
|
|
if (c == pc || c == apc) alphaSeq++;
|
||
|
|
pc = (char)tolower (c);
|
||
|
|
if (c == 'I') apc = '1';
|
||
|
|
else if (c == 'O') apc = '0';
|
||
|
|
else if (c == 'A') apc = '@';
|
||
|
|
}
|
||
|
|
else if (isdigit (c)) {
|
||
|
|
digit++;
|
||
|
|
if (c == pc || c == apc) numericSeq++;
|
||
|
|
else if (c + 1 == pc || c - 1 == pc) numericSeq++;
|
||
|
|
else if (c - 1 == apc) numericSeq++;
|
||
|
|
if (isdigit (pc)) digitSeq++;
|
||
|
|
pc = c;
|
||
|
|
if (c == '0') apc = 'o';
|
||
|
|
else if (c == '3') apc = 'e';
|
||
|
|
else if (c == '1') apc = 'l';
|
||
|
|
else apc = c;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
if (i != len ) special++; // Don't count when last character
|
||
|
|
if (c == pc) specialSeq++;
|
||
|
|
pc = apc = c;
|
||
|
|
if (c == '@') apc = 'A';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//printf ("len=%d upper=%d lower=%d dig=%d spcl=%d alphaSeq=%d numSeq=%d spclSeq=%d\n",
|
||
|
|
// len, upper, lower, digit, special, alphaSeq, numericSeq, specialSeq);
|
||
|
|
|
||
|
|
double strength = 0.0;
|
||
|
|
if (digit >= 3 && digitSeq == digit - 1) len -= digitSeq;
|
||
|
|
if (len >= 8 ) strength += (len > 12 ? 100.0 : len * 8.0);
|
||
|
|
else strength += 35.0 - ((7 - len) * (7 - len));
|
||
|
|
if (lower && upper && digit && special) strength += 50.0;
|
||
|
|
strength -= (alphaSeq + numericSeq + specialSeq) * 5.0;
|
||
|
|
if (strength < 0.0 ) strength = 0.0;
|
||
|
|
else if (strength > 100.0) strength = 1.0;
|
||
|
|
else strength /= 100.0;
|
||
|
|
return strength;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Derive a key from the user password, can be updated to use bcrypt, scrypt or other in time
|
||
|
|
// PBKDF2 (Password-Based Key Derivation Function 2) - also known as PKCS #5 v2.0 or RFC 2898
|
||
|
|
// Accepts MCF format $pbkdf2$2500$salt$keydata or base64 encoded raw keydata or "" for initial
|
||
|
|
// Also Accepts $pbkdf2$10000$$ to create key with specific number of rounds and random salt
|
||
|
|
// Returns "" for failure, valid MCF for validation (or creation)
|
||
|
|
std::string sequencelogic::SlowHash (const std::string& password, const std::string& value)
|
||
|
|
{
|
||
|
|
std::string hash = "";
|
||
|
|
|
||
|
|
// Check for garbage input
|
||
|
|
if (password.size() == 0)
|
||
|
|
return hash;
|
||
|
|
|
||
|
|
int rounds = 0;
|
||
|
|
int saltSize = SL_AES_BLOCK_LEN;
|
||
|
|
unsigned char salt[32];
|
||
|
|
char b64salt[64];
|
||
|
|
unsigned char skey[SL_AES_KEY_LEN];
|
||
|
|
char b64[128];
|
||
|
|
stringstream s;
|
||
|
|
|
||
|
|
// Check if validation value exists in MCF, parse rounds and salt, validate password
|
||
|
|
if (value.size() > 0 && value[0] == '$') {
|
||
|
|
std::vector<std::string> params = sequencelogic::SplitString (value, '$');
|
||
|
|
if (params.size() >= 4) {
|
||
|
|
if (params[1].compare ("pbkdf2") != 0) {
|
||
|
|
IONUDEBUG ("sequencelogic::SlowHash() - unknown algorithm identifier %s", params[1].c_str());
|
||
|
|
return hash;
|
||
|
|
}
|
||
|
|
rounds = atoi (params[2].c_str());
|
||
|
|
if (rounds < 2500) {
|
||
|
|
IONUDEBUG ("sequencelogic::SlowHash() - number of rounds must be at least 2500 not %s", params[2].c_str());
|
||
|
|
return hash;
|
||
|
|
}
|
||
|
|
// No salt specified, get random bytes
|
||
|
|
if (params[3].size() == 0) {
|
||
|
|
if (RAND_bytes (salt, SL_AES_BLOCK_LEN) != 1)
|
||
|
|
return hash;
|
||
|
|
sequencelogic::Base64Encode (salt, SL_AES_BLOCK_LEN, b64salt);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
saltSize = (int)sequencelogic::Base64Decode (params[3].c_str(), salt);
|
||
|
|
|
||
|
|
// Derive the key, validate or create MCF
|
||
|
|
if (PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), (int)password.size(), salt, saltSize, rounds, SL_AES_KEY_LEN, skey) == 1 ) {
|
||
|
|
sequencelogic::Base64Encode (skey, SL_AES_KEY_LEN, b64);
|
||
|
|
// Not salt or key passed in so create new MCF
|
||
|
|
if (params[3].size() == 0 || params[4].size() == 0) {
|
||
|
|
s << "$pbkdf2$" << params[2] << "$" << b64salt << "$" << b64;
|
||
|
|
return s.str();
|
||
|
|
}
|
||
|
|
else if (params[4].compare (b64) != 0)
|
||
|
|
return hash;
|
||
|
|
else
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
return hash;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
return hash;
|
||
|
|
}
|
||
|
|
// Old style raw key data, 2500 rounds, fixed salt, validate password
|
||
|
|
else if (value.size() > 0 && value[0] != '$') {
|
||
|
|
unsigned char salt[8];
|
||
|
|
sequencelogic::HexToBinary (SL_KEY_SALT, salt);
|
||
|
|
if (PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), (int)password.size(), salt, SL_KEY_SALT_LEN, 2500, SL_AES_KEY_LEN, skey) == 1 ) {
|
||
|
|
sequencelogic::Base64Encode (skey, SL_AES_KEY_LEN, b64);
|
||
|
|
// Password does not match
|
||
|
|
if (value.compare (b64) != 0)
|
||
|
|
return hash;
|
||
|
|
else {
|
||
|
|
s << "$pbkdf2$" << 2500 << "$" << SL_KEY_SALT << "$" << b64 << "$";
|
||
|
|
return s.str();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
return hash;
|
||
|
|
}
|
||
|
|
// New or updated value
|
||
|
|
if (rounds != GetRounds (password.c_str())) {
|
||
|
|
rounds = GetRounds (password.c_str());
|
||
|
|
if (RAND_bytes (salt, SL_AES_BLOCK_LEN) != 1)
|
||
|
|
return hash;
|
||
|
|
if (PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), (int)password.size(), salt, saltSize, rounds, SL_AES_KEY_LEN, skey) == 1 ) {
|
||
|
|
sequencelogic::Base64Encode (salt, SL_AES_BLOCK_LEN, b64);
|
||
|
|
s << "$pbkdf2$" << rounds << "$" << b64 << "$";
|
||
|
|
sequencelogic::Base64Encode (skey, SL_AES_KEY_LEN, b64);
|
||
|
|
s << b64;
|
||
|
|
return s.str();
|
||
|
|
}
|
||
|
|
return hash;
|
||
|
|
}
|
||
|
|
// Validated password, no changes to rounds or algorithm
|
||
|
|
else
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Derive a key from the user password, can be updated to use bcrypt, scrypt or other in time
|
||
|
|
// PBKDF2 (Password-Based Key Derivation Function 2) - also known as PKCS #5 v2.0 or RFC 2898
|
||
|
|
bool sequencelogic::DeriveKey (const char* password, unsigned char* key)
|
||
|
|
{
|
||
|
|
// Check for garbage input
|
||
|
|
int len = (int)sequencelogic::StrLen (password, SL_PASSWORD_MAX_LEN);
|
||
|
|
if (len == 0 || !key)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
int rounds = GetRounds (password);
|
||
|
|
unsigned char salt[8];
|
||
|
|
sequencelogic::HexToBinary (SL_KEY_SALT, salt);
|
||
|
|
|
||
|
|
#ifdef TIME_DEBUG
|
||
|
|
long ms; // System time in milliseconds
|
||
|
|
ms = sequencelogic::GetSystemTime();
|
||
|
|
#endif
|
||
|
|
if (PKCS5_PBKDF2_HMAC_SHA1(password, len, salt, SL_KEY_SALT_LEN, rounds, SL_AES_KEY_LEN, key) == 0 ) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
#ifdef TIME_DEBUG
|
||
|
|
ms = sequencelogic::GetSystemTime() - ms;
|
||
|
|
if (ms < 1000) {
|
||
|
|
unsigned char waste[SL_AES_KEY_LEN];
|
||
|
|
rounds = (1000 - ms) * (rounds / ms);
|
||
|
|
PKCS5_PBKDF2_HMAC_SHA1(password, len, salt, SL_KEY_SALT_LEN, rounds, SL_AES_KEY_LEN, waste);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
RAND_add (key, SL_AES_KEY_LEN, 16.0);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// CloudGaurd key has two parts, a 32 byte half key and the 32 byte PBKDF2 key
|
||
|
|
// This method generates these values
|
||
|
|
bool sequencelogic::GenerateCGKey (const char* password, unsigned char* key, unsigned char* halfkey)
|
||
|
|
{
|
||
|
|
// Check for garbage input
|
||
|
|
int len = (int)sequencelogic::StrLen (password, SL_PASSWORD_MAX_LEN);
|
||
|
|
if (len == 0 || !key || !halfkey)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
int rounds = GetRounds (password);
|
||
|
|
if (RAND_bytes (halfkey, SL_AES_KEY_LEN) != 1)
|
||
|
|
return false;
|
||
|
|
if (PKCS5_PBKDF2_HMAC_SHA1(password, len, halfkey, SL_AES_KEY_LEN, rounds, SL_AES_KEY_LEN, key) == 0 ) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Validate that the user password and half key match match
|
||
|
|
bool sequencelogic::ValidateCGKey (const char* password, const unsigned char* key, const unsigned char* halfkey)
|
||
|
|
{
|
||
|
|
// Check for garbage input
|
||
|
|
int len = (int)sequencelogic::StrLen (password, SL_PASSWORD_MAX_LEN);
|
||
|
|
if (len == 0 || !key || !halfkey)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
int rounds = GetRounds (password);
|
||
|
|
unsigned char vkey[SL_AES_KEY_LEN];
|
||
|
|
|
||
|
|
if (PKCS5_PBKDF2_HMAC_SHA1(password, len, halfkey, SL_AES_KEY_LEN, rounds, SL_AES_KEY_LEN, vkey) == 0 ) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return (memcmp (key, vkey, SL_AES_KEY_LEN) == 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Generate the user key from the password and cg half key
|
||
|
|
bool sequencelogic::DeriveUserKey (const char* password, unsigned char* userkey, const unsigned char* halfkey)
|
||
|
|
{
|
||
|
|
// Check for garbage input
|
||
|
|
int len = (int)sequencelogic::StrLen (password, SL_PASSWORD_MAX_LEN);
|
||
|
|
if (len == 0 || !userkey || !halfkey)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
std::string hex = sequencelogic::DigestMessage (MESSAGE_DIGEST_SHA256, (unsigned char*)password, len);
|
||
|
|
if (hex.size() != 2 * SL_AES_KEY_LEN) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
sequencelogic::HexToBinary (hex.c_str(), userkey);
|
||
|
|
sequencelogic::XorKeys (userkey, halfkey, userkey, SL_AES_KEY_LEN);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool sequencelogic::RandomBytes (size_t bytes, unsigned char* buffer)
|
||
|
|
{
|
||
|
|
if (RAND_bytes(buffer, (int)bytes))
|
||
|
|
return true;
|
||
|
|
else
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string sequencelogic::RandomBase32 (size_t length)
|
||
|
|
{
|
||
|
|
std::string rb ("");
|
||
|
|
if (length > 1 && length < 256) {
|
||
|
|
char b32[256] = {};
|
||
|
|
unsigned char buffer[256];
|
||
|
|
size_t bytes = ((length+3) * 5) / 8;
|
||
|
|
if (RAND_bytes(buffer, (int)bytes)) {
|
||
|
|
sequencelogic::Base32Encode (buffer, bytes, b32);
|
||
|
|
b32[length] = '\0';
|
||
|
|
rb = b32;
|
||
|
|
return rb;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
IONUDEBUG ("sequencelogic::RandomBase32() - failed for %d length", (int)length);
|
||
|
|
return rb;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string sequencelogic::RandomBase64 (size_t length)
|
||
|
|
{
|
||
|
|
std::string rb ("");
|
||
|
|
if (length > 1 && length < 256) {
|
||
|
|
char b64[256] = {};
|
||
|
|
unsigned char buffer[256];
|
||
|
|
size_t bytes = ((length+1) * 3) / 4;
|
||
|
|
if (RAND_bytes(buffer, (int)bytes)) {
|
||
|
|
sequencelogic::Base64Encode (buffer, bytes, b64);
|
||
|
|
b64[length] = '\0';
|
||
|
|
rb = b64;
|
||
|
|
return rb;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
IONUDEBUG ("sequencelogic::RandomBase64() - failed for %d length", (int)length);
|
||
|
|
return rb;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Default is the largest set of ascii characters that can be used
|
||
|
|
static const char* passwordset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890`~!@#$%^&*()-_=+[]{}\\|;:'\",.<>/?";
|
||
|
|
|
||
|
|
std::string sequencelogic::RandomPassWord (size_t len, const char* charset)
|
||
|
|
{
|
||
|
|
if (! charset) charset = passwordset;
|
||
|
|
size_t setlen = sequencelogic::StrLen (charset, 96);
|
||
|
|
std::string password = "";
|
||
|
|
password.reserve (len);
|
||
|
|
if (setlen >= 36) { // Must be at least base36 character set
|
||
|
|
while (len > 0) {
|
||
|
|
unsigned char c;
|
||
|
|
RAND_bytes (&c, 1);
|
||
|
|
size_t index = (size_t)c;
|
||
|
|
if (index < setlen) {
|
||
|
|
password += (charset[index]);
|
||
|
|
len--;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return password;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
void sequencelogic::DumpAsBinary (unsigned char* data, size_t bytes)
|
||
|
|
{
|
||
|
|
char* buff = new char[(bytes * 8) + 24];
|
||
|
|
char* bin = buff;
|
||
|
|
for (size_t i = 0; i < bytes; ++i) {
|
||
|
|
unsigned char b=0x80;
|
||
|
|
for (int bit = 0; bit < 8; ++bit) {
|
||
|
|
*bin++ = (data[i] & b) ? '1' : '0';
|
||
|
|
b = b >> 1;
|
||
|
|
}
|
||
|
|
*bin++ = ' ';
|
||
|
|
}
|
||
|
|
*bin++ = '\0';
|
||
|
|
printf ("%s\n", buff);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get the last system call error formatted as a string;
|
||
|
|
std::string sequencelogic::GetLastErrorMessage()
|
||
|
|
{
|
||
|
|
std::string retVal;
|
||
|
|
#ifdef WIN32
|
||
|
|
DWORD nErr = GetLastError();
|
||
|
|
retVal = GetErrorMessage(nErr);
|
||
|
|
#endif
|
||
|
|
return retVal;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string sequencelogic::GetErrorMessage(int nErrorCode)
|
||
|
|
{
|
||
|
|
std::string errmsg = "";
|
||
|
|
#ifdef WIN32
|
||
|
|
LPVOID lpMsgBuf;
|
||
|
|
|
||
|
|
FormatMessage(
|
||
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
||
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||
|
|
NULL,
|
||
|
|
nErrorCode,
|
||
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||
|
|
(LPTSTR) &lpMsgBuf,
|
||
|
|
0, NULL );
|
||
|
|
|
||
|
|
errmsg.assign ((LPTSTR)lpMsgBuf, lstrlen((LPCTSTR)lpMsgBuf));
|
||
|
|
LocalFree(lpMsgBuf);
|
||
|
|
#else
|
||
|
|
errmsg = strerror (errno);
|
||
|
|
#endif
|
||
|
|
return errmsg;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Convert from std::string to std::wstring
|
||
|
|
*/
|
||
|
|
std::wstring sequencelogic::toWString(const std::string &str, const std::locale &loc /*= std::locale()*/)
|
||
|
|
{
|
||
|
|
if (str.size() == 0)
|
||
|
|
return std::wstring();
|
||
|
|
|
||
|
|
std::vector<wchar_t> tmpBuf(str.size());
|
||
|
|
std::use_facet<std::ctype<wchar_t> >(loc).widen(str.data(), str.data()+str.size(), tmpBuf.data());
|
||
|
|
return std::wstring(tmpBuf.data(), tmpBuf.size());
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Convert from std::wstring to std::string
|
||
|
|
*/
|
||
|
|
std::string sequencelogic::toAString(const std::wstring &wstr, const std::locale &loc /*= std::locale()*/)
|
||
|
|
{
|
||
|
|
if (wstr.size() == 0)
|
||
|
|
return std::string();
|
||
|
|
|
||
|
|
std::vector<char> tmpBuf(wstr.size());
|
||
|
|
std::use_facet<std::ctype<wchar_t> >(loc).narrow(wstr.data(), wstr.data()+wstr.size(), '?', tmpBuf.data());
|
||
|
|
return std::string(tmpBuf.data(), tmpBuf.size());
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef WIN32
|
||
|
|
#define MAX_NAME 256
|
||
|
|
bool sequencelogic::GetLogonFromToken(HANDLE hToken, LPSTR lpName, LPSTR lpDomain)
|
||
|
|
{
|
||
|
|
DWORD dwSize = MAX_NAME;
|
||
|
|
bool bSuccess = false;
|
||
|
|
DWORD dwLength = 0;
|
||
|
|
PTOKEN_USER ptu = NULL;
|
||
|
|
|
||
|
|
if (!GetTokenInformation(hToken, // handle to the access token
|
||
|
|
TokenUser, // get information about the token's groups
|
||
|
|
(LPVOID) ptu, // pointer to PTOKEN_USER buffer
|
||
|
|
0, // size of buffer
|
||
|
|
&dwLength)) { // receives required buffer size
|
||
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
ptu = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
|
||
|
|
if (ptu == NULL)
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (GetTokenInformation(hToken, // handle to the access token
|
||
|
|
TokenUser, // get information about the token's groups
|
||
|
|
(LPVOID) ptu, // pointer to PTOKEN_USER buffer
|
||
|
|
dwLength, // size of buffer
|
||
|
|
&dwLength)) { // receives required buffer size
|
||
|
|
|
||
|
|
SID_NAME_USE SidType;
|
||
|
|
|
||
|
|
if( !LookupAccountSid (NULL, ptu->User.Sid, lpName, &dwSize, lpDomain, &dwSize, &SidType)) {
|
||
|
|
DWORD dwResult = GetLastError();
|
||
|
|
IONUDEBUG ("LookupAccountSid Error %u\n", GetLastError());
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
bSuccess = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (ptu != NULL)
|
||
|
|
HeapFree (GetProcessHeap(), 0, (LPVOID)ptu);
|
||
|
|
return bSuccess;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string sequencelogic::GetProcessOwner (DWORD processId)
|
||
|
|
{
|
||
|
|
std::string owner = "";
|
||
|
|
|
||
|
|
HANDLE hProcess = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, processId);
|
||
|
|
if(hProcess == NULL)
|
||
|
|
return owner;
|
||
|
|
HANDLE hToken = NULL;
|
||
|
|
|
||
|
|
if( !OpenProcessToken (hProcess, TOKEN_QUERY, &hToken))
|
||
|
|
{
|
||
|
|
CloseHandle (hProcess);
|
||
|
|
return owner;
|
||
|
|
}
|
||
|
|
char lpName[MAX_PATH] = "";
|
||
|
|
char lpDomain[MAX_NAME] = "";
|
||
|
|
if (GetLogonFromToken (hToken, lpName, lpDomain)) {
|
||
|
|
owner = lpName;
|
||
|
|
//IONUDEBUG ("Current user is %s\\%s\n", lpDomain, lpName);
|
||
|
|
}
|
||
|
|
|
||
|
|
CloseHandle (hToken);
|
||
|
|
CloseHandle (hProcess);
|
||
|
|
return owner;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string sequencelogic::GetProcessName (DWORD processId)
|
||
|
|
{
|
||
|
|
std::string process = "";
|
||
|
|
TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");
|
||
|
|
|
||
|
|
// Get a handle to the process.
|
||
|
|
HANDLE hProcess = OpenProcess (PROCESS_QUERY_INFORMATION |
|
||
|
|
PROCESS_VM_READ,
|
||
|
|
FALSE, processId );
|
||
|
|
|
||
|
|
// Get the process name.
|
||
|
|
if (NULL != hProcess ) {
|
||
|
|
HMODULE hMod;
|
||
|
|
DWORD cbNeeded;
|
||
|
|
if (EnumProcessModules (hProcess, &hMod, sizeof(hMod), &cbNeeded)) {
|
||
|
|
GetModuleBaseName (hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(TCHAR));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Release the handle to the process.
|
||
|
|
CloseHandle( hProcess );
|
||
|
|
|
||
|
|
process = szProcessName;
|
||
|
|
return process;
|
||
|
|
}
|
||
|
|
|
||
|
|
#else
|
||
|
|
std::string sequencelogic::GetProcessOwner (pid_t processId)
|
||
|
|
{
|
||
|
|
std::string owner = "";
|
||
|
|
#ifdef LIN64
|
||
|
|
uid_t uid = getuid();
|
||
|
|
struct passwd* pass = getpwuid (uid);
|
||
|
|
if (pass)
|
||
|
|
owner = pass->pw_name;
|
||
|
|
#endif
|
||
|
|
return owner;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string sequencelogic::GetProcessName (pid_t processId)
|
||
|
|
{
|
||
|
|
std::string process = "";
|
||
|
|
#ifdef LIN64
|
||
|
|
char name[1024];
|
||
|
|
size_t start = 0;
|
||
|
|
sprintf (name, "/proc/%d/cmdline", processId);
|
||
|
|
FILE* f = fopen (name,"r");
|
||
|
|
if (f) {
|
||
|
|
size_t size;
|
||
|
|
size = fread (name, sizeof(char), 1024, f);
|
||
|
|
if (size > 0) {
|
||
|
|
for (size_t i = 0; i < size; ++i) {
|
||
|
|
if (name[i] == ' ' || name[i] == '\n') {
|
||
|
|
name[i] = '\0';
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
else if (name[i] == '/')
|
||
|
|
start = i + 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
fclose (f);
|
||
|
|
process = &name[start];
|
||
|
|
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
return process;
|
||
|
|
}
|
||
|
|
#endif
|