#include "flaim.h" #include #include #include #include #include #include #include #include #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 @10@ EncDef SecretKey\n" " 1 type aes\n" "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_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_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; }