309 lines
8.7 KiB
C++
309 lines
8.7 KiB
C++
#include "flaim.h"
|
|
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#define PERSON_TAG 1
|
|
#define LAST_NAME_TAG 2
|
|
#define FIRST_NAME_TAG 3
|
|
#define SECRET_TAG 4
|
|
#define AGE_TAG 5
|
|
#define SECRET_ENCDEF 10
|
|
|
|
static const char *kDictionary =
|
|
"0 @1@ field Person\n"
|
|
" 1 type text\n"
|
|
"0 @2@ field LastName\n"
|
|
" 1 type text\n"
|
|
"0 @3@ field FirstName\n"
|
|
" 1 type text\n"
|
|
"0 @4@ field Secret\n"
|
|
" 1 type text\n"
|
|
"0 @5@ field Age\n"
|
|
" 1 type number\n"
|
|
"0 @100@ index LastFirst_IX\n"
|
|
" 1 language US\n"
|
|
" 1 key\n"
|
|
" 2 field 2\n"
|
|
" 3 required\n"
|
|
" 2 field 3\n"
|
|
" 3 required\n";
|
|
|
|
static const char *kSecret = "mars-nwe-flaim-at-rest-secret-value";
|
|
|
|
static void fail(const char *what, RCODE rc)
|
|
{
|
|
if (rc)
|
|
{
|
|
fprintf(stderr, "FAIL: %s: 0x%04x %s\n", what, (unsigned)rc,
|
|
(const char *)FlmErrorString(rc));
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "FAIL: %s\n", what);
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
static void ensure_dir(const char *path)
|
|
{
|
|
if (mkdir(path, 0700) != 0 && errno != EEXIST)
|
|
{
|
|
perror(path);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void rm_rf(const char *path)
|
|
{
|
|
DIR *dir = opendir(path);
|
|
if (!dir)
|
|
{
|
|
unlink(path);
|
|
return;
|
|
}
|
|
|
|
struct dirent *ent;
|
|
while ((ent = readdir(dir)) != NULL)
|
|
{
|
|
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
|
|
{
|
|
continue;
|
|
}
|
|
char child[4096];
|
|
snprintf(child, sizeof(child), "%s/%s", path, ent->d_name);
|
|
rm_rf(child);
|
|
}
|
|
closedir(dir);
|
|
rmdir(path);
|
|
}
|
|
|
|
static int file_contains(const char *path, const char *needle)
|
|
{
|
|
FILE *f = fopen(path, "rb");
|
|
if (!f)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const size_t nlen = strlen(needle);
|
|
unsigned char buf[8192];
|
|
size_t overlap = 0;
|
|
int found = 0;
|
|
|
|
while (!found)
|
|
{
|
|
size_t got = fread(buf + overlap, 1, sizeof(buf) - overlap, f);
|
|
if (got == 0)
|
|
{
|
|
break;
|
|
}
|
|
size_t total = overlap + got;
|
|
for (size_t i = 0; i + nlen <= total; ++i)
|
|
{
|
|
if (memcmp(buf + i, needle, nlen) == 0)
|
|
{
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (nlen > 1 && total >= nlen - 1)
|
|
{
|
|
overlap = nlen - 1;
|
|
memmove(buf, buf + total - overlap, overlap);
|
|
}
|
|
else
|
|
{
|
|
overlap = total;
|
|
memmove(buf, buf, overlap);
|
|
}
|
|
}
|
|
|
|
fclose(f);
|
|
return found;
|
|
}
|
|
|
|
static void assert_secret_not_plaintext(const char *dir)
|
|
{
|
|
DIR *d = opendir(dir);
|
|
if (!d)
|
|
{
|
|
perror(dir);
|
|
exit(1);
|
|
}
|
|
|
|
struct dirent *ent;
|
|
while ((ent = readdir(d)) != NULL)
|
|
{
|
|
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
|
|
{
|
|
continue;
|
|
}
|
|
char path[4096];
|
|
snprintf(path, sizeof(path), "%s/%s", dir, ent->d_name);
|
|
struct stat st;
|
|
if (stat(path, &st) != 0)
|
|
{
|
|
continue;
|
|
}
|
|
if (S_ISDIR(st.st_mode))
|
|
{
|
|
assert_secret_not_plaintext(path);
|
|
}
|
|
else if (S_ISREG(st.st_mode) && file_contains(path, kSecret))
|
|
{
|
|
fprintf(stderr, "FAIL: encrypted FLAIM payload appears as plaintext in %s\n", path);
|
|
exit(1);
|
|
}
|
|
}
|
|
closedir(d);
|
|
}
|
|
|
|
static void add_encryption_definition(HFDB hDb)
|
|
{
|
|
FlmRecord *rec = NULL;
|
|
void *field = NULL;
|
|
RCODE rc;
|
|
|
|
rec = f_new FlmRecord;
|
|
if (!rec)
|
|
{
|
|
fail("alloc EncDef FlmRecord", FERR_MEM);
|
|
}
|
|
|
|
if (RC_BAD(rc = rec->insertLast(0, FLM_ENCDEF_TAG, FLM_TEXT_TYPE, &field))) fail("insert EncDef root", rc);
|
|
if (RC_BAD(rc = rec->setNative(field, "SecretKey"))) fail("set EncDef name", rc);
|
|
if (RC_BAD(rc = rec->insertLast(1, FLM_TYPE_TAG, FLM_TEXT_TYPE, &field))) fail("insert EncDef type", rc);
|
|
if (RC_BAD(rc = rec->setNative(field, "aes"))) fail("set EncDef type", rc);
|
|
|
|
FLMUINT drn = SECRET_ENCDEF;
|
|
if (RC_BAD(rc = FlmRecordAdd(hDb, FLM_DICT_CONTAINER, &drn, rec, 0))) fail("FlmRecordAdd EncDef", rc);
|
|
if (drn != SECRET_ENCDEF) fail("EncDef DRN mismatch", 0);
|
|
rec->Release();
|
|
}
|
|
|
|
static void add_person(HFDB hDb, FLMUINT *drn_out)
|
|
{
|
|
FlmRecord *rec = NULL;
|
|
void *field = NULL;
|
|
RCODE rc;
|
|
|
|
rec = f_new FlmRecord;
|
|
if (!rec)
|
|
{
|
|
fail("alloc FlmRecord", FERR_MEM);
|
|
}
|
|
|
|
if (RC_BAD(rc = rec->insertLast(0, PERSON_TAG, FLM_TEXT_TYPE, NULL))) fail("insert root", rc);
|
|
if (RC_BAD(rc = rec->insertLast(1, FIRST_NAME_TAG, FLM_TEXT_TYPE, &field))) fail("insert first", rc);
|
|
if (RC_BAD(rc = rec->setNative(field, "Mars"))) fail("set first", rc);
|
|
if (RC_BAD(rc = rec->insertLast(1, LAST_NAME_TAG, FLM_TEXT_TYPE, &field))) fail("insert last", rc);
|
|
if (RC_BAD(rc = rec->setNative(field, "NWE"))) fail("set last", rc);
|
|
if (RC_BAD(rc = rec->insertLast(1, SECRET_TAG, FLM_TEXT_TYPE, &field))) fail("insert secret", rc);
|
|
if (RC_BAD(rc = rec->setNative(field, kSecret, SECRET_ENCDEF))) fail("set encrypted secret", rc);
|
|
if (!rec->isEncryptedField(field)) fail("secret field was not marked encrypted", 0);
|
|
if (RC_BAD(rc = rec->insertLast(1, AGE_TAG, FLM_NUMBER_TYPE, &field))) fail("insert age", rc);
|
|
if (RC_BAD(rc = rec->setUINT(field, 28))) fail("set age", rc);
|
|
|
|
FLMUINT drn = 0;
|
|
if (RC_BAD(rc = FlmRecordAdd(hDb, FLM_DATA_CONTAINER, &drn, rec, 0))) fail("FlmRecordAdd", rc);
|
|
*drn_out = drn;
|
|
rec->Release();
|
|
}
|
|
|
|
static void verify_person(HFDB hDb, FLMUINT drn)
|
|
{
|
|
RCODE rc;
|
|
FlmRecord *rec = NULL;
|
|
if (RC_BAD(rc = FlmRecordRetrieve(hDb, FLM_DATA_CONTAINER, drn, FO_EXACT, &rec, NULL)))
|
|
{
|
|
fail("FlmRecordRetrieve", rc);
|
|
}
|
|
|
|
char value[128];
|
|
FLMUINT len = sizeof(value);
|
|
void *field = rec->find(rec->root(), SECRET_TAG);
|
|
if (!field) fail("missing secret field", 0);
|
|
if (RC_BAD(rc = rec->getNative(field, value, &len))) fail("decrypt secret", rc);
|
|
if (strcmp(value, kSecret) != 0) fail("decrypted secret value mismatch", 0);
|
|
if (!rec->isEncryptedField(field)) fail("retrieved secret field was not encrypted", 0);
|
|
|
|
field = rec->find(rec->root(), AGE_TAG);
|
|
if (!field) fail("missing age field", 0);
|
|
FLMUINT age = 0;
|
|
if (RC_BAD(rc = rec->getUINT(field, &age))) fail("get age", rc);
|
|
if (age != 28) fail("age value mismatch", 0);
|
|
|
|
rec->Release();
|
|
}
|
|
|
|
static void verify_cursor(HFDB hDb)
|
|
{
|
|
HFCURSOR cursor = HFCURSOR_NULL;
|
|
RCODE rc;
|
|
FLMBYTE value[64];
|
|
FlmRecord *rec = NULL;
|
|
|
|
if (RC_BAD(rc = FlmCursorInit(hDb, FLM_DATA_CONTAINER, &cursor))) fail("FlmCursorInit", rc);
|
|
if (RC_BAD(rc = FlmCursorAddField(cursor, LAST_NAME_TAG, 0))) fail("cursor last field", rc);
|
|
if (RC_BAD(rc = FlmCursorAddOp(cursor, FLM_EQ_OP))) fail("cursor last eq", rc);
|
|
f_sprintf((char *)value, "NWE");
|
|
if (RC_BAD(rc = FlmCursorAddValue(cursor, FLM_STRING_VAL, value, 0))) fail("cursor last value", rc);
|
|
if (RC_BAD(rc = FlmCursorAddOp(cursor, FLM_AND_OP))) fail("cursor and", rc);
|
|
if (RC_BAD(rc = FlmCursorAddField(cursor, FIRST_NAME_TAG, 0))) fail("cursor first field", rc);
|
|
if (RC_BAD(rc = FlmCursorAddOp(cursor, FLM_EQ_OP))) fail("cursor first eq", rc);
|
|
f_sprintf((char *)value, "Mars");
|
|
if (RC_BAD(rc = FlmCursorAddValue(cursor, FLM_STRING_VAL, value, 0))) fail("cursor first value", rc);
|
|
if (RC_BAD(rc = FlmCursorFirst(cursor, &rec))) fail("FlmCursorFirst", rc);
|
|
if (rec) rec->Release();
|
|
FlmCursorFree(&cursor);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
fprintf(stderr, "usage: %s WORKDIR\n", argv[0]);
|
|
return 2;
|
|
}
|
|
|
|
rm_rf(argv[1]);
|
|
ensure_dir(argv[1]);
|
|
|
|
char db[4096], data[4096], rfl[4096];
|
|
snprintf(db, sizeof(db), "%s/classic.db", argv[1]);
|
|
snprintf(data, sizeof(data), "%s/data", argv[1]);
|
|
snprintf(rfl, sizeof(rfl), "%s/rfl", argv[1]);
|
|
ensure_dir(data);
|
|
ensure_dir(rfl);
|
|
|
|
RCODE rc;
|
|
HFDB hDb = HFDB_NULL;
|
|
FLMUINT drn = 0;
|
|
|
|
if (RC_BAD(rc = FlmStartup())) fail("FlmStartup", rc);
|
|
if (RC_BAD(rc = FlmDbCreate(db, data, rfl, NULL, kDictionary, NULL, &hDb))) fail("FlmDbCreate", rc);
|
|
if (RC_BAD(rc = FlmDbTransBegin(hDb, FLM_UPDATE_TRANS, 15))) fail("FlmDbTransBegin", rc);
|
|
add_encryption_definition(hDb);
|
|
add_person(hDb, &drn);
|
|
if (RC_BAD(rc = FlmDbTransCommit(hDb))) fail("FlmDbTransCommit", rc);
|
|
verify_person(hDb, drn);
|
|
verify_cursor(hDb);
|
|
FlmDbClose(&hDb);
|
|
|
|
if (RC_BAD(rc = FlmDbOpen(db, data, rfl, 0, NULL, &hDb))) fail("FlmDbOpen", rc);
|
|
verify_person(hDb, drn);
|
|
FlmDbClose(&hDb);
|
|
FlmShutdown();
|
|
|
|
assert_secret_not_plaintext(argv[1]);
|
|
printf("mars-nwe FLAIM classic API/encryption smoke passed: %s\n", db);
|
|
return 0;
|
|
}
|