void test_iter_delete_entry(void **state) { /* Test that deleting entry under cursor does not interrupt iteration */ CF_DB *db; assert_int_equal(OpenDB(&db, dbid_classes), true); assert_int_equal(WriteDB(db, "foobar", "abc", 3), true); assert_int_equal(WriteDB(db, "bazbaz", "def", 3), true); assert_int_equal(WriteDB(db, "booo", "ghi", 3), true); CF_DBC *cursor; assert_int_equal(NewDBCursor(db, &cursor), true); char *key; int ksize; void *value; int vsize; assert_int_equal(NextDB(db, cursor, &key, &ksize, &value, &vsize), true); assert_int_equal(DBCursorDeleteEntry(cursor), true); assert_int_equal(NextDB(db, cursor, &key, &ksize, &value, &vsize), true); assert_int_equal(NextDB(db, cursor, &key, &ksize, &value, &vsize), true); assert_int_equal(DeleteDBCursor(db, cursor), true); CloseDB(db); }
static void test_no_migration(void) { DBHandle *db = setup(true); CloseDB(db); /* Migration on empty DB should produce single "version" key */ assert_int_equal(OpenDB(&db, dbid_lastseen), true); DBCursor *cursor; assert_int_equal(NewDBCursor(db, &cursor), true); char *key; void *value; int ksize, vsize; while (NextDB(cursor, &key, &ksize, &value, &vsize)) { assert_int_equal(ksize, strlen("version") + 1); assert_string_equal(key, "version"); assert_int_equal(vsize, 2); assert_string_equal(value, "1"); } assert_int_equal(DeleteDBCursor(cursor), true); CloseDB(db); }
static bool PurgeCurrentLastSeen() { CF_DB *db_conn = NULL; CF_DBC *db_cursor = NULL; char *key = NULL; void *value = NULL; int ksize = 0, vsize = 0; if (!OpenDB(&db_conn, dbid_lastseen)) { Log(LOG_LEVEL_ERR, "Unable to open lastseen db"); return false; } if (!NewDBCursor(db_conn, &db_cursor)) { Log(LOG_LEVEL_ERR, "Unable to scan lastseen db"); CloseDB(db_conn); return false; } while (NextDB(db_cursor, &key, &ksize, &value, &vsize)) { /* Only read the 'quality of connection' entries */ if (key[0] != 'q') { continue; } time_t then = 0; if (value != NULL) { if (sizeof(KeyHostSeen) < vsize) { Log(LOG_LEVEL_ERR, "Invalid entry in lastseen database."); continue; } KeyHostSeen entry = { 0 }; memcpy(&entry, value, vsize); then = entry.lastseen; } if (then - START_TIME > NHOSTS) { DBCursorDeleteEntry(db_cursor); Log(LOG_LEVEL_DEBUG, "Deleting expired entry for %s", key); continue; } } DeleteDBCursor(db_cursor); CloseDB(db_conn); return true; }
static void TestCursorIteration(CF_DB *db) { CF_DBC *dbc; if(!NewDBCursor(db, &dbc)) { FatalError("Test: could not create cursor"); pthread_exit((void*)STATUS_ERROR); } char *key; void *value; int key_sz, value_sz; int count = 0; while(NextDB(db, dbc, &key, &key_sz, &value, &value_sz)) { int key_num = *(int *)key; int value_num = *(int *)value; //int key_num = atoi(key); //int value_num = atoi(value); if(key_num >= 0 && key_num < RECORD_COUNT_JUNK) { if((key_num + VALUE_OFFSET1 != value_num) && (key_num + VALUE_OFFSET2 != value_num)) { printf("Error: key,value %d,%d are inconsistent\n", key_num, value_num); } } else if(key_num == READWRITEKEY) { if(!ReadWriteDataIsValid(value)) { printf("Error: ReadWrite data is invalid\n"); } } else { printf("Error: invalid key \"%s\"", key); } count++; } if(count != RECORD_COUNT_TOTAL) { printf("Error: During iteration count was %d (expected %d)\n", count, RECORD_COUNT_TOTAL); } if(!DeleteDBCursor(db, dbc)) { FatalError("Test: could not delete cursor"); } }
static bool BundlesMigrationVersion0(DBHandle *db) { bool errors = false; DBCursor *cursor; if (!NewDBCursor(db, &cursor)) { return false; } char *key; void *value; int ksize, vsize; while (NextDB(cursor, &key, &ksize, &value, &vsize)) { if (ksize == 0) { Log(LOG_LEVEL_INFO, "BundlesMigrationVersion0: Database structure error -- zero-length key."); continue; } if (strchr(key, '.')) // is qualified name? { continue; } char *fqname = StringConcatenate(3, "default", ".", key); if (!WriteDB(db, fqname, value, vsize)) { Log(LOG_LEVEL_INFO, "Unable to write version 1 bundle entry for '%s'", key); errors = true; continue; } if (!DBCursorDeleteEntry(cursor)) { Log(LOG_LEVEL_INFO, "Unable to delete version 0 bundle entry for '%s'", key); errors = true; } } if (DeleteDBCursor(cursor) == false) { Log(LOG_LEVEL_ERR, "BundlesMigrationVersion0: Unable to close cursor"); errors = true; } if ((!errors) && (!WriteDB(db, "version", "1", sizeof("1")))) { errors = true; } return !errors; }
static void Mon_DumpSlowlyVaryingObservations(void) { CF_DB *dbp; CF_DBC *dbcp; FILE *fout; char *key; void *stored; int ksize, vsize; char name[CF_BUFSIZE]; if (!OpenDB(&dbp, dbid_static)) { return; } snprintf(name, CF_BUFSIZE - 1, "%s%cstate%cstatic_data", CFWORKDIR, FILE_SEPARATOR, FILE_SEPARATOR); if ((fout = fopen(name, "w")) == NULL) { Log(LOG_LEVEL_ERR, "Unable to save discovery data in '%s'. (fopen: %s)", name, GetErrorStr()); CloseDB(dbp); return; } /* Acquire a cursor for the database. */ if (!NewDBCursor(dbp, &dbcp)) { Log(LOG_LEVEL_INFO, "Unable to scan class db"); CloseDB(dbp); return; } while (NextDB(dbcp, &key, &ksize, &stored, &vsize)) { char buf[CF_MAXVARSIZE], lval[CF_MAXVARSIZE], rval[CF_BUFSIZE]; strncpy(buf, key, CF_MAXVARSIZE - 1); sscanf(buf, "%s:", lval); if (stored != NULL) { strncpy(rval, stored, CF_BUFSIZE - 1); fprintf(fout, "%s:%s\n", lval, rval); } } DeleteDBCursor(dbcp); CloseDB(dbp); fclose(fout); }
/* * Provides empty lastseen DB */ static DBHandle *setup(bool clean) { char cmd[CF_BUFSIZE]; xsnprintf(cmd, CF_BUFSIZE, "rm -rf '%s'/*", CFWORKDIR); system(cmd); DBHandle *db; OpenDB(&db, dbid_lastseen); if (clean) { /* There is no way to disable hook in OpenDB yet, so just undo * everything */ DBCursor *cursor; if (!NewDBCursor(db, &cursor)) { return NULL; } char *key; void *value; int ksize, vsize; while (NextDB(cursor, &key, &ksize, &value, &vsize)) { DBCursorDeleteEntry(cursor); } if (!DeleteDBCursor(cursor)) { return NULL; } } return db; }
int LastSeenHostKeyCount(void) { CF_DB *dbp; CF_DBC *dbcp; QPoint entry; char *key; void *value; int ksize, vsize; int count = 0; if (OpenDB(&dbp, dbid_lastseen)) { memset(&entry, 0, sizeof(entry)); if (NewDBCursor(dbp, &dbcp)) { while (NextDB(dbcp, &key, &ksize, &value, &vsize)) { /* Only look for valid "hostkey" entries */ if ((key[0] != 'k') || (value == NULL)) { continue; } count++; } DeleteDBCursor(dbcp); } CloseDB(dbp); } return count; }
bool ScanLastSeenQuality(LastSeenQualityCallback callback, void *ctx) { DBHandle *db; DBCursor *cursor; if (!OpenDB(&db, dbid_lastseen)) { Log(LOG_LEVEL_ERR, "Unable to open lastseen database"); return false; } if (!NewDBCursor(db, &cursor)) { Log(LOG_LEVEL_ERR, "Unable to create lastseen database cursor"); CloseDB(db); return false; } char *key; void *value; int ksize, vsize; while (NextDB(cursor, &key, &ksize, &value, &vsize)) { /* Only look for "keyhost" entries */ if (key[0] != 'k') { continue; } const char *hostkey = key + 1; const char *address = value; char incoming_key[CF_BUFSIZE]; snprintf(incoming_key, CF_BUFSIZE, "qi%s", hostkey); KeyHostSeen incoming; if (ReadDB(db, incoming_key, &incoming, sizeof(incoming))) { if (!(*callback)(hostkey, address, true, &incoming, ctx)) { break; } } char outgoing_key[CF_BUFSIZE]; snprintf(outgoing_key, CF_BUFSIZE, "qo%s", hostkey); KeyHostSeen outgoing; if (ReadDB(db, outgoing_key, &outgoing, sizeof(outgoing))) { if (!(*callback)(hostkey, address, false, &outgoing, ctx)) { break; } } } DeleteDBCursor(cursor); CloseDB(db); return true; }
/** * @brief check whether the lastseen DB is coherent or not * * A DB is coherent mainly if all the entries are valid and if there is * a strict one-to-one correspondance between hosts and key digests * (whether in MD5 or SHA1 format). * * @retval true if the lastseen DB is coherent, false otherwise */ bool IsLastSeenCoherent(void) { DBHandle *db; DBCursor *cursor; bool res = true; if (!OpenDB(&db, dbid_lastseen)) { Log(LOG_LEVEL_ERR, "Unable to open lastseen database"); return false; } if (!NewDBCursor(db, &cursor)) { Log(LOG_LEVEL_ERR, "Unable to create lastseen database cursor"); CloseDB(db); return false; } char *key; void *value; int ksize, vsize; Item *qkeys=NULL; Item *akeys=NULL; Item *kkeys=NULL; Item *ahosts=NULL; Item *khosts=NULL; char val[CF_BUFSIZE]; while (NextDB(cursor, &key, &ksize, &value, &vsize)) { if (key[0] != 'k' && key[0] != 'q' && key[0] != 'a' ) { continue; } if (key[0] == 'q' ) { if (strncmp(key,"qiSHA=",5)==0 || strncmp(key,"qoSHA=",5)==0 || strncmp(key,"qiMD5=",5)==0 || strncmp(key,"qoMD5=",5)==0) { if (IsItemIn(qkeys, key+2)==false) { PrependItem(&qkeys, key+2, NULL); } } } if (key[0] == 'k' ) { if (strncmp(key, "kSHA=", 4)==0 || strncmp(key, "kMD5=", 4)==0) { if (IsItemIn(kkeys, key+1)==false) { PrependItem(&kkeys, key+1, NULL); } if (ReadDB(db, key, &val, vsize)) { if (IsItemIn(khosts, val)==false) { PrependItem(&khosts, val, NULL); } } } } if (key[0] == 'a' ) { if (IsItemIn(ahosts, key+1)==false) { PrependItem(&ahosts, key+1, NULL); } if (ReadDB(db, key, &val, vsize)) { if (IsItemIn(akeys, val)==false) { PrependItem(&akeys, val, NULL); } } } } DeleteDBCursor(cursor); CloseDB(db); if (ListsCompare(ahosts, khosts) == false) { res = false; goto clean; } if (ListsCompare(akeys, kkeys) == false) { res = false; goto clean; } clean: DeleteItemList(qkeys); DeleteItemList(akeys); DeleteItemList(kkeys); DeleteItemList(ahosts); DeleteItemList(khosts); return res; }
static void VerifyFriendConnections(int hours,struct Attributes a,struct Promise *pp) /* Go through the database of recent connections and check for Long Time No See ...*/ { CF_DB *dbp; CF_DBC *dbcp; char *key; void *value; int ksize,vsize; int secs = SECONDS_PER_HOUR*hours, criterion, overdue; time_t now = time(NULL),lsea = (time_t)CF_WEEK, tthen, then; char name[CF_BUFSIZE],hostname[CF_BUFSIZE],datebuf[CF_MAXVARSIZE]; char addr[CF_BUFSIZE],type[CF_BUFSIZE],output[CF_BUFSIZE]; struct QPoint entry; double average = 0.0, var = 0.0, ticksperminute = 60.0; double ticksperhour = (double)SECONDS_PER_HOUR; CfOut(cf_verbose,"","CheckFriendConnections(%d)\n",hours); snprintf(name,CF_BUFSIZE-1,"%s/lastseen/%s",CFWORKDIR,CF_LASTDB_FILE); MapName(name); if (!OpenDB(name,&dbp)) { return; } /* Acquire a cursor for the database. */ if (!NewDBCursor(dbp,&dbcp)) { CfOut(cf_inform,""," !! Unable to scan friend db"); return; } /* Walk through the database and print out the key/data pairs. */ while(NextDB(dbp,dbcp,&key,&ksize,&value,&vsize)) { memset(&entry, 0, sizeof(entry)); strncpy(hostname,(char *)key,ksize); if (value != NULL) { memcpy(&entry,value,sizeof(entry)); then = (time_t)entry.q; average = (double)entry.expect; var = (double)entry.var; } else { continue; } if (then == 0) { continue; // No data } /* Got data, now get expiry criterion */ if (secs == 0) { /* Twice the average delta is significant */ criterion = (now - then > (int)(average+2.0*sqrt(var)+0.5)); overdue = now - then - (int)(average); } else { criterion = (now - then > secs); overdue = (now - then - secs); } if (LASTSEENEXPIREAFTER < 0) { lsea = (time_t)CF_WEEK; } else { lsea = LASTSEENEXPIREAFTER; } if (a.report.friend_pattern) { if (FullTextMatch(a.report.friend_pattern,IPString2Hostname(hostname+1))) { CfOut(cf_verbose,"","Not judging friend %s\n",hostname); criterion = false; lsea = CF_INFINITY; } } tthen = (time_t)then; snprintf(datebuf,CF_MAXVARSIZE-1,"%s",cf_ctime(&tthen)); datebuf[strlen(datebuf)-9] = '\0'; /* Chop off second and year */ snprintf(addr,15,"%s",hostname+1); switch(*hostname) { case '+': snprintf(type,CF_BUFSIZE,"last responded to hails"); break; case'-': snprintf(type,CF_BUFSIZE,"last hailed us"); break; } snprintf(output,CF_BUFSIZE,"Host %s i.e. %s %s @ [%s] (overdue by %d mins)", IPString2Hostname(hostname+1), addr, type, datebuf, overdue/(int)ticksperminute); if (criterion) { CfOut(cf_error,"",output); } else { CfOut(cf_verbose,"",output); } snprintf(output,CF_BUFSIZE,"i.e. (%.2f) hrs ago, Av %.2f +/- %.2f hrs\n", ((double)(now-then))/ticksperhour, average/ticksperhour, sqrt(var)/ticksperhour); if (criterion) { CfOut(cf_error,"",output); } else { CfOut(cf_verbose,"",output); } if (now - then > lsea) { CfOut(cf_error,"","Giving up on host %s -- %d hours since last seen",IPString2Hostname(hostname+1),hours); DeleteDB(dbp,hostname); } memset(&value,0,sizeof(value)); memset(&key,0,sizeof(key)); } DeleteDBCursor(dbp,dbcp); CloseDB(dbp); }
static void VerifyFriendReliability(struct Attributes a,struct Promise *pp) { CF_DB *dbp; CF_DBC *dbcp; int i,ksize,vsize; char *key; void *value; double n[CF_RELIABLE_CLASSES],n_av[CF_RELIABLE_CLASSES],total; double p[CF_RELIABLE_CLASSES],p_av[CF_RELIABLE_CLASSES]; char name[CF_BUFSIZE],hostname[CF_BUFSIZE],timekey[CF_MAXVARSIZE]; struct QPoint entry; struct Item *ip, *hostlist = NULL; double average,var,sum,sum_av,expect,actual; time_t now = time(NULL), then, lastseen = CF_WEEK; CfOut(cf_verbose,"","CheckFriendReliability()\n"); snprintf(name,CF_BUFSIZE-1,"%s/%s",CFWORKDIR,CF_LASTDB_FILE); average = (double) CF_HOUR; /* It will take a week for a host to be deemed reliable */ var = 0; if (!OpenDB(name,&dbp)) { return; } if (!NewDBCursor(dbp,&dbcp)) { CfOut(cf_inform,""," !! Unable to scan last-seen db"); return; } while(NextDB(dbp,dbcp,&key,&ksize,&value,&vsize)) { strcpy(hostname,IPString2Hostname((char *)key+1)); if (!IsItemIn(hostlist,hostname)) { /* Check hostname not recorded twice with +/- */ AppendItem(&hostlist,hostname,NULL); CfOut(cf_verbose,""," Measuring reliability of %s\n",hostname); } } DeleteDBCursor(dbp,dbcp); CloseDB(dbp); /* Now go through each host and recompute entropy */ for (ip = hostlist; ip != NULL; ip=ip->next) { snprintf(name,CF_BUFSIZE-1,"%s/%s.%s",CFWORKDIR,CF_LASTDB_FILE,ip->name); MapName(name); if (!OpenDB(name,&dbp)) { return; } for (i = 0; i < CF_RELIABLE_CLASSES; i++) { n[i] = n_av[i] = 0.0; } total = 0.0; for (now = CF_MONDAY_MORNING; now < CF_MONDAY_MORNING+CF_WEEK; now += CF_MEASURE_INTERVAL) { strcpy(timekey,GenTimeKey(now)); if (ReadDB(dbp,timekey,&value,sizeof(entry))) { memcpy(&entry,value,sizeof(entry)); then = (time_t)entry.q; lastseen = now - then; if (lastseen < 0) { lastseen = 0; /* Never seen before, so pretend */ } average = (double)entry.expect; var = (double)entry.var; Debug("%s => then = %ld, lastseen = %ld, average=%.2f\n",hostname,then,lastseen,average); } else { /* If we have no data, it means no contact for whatever reason. It could be unable to respond unwilling to respond, policy etc. Assume for argument that we expect regular responses ... */ lastseen += CF_MEASURE_INTERVAL; /* infer based on no data */ } for (i = 0; i < CF_RELIABLE_CLASSES; i++) { if (lastseen >= i*CF_HOUR && lastseen < (i+1)*CF_HOUR) { n[i]++; } if (average >= (double)(i*CF_HOUR) && average < (double)((i+1)*CF_HOUR)) { n_av[i]++; } } total++; } sum = sum_av = 0.0; for (i = 0; i < CF_RELIABLE_CLASSES; i++) { p[i] = n[i]/total; p_av[i] = n_av[i]/total; sum += p[i]; sum_av += p_av[i]; } Debug("Reliabilities sum to %.2f av %.2f\n\n",sum,sum_av); sum = sum_av = 0.0; for (i = 0; i < CF_RELIABLE_CLASSES; i++) { if (p[i] == 0.0) { continue; } sum -= p[i] * log(p[i]); } for (i = 0; i < CF_RELIABLE_CLASSES; i++) { if (p_av[i] == 0.0) { continue; } sum_av -= p_av[i] * log(p_av[i]); } actual = sum/log((double)CF_RELIABLE_CLASSES)*100.0; expect = sum_av/log((double)CF_RELIABLE_CLASSES)*100.0; CfOut(cf_verbose,"","Scaled entropy for %s = %.1f %%\n",ip->name,actual); CfOut(cf_verbose,"","Expected entropy for %s = %.1f %%\n\n",ip->name,expect); if (actual > expect) { CfOut(cf_inform,""," !! The reliability of %s deteriorated\n",ip->name); } if (actual > 50.0) { CfOut(cf_error,"","FriendStatus reports the intermittency of %s above 50%% (SEUs)\n",ip->name); } if (expect > actual) { CfOut(cf_inform,"","The reliability of %s is improved\n",ip->name); } CloseDB(dbp); } DeleteItemList(hostlist); }
void PurgeHashes(char *path, Attributes attr, Promise *pp) /* Go through the database and purge records about non-existent files */ { CF_DB *dbp; CF_DBC *dbcp; struct stat statbuf; int ksize, vsize; char *key; void *value; if (!OpenDB(&dbp,dbid_checksums)) { return; } if (path) { if (cfstat(path, &statbuf) == -1) { DeleteDB(dbp, path); } CloseDB(dbp); return; } /* Acquire a cursor for the database. */ if (!NewDBCursor(dbp, &dbcp)) { CfOut(OUTPUT_LEVEL_INFORM, "", " !! Unable to scan hash database"); CloseDB(dbp); return; } /* Walk through the database and print out the key/data pairs. */ while (NextDB(dbp, dbcp, &key, &ksize, &value, &vsize)) { char *obj = (char *) key + CF_INDEX_OFFSET; if (cfstat(obj, &statbuf) == -1) { if (attr.change.update) { DBCursorDeleteEntry(dbcp); } else { cfPS(OUTPUT_LEVEL_ERROR, CF_WARN, "", pp, attr, "ALERT: File %s no longer exists!", obj); } LogHashChange(obj, cf_file_removed, "File removed", pp); } memset(&key, 0, sizeof(key)); memset(&value, 0, sizeof(value)); } DeleteDBCursor(dbp, dbcp); CloseDB(dbp); }
void NoteClassUsage(AlphaList baselist, int purge) { CF_DB *dbp; CF_DBC *dbcp; void *stored; char *key; int ksize, vsize; Event e, entry, newe; double lsea = SECONDS_PER_WEEK * 52; /* expire after (about) a year */ time_t now = time(NULL); Item *list = NULL; const Item *ip; double lastseen; double vtrue = 1.0; /* end with a rough probability */ /* Only do this for the default policy, too much "downgrading" otherwise */ if (MINUSF) { return; } AlphaListIterator it = AlphaListIteratorInit(&baselist); for (ip = AlphaListIteratorNext(&it); ip != NULL; ip = AlphaListIteratorNext(&it)) { if ((IGNORECLASS(ip->name))) { CfDebug("Ignoring class %s (not packing)", ip->name); continue; } IdempPrependItem(&list, ip->name, NULL); } if (!OpenDB(&dbp, dbid_classes)) { return; } /* First record the classes that are in use */ for (ip = list; ip != NULL; ip = ip->next) { if (ReadDB(dbp, ip->name, &e, sizeof(e))) { CfDebug("FOUND %s with %lf\n", ip->name, e.Q.expect); lastseen = now - e.t; newe.t = now; newe.Q = QAverage(e.Q, vtrue, 0.7); } else { lastseen = 0.0; newe.t = now; /* With no data it's 50/50 what we can say */ newe.Q = QDefinite(0.5 * vtrue); } if (lastseen > lsea) { CfDebug("Class usage record %s expired\n", ip->name); DeleteDB(dbp, ip->name); } else { WriteDB(dbp, ip->name, &newe, sizeof(newe)); } } /* Then update with zero the ones we know about that are not active */ if (purge) { /* Acquire a cursor for the database and downgrade classes that did not get defined this time*/ if (!NewDBCursor(dbp, &dbcp)) { CfOut(cf_inform, "", " !! Unable to scan class db"); CloseDB(dbp); DeleteItemList(list); return; } memset(&entry, 0, sizeof(entry)); while (NextDB(dbp, dbcp, &key, &ksize, &stored, &vsize)) { time_t then; char eventname[CF_BUFSIZE]; memset(eventname, 0, CF_BUFSIZE); strncpy(eventname, (char *) key, ksize); if (stored != NULL) { memcpy(&entry, stored, sizeof(entry)); then = entry.t; lastseen = now - then; if (lastseen > lsea) { CfDebug("Class usage record %s expired\n", eventname); DBCursorDeleteEntry(dbcp); } else if (!IsItemIn(list, eventname)) { newe.t = then; newe.Q = QAverage(entry.Q, 0, 0.5); if (newe.Q.expect <= 0.0001) { CfDebug("Deleting class %s as %lf is zero\n", eventname, newe.Q.expect); DBCursorDeleteEntry(dbcp); } else { CfDebug("Downgrading class %s from %lf to %lf\n", eventname, entry.Q.expect, newe.Q.expect); DBCursorWriteEntry(dbcp, &newe, sizeof(newe)); } } } } DeleteDBCursor(dbp, dbcp); } CloseDB(dbp); DeleteItemList(list); }
PromiseResult PurgeHashes(EvalContext *ctx, char *path, Attributes attr, const Promise *pp) /* Go through the database and purge records about non-existent files */ { CF_DB *dbp; CF_DBC *dbcp; struct stat statbuf; int ksize, vsize; char *key; void *value; if (!OpenDB(&dbp,dbid_checksums)) { return PROMISE_RESULT_NOOP; } if (path) { if (stat(path, &statbuf) == -1) { DeleteDB(dbp, path); } CloseDB(dbp); return PROMISE_RESULT_NOOP; } /* Acquire a cursor for the database. */ if (!NewDBCursor(dbp, &dbcp)) { Log(LOG_LEVEL_INFO, "Unable to scan hash database"); CloseDB(dbp); return PROMISE_RESULT_NOOP; } /* Walk through the database and print out the key/data pairs. */ PromiseResult result = PROMISE_RESULT_NOOP; while (NextDB(dbcp, &key, &ksize, &value, &vsize)) { char *obj = (char *) key + CF_INDEX_OFFSET; if (stat(obj, &statbuf) == -1) { if (attr.change.update) { DBCursorDeleteEntry(dbcp); } else { cfPS(ctx, LOG_LEVEL_NOTICE, PROMISE_RESULT_WARN, pp, attr, "File '%s' no longer exists", obj); result = PromiseResultUpdate(result, PROMISE_RESULT_WARN); } LogHashChange(obj, FILE_STATE_REMOVED, "File removed", pp); } } DeleteDBCursor(dbcp); CloseDB(dbp); return result; }
void UpdateLastSeen() // This function is temporarily or permanently deprecated { double lsea = LASTSEENEXPIREAFTER; int intermittency = false,qsize,ksize; struct CfKeyHostSeen q,newq; double lastseen,delta2; void *stored; CF_DB *dbp = NULL,*dbpent = NULL; CF_DBC *dbcp; char name[CF_BUFSIZE],*key; struct Rlist *rp; struct CfKeyBinding *kp; time_t now = time(NULL); static time_t then; if (now < then + 300 && then > 0 && then <= now + 300) { // Rate limiter return; } then = now; CfOut(cf_verbose,""," -> Writing last-seen observations"); ThreadLock(cft_server_keyseen); if (SERVER_KEYSEEN == NULL) { ThreadUnlock(cft_server_keyseen); CfOut(cf_verbose,""," -> Keyring is empty"); return; } if (BooleanControl("control_agent",CFA_CONTROLBODY[cfa_intermittency].lval)) { CfOut(cf_inform,""," -> Recording intermittency"); intermittency = true; } snprintf(name,CF_BUFSIZE-1,"%s/%s",CFWORKDIR,CF_LASTDB_FILE); MapName(name); if (!OpenDB(name,&dbp)) { ThreadUnlock(cft_server_keyseen); return; } /* First scan for hosts that have moved address and purge their records so that the database always has a 1:1 relationship between keyhash and IP address */ if (!NewDBCursor(dbp,&dbcp)) { ThreadUnlock(cft_server_keyseen); CfOut(cf_inform,""," !! Unable to scan class db"); return; } while(NextDB(dbp,dbcp,&key,&ksize,&stored,&qsize)) { memcpy(&q,stored,sizeof(q)); lastseen = (double)now - q.Q.q; if (lastseen > lsea) { CfOut(cf_verbose,""," -> Last-seen record for %s expired after %.1lf > %.1lf hours\n",key,lastseen/3600,lsea/3600); DeleteDB(dbp,key); } for (rp = SERVER_KEYSEEN; rp != NULL; rp=rp->next) { kp = (struct CfKeyBinding *) rp->item; if ((strcmp(q.address,kp->address) == 0) && (strcmp(key+1,kp->name+1) != 0)) { CfOut(cf_verbose,""," ! Deleting %s's address (%s=%d) as this host %s seems to have moved elsewhere (%s=5d)",key,kp->address,strlen(kp->address),kp->name,q.address,strlen(q.address)); DeleteDB(dbp,key); } } } DeleteDBCursor(dbp,dbcp); /* Now perform updates with the latest data */ for (rp = SERVER_KEYSEEN; rp != NULL; rp=rp->next) { kp = (struct CfKeyBinding *) rp->item; now = kp->timestamp; if (intermittency) { /* Open special file for peer entropy record - INRIA intermittency */ snprintf(name,CF_BUFSIZE-1,"%s/lastseen/%s.%s",CFWORKDIR,CF_LASTDB_FILE,kp->name); MapName(name); if (!OpenDB(name,&dbpent)) { continue; } } if (ReadDB(dbp,kp->name,&q,sizeof(q))) { lastseen = (double)now - q.Q.q; if (q.Q.q <= 0) { lastseen = 300; q.Q.expect = 0; q.Q.var = 0; } newq.Q.q = (double)now; newq.Q.expect = GAverage(lastseen,q.Q.expect,0.4); delta2 = (lastseen - q.Q.expect)*(lastseen - q.Q.expect); newq.Q.var = GAverage(delta2,q.Q.var,0.4); strncpy(newq.address,kp->address,CF_ADDRSIZE-1); } else { lastseen = 0.0; newq.Q.q = (double)now; newq.Q.expect = 0.0; newq.Q.var = 0.0; strncpy(newq.address,kp->address,CF_ADDRSIZE-1); } if (lastseen > lsea) { CfOut(cf_verbose,""," -> Last-seen record for %s expired after %.1lf > %.1lf hours\n",kp->name,lastseen/3600,lsea/3600); DeleteDB(dbp,kp->name); } else { char timebuf[26]; CfOut(cf_verbose,""," -> Last saw %s (alias %s) at %s (noexpiry %.1lf <= %.1lf)\n",kp->name,kp->address,cf_strtimestamp_local(now,timebuf),lastseen/3600,lsea/3600); WriteDB(dbp,kp->name,&newq,sizeof(newq)); if (intermittency) { WriteDB(dbpent,GenTimeKey(now),&newq,sizeof(newq)); } } if (intermittency && dbpent) { CloseDB(dbpent); } } ThreadUnlock(cft_server_keyseen); }
static void test_migration(void) { CF_DB *db; Seq *list = SeqNew(NO_FILES, free); // Hand crafted from the new version of NewIndexKey(). char checksum_key[NO_FILES][30] = { { 'H','_','M','D','5','\0','\0','\0','\0','\0', '/','e','t','c','/','h','o','s','t','s','\0' }, { 'H','_','M','D','5','\0','\0','\0','\0','\0', '/','e','t','c','/','p','a','s','s','w','d','\0' }, { 'H','_','M','D','5','\0','\0','\0','\0','\0', '/','f','i','l','e','1','\0' }, { 'H','_','M','D','5','\0','\0','\0','\0','\0', '/','f','i','l','e','2','\0' }, }; char *filestat_key[NO_FILES] = { "S_/etc/hosts", "S_/etc/passwd", "S_/file1", "S_/file2", }; // Should cause migration to happen. assert_true(FileChangesGetDirectoryList("/etc", list)); assert_int_equal(SeqLength(list), 2); assert_string_equal(SeqAt(list, 0), "hosts"); assert_string_equal(SeqAt(list, 1), "passwd"); SeqClear(list); assert_true(FileChangesGetDirectoryList("/", list)); assert_int_equal(SeqLength(list), 2); assert_string_equal(SeqAt(list, 0), "file1"); assert_string_equal(SeqAt(list, 1), "file2"); assert_true(OpenDB(&db, dbid_changes)); for (int c = 0; c < NO_FILES; c++) { { int ksize = 2 + CHANGES_HASH_FILE_NAME_OFFSET + strlen(checksum_key[c] + 2 + CHANGES_HASH_FILE_NAME_OFFSET) + 1; int vsize = ValueSizeDB(db, checksum_key[c], ksize); assert_int_equal(vsize, strlen(CHECKSUM_VALUE[c]) + 1); char value[vsize]; assert_true(ReadComplexKeyDB(db, checksum_key[c], ksize, value, vsize)); assert_int_equal(memcmp(value, CHECKSUM_VALUE[c], vsize), 0); } { int vsize = ValueSizeDB(db, filestat_key[c], strlen(filestat_key[c]) + 1); assert_int_equal(vsize, sizeof(struct stat)); char value[vsize]; assert_true(ReadDB(db, filestat_key[c], value, vsize)); assert_int_equal(memcmp(value, &filestat_value, vsize), 0); } } int db_entries = 0; CF_DBC *db_cursor; assert_true(NewDBCursor(db, &db_cursor)); char *key, *value; int ksize, vsize; while (NextDB(db_cursor, &key, &ksize, (void **)&value, &vsize)) { db_entries++; } DeleteDBCursor(db_cursor); // 2 x Directories ("/" and "/etc") // 4 x File hashes // 4 x File stats assert_int_equal(db_entries, 10); CloseDB(db); }
void IPString2KeyDigest(char *ipv4, char *result) { CF_DB *dbp; CF_DBC *dbcp; char *key; void *value; KeyHostSeen entry; int ksize, vsize; unsigned char digest[EVP_MAX_MD_SIZE + 1]; result[0] = '\0'; if (strcmp(ipv4, "127.0.0.1") == 0 || strcmp(ipv4, "::1") == 0 || strcmp(ipv4, VIPADDRESS) == 0) { if (PUBKEY) { HashPubKey(PUBKEY, digest, CF_DEFAULT_DIGEST); snprintf(result, CF_MAXVARSIZE, "%s", HashPrint(CF_DEFAULT_DIGEST, digest)); } return; } if (!OpenDB(&dbp, dbid_lastseen)) { return; } if (!NewDBCursor(dbp, &dbcp)) { CfOut(cf_inform, "", " !! Unable to scan last-seen database"); CloseDB(dbp); return; } /* Initialize the key/data return pair. */ memset(&entry, 0, sizeof(entry)); /* Walk through the database and print out the key/data pairs. */ while (NextDB(dbp, dbcp, &key, &ksize, &value, &vsize)) { if (value != NULL) { memcpy(&entry, value, sizeof(entry)); // Warning this is not 1:1 if (strcmp(ipv4, MapAddress((char *) entry.address)) == 0) { CfOut(cf_verbose, "", " -> Matched IP %s to key %s", ipv4, key + 1); strncpy(result, key + 1, CF_MAXVARSIZE - 1); break; } } } DeleteDBCursor(dbp, dbcp); CloseDB(dbp); if (NULL_OR_EMPTY(result)) { CfOut(cf_verbose, "", "!! Unable to find a key for ip %s", ipv4); } }
Item *ContextAccessControl(EvalContext *ctx, char *in, ServerConnectionState *conn, int encrypt) { Auth *ap; int access = false; char client_regex[CF_BUFSIZE]; CF_DB *dbp; CF_DBC *dbcp; int ksize, vsize; char *key; void *value; time_t now = time(NULL); CfState q; Item *ip, *matches = NULL, *candidates = NULL; sscanf(in, "CONTEXT %255[^\n]", client_regex); if (!OpenDB(&dbp, dbid_state)) { return NULL; } if (!NewDBCursor(dbp, &dbcp)) { Log(LOG_LEVEL_INFO, "Unable to scan persistence cache"); CloseDB(dbp); return NULL; } while (NextDB(dbcp, &key, &ksize, &value, &vsize)) { memcpy((void *) &q, value, sizeof(CfState)); if (now > q.expires) { Log(LOG_LEVEL_VERBOSE, " Persistent class %s expired", key); DBCursorDeleteEntry(dbcp); } else { if (FullTextMatch(ctx, client_regex, key)) { Log(LOG_LEVEL_VERBOSE, " - Found key %s...", key); AppendItem(&candidates, key, NULL); } } } DeleteDBCursor(dbcp); CloseDB(dbp); for (ip = candidates; ip != NULL; ip = ip->next) { for (ap = SV.varadmit; ap != NULL; ap = ap->next) { int res = false; if (FullTextMatch(ctx, ap->path, ip->name)) { res = true; } if (res) { Log(LOG_LEVEL_VERBOSE, "Found a matching rule in access list (%s in %s)", ip->name, ap->path); if (ap->classpattern == false) { Log(LOG_LEVEL_ERR, "Context %s requires a literal server item...cannot set variable directly by path", ap->path); access = false; continue; } if ((!encrypt) && (ap->encrypt == true)) { Log(LOG_LEVEL_ERR, "Context %s requires encrypt connection...will not serve", ip->name); access = false; break; } else { Log(LOG_LEVEL_DEBUG, "Checking whether to map root privileges"); if ((IsMatchItemIn(ctx, ap->maproot, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->maproot, conn->hostname))) { conn->maproot = true; Log(LOG_LEVEL_VERBOSE, "Mapping root privileges"); } else { Log(LOG_LEVEL_VERBOSE, "No root privileges granted"); } if ((IsMatchItemIn(ctx, ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = true; Log(LOG_LEVEL_DEBUG, "Access privileges - match found"); } } } } for (ap = SV.vardeny; ap != NULL; ap = ap->next) { if (strcmp(ap->path, ip->name) == 0) { if ((IsMatchItemIn(ctx, ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = false; Log(LOG_LEVEL_VERBOSE, "Host %s explicitly denied access to context %s", conn->hostname, ip->name); break; } } } if (access) { Log(LOG_LEVEL_VERBOSE, "Host %s granted access to context '%s'", conn->hostname, ip->name); AppendItem(&matches, ip->name, NULL); if (encrypt && LOGENCRYPT) { /* Log files that were marked as requiring encryption */ Log(LOG_LEVEL_INFO, "Host %s granted access to context '%s'", conn->hostname, ip->name); } } else { Log(LOG_LEVEL_VERBOSE, "Host %s denied access to context '%s'", conn->hostname, ip->name); } } DeleteItemList(candidates); return matches; }
static void PurgeMultipleIPReferences(CF_DB *dbp, char *rkey, char *ipaddress) { CF_DBC *dbcp; KeyHostSeen q; double lastseen, lsea = LASTSEENEXPIREAFTER; void *stored; char *key; time_t now = time(NULL); int qsize, ksize, update_address, keys_match; // This is an expensive call, but it is the price we pay for consistency // Make sure we only call it if we have to if (!NewDBCursor(dbp, &dbcp)) { CfOut(cf_inform, "", " !! Unable to scan the last seen db"); return; } while (NextDB(dbp, dbcp, &key, &ksize, &stored, &qsize)) { keys_match = false; if (strcmp(key + 1, rkey + 1) == 0) { keys_match = true; } memcpy(&q, stored, sizeof(q)); lastseen = (double) now - q.Q.q; if (lastseen > lsea) { CfOut(cf_verbose, "", " -> Last-seen record for %s expired after %.1lf > %.1lf hours\n", key, lastseen / 3600, lsea / 3600); DBCursorDeleteEntry(dbcp); continue; } // Avoid duplicate address/key pairs if (keys_match && strcmp(q.address, ipaddress) != 0) { CfOut(cf_verbose, "", " ! Synchronizing %s's address as this host %s seems to have moved from location %s to %s", key, rkey, q.address, ipaddress); strcpy(q.address, ipaddress); update_address = true; } else if (!keys_match && strcmp(q.address, ipaddress) == 0) { CfOut(cf_verbose, "", " ! Updating %s's address (%s) as this host %s seems to have gone off line", key, ipaddress, rkey); strcpy(q.address, CF_UNKNOWN_IP); update_address = true; } else { update_address = false; } if (update_address) { DBCursorWriteEntry(dbcp, &q, sizeof(q)); } } DeleteDBCursor(dbp, dbcp); }
void SetMeasurementPromises(Item ** classlist) { CF_DB *dbp; CF_DBC *dbcp; char eventname[CF_MAXVARSIZE], assignment[CF_BUFSIZE]; Event entry; char *key; void *stored; int ksize, vsize; if (!OpenDB(&dbp, dbid_measure)) { return; } if (!NewDBCursor(dbp, &dbcp)) { Log(LOG_LEVEL_INFO, "Unable to scan class db"); CloseDB(dbp); return; } memset(&entry, 0, sizeof(entry)); while (NextDB(dbcp, &key, &ksize, &stored, &vsize)) { if (stored != NULL) { if (sizeof(entry) < vsize) { Log(LOG_LEVEL_ERR, "Invalid entry in measurements database. Expected size: %zu, actual size: %d", sizeof(entry), vsize); continue; } strcpy(eventname, (char *) key); memcpy(&entry, stored, MIN(vsize, sizeof(entry))); Log(LOG_LEVEL_VERBOSE, "Setting measurement event %s", eventname); // a.measure.data_type is not longer known here, so look for zero decimals if ((int) (entry.Q.q * 10) % 10 == 0) { snprintf(assignment, CF_BUFSIZE - 1, "value_%s=%.0lf", eventname, entry.Q.q); } else { snprintf(assignment, CF_BUFSIZE - 1, "value_%s=%.2lf", eventname, entry.Q.q); } AppendItem(classlist, assignment, NULL); snprintf(assignment, CF_BUFSIZE - 1, "av_%s=%.2lf", eventname, entry.Q.expect); AppendItem(classlist, assignment, NULL); snprintf(assignment, CF_BUFSIZE - 1, "dev_%s=%.2lf", eventname, sqrt(entry.Q.var)); AppendItem(classlist, assignment, NULL); } } DeleteDBCursor(dbcp); CloseDB(dbp); }
void ShowLastSeenHosts() { CF_DB *dbp; CF_DBC *dbcp; char *key; void *value; char name[CF_BUFSIZE],hostname[CF_BUFSIZE],address[CF_MAXVARSIZE]; struct CfKeyHostSeen entry; int ksize,vsize; int count = 0; snprintf(name,CF_BUFSIZE-1,"%s/%s",CFWORKDIR,CF_LASTDB_FILE); MapName(name); if (!OpenDB(name,&dbp)) { return; } /* Acquire a cursor for the database. */ if (!NewDBCursor(dbp,&dbcp)) { CfOut(cf_inform,""," !! Unable to scan last-seen database"); CloseDB(dbp); return; } /* Initialize the key/data return pair. */ printf("%9.9s %17.17s %-25.25s %15.15s\n","Direction","IP","Name","Key"); /* Walk through the database and print out the key/data pairs. */ while(NextDB(dbp,dbcp,&key,&ksize,&value,&vsize)) { if (value != NULL) { memset(&entry, 0, sizeof(entry)); memset(hostname, 0, sizeof(hostname)); memset(address, 0, sizeof(address)); memcpy(&entry,value,sizeof(entry)); strncpy(hostname,(char *)key,sizeof(hostname)-1); strncpy(address,(char *)entry.address,sizeof(address)-1); ++count; } else { continue; } CfOut(cf_verbose,""," -> Reporting on %s",hostname); printf("%-9.9s %17.17s %-25.25s %s\n", hostname[0] == '+' ? "Outgoing" : "Incoming", address, IPString2Hostname(address), hostname+1 ); } printf("Total Entries: %d\n",count); DeleteDBCursor(dbp,dbcp); CloseDB(dbp); }
void EvalContextHeapPersistentLoadAll(EvalContext *ctx) { CF_DB *dbp; CF_DBC *dbcp; int ksize, vsize; char *key; void *value; time_t now = time(NULL); CfState q; if (LOOKUP) { return; } Banner("Loading persistent classes"); if (!OpenDB(&dbp, dbid_state)) { return; } /* Acquire a cursor for the database. */ if (!NewDBCursor(dbp, &dbcp)) { CfOut(OUTPUT_LEVEL_INFORM, "", " !! Unable to scan persistence cache"); return; } while (NextDB(dbcp, &key, &ksize, &value, &vsize)) { memcpy((void *) &q, value, sizeof(CfState)); CfDebug(" - Found key %s...\n", key); if (now > q.expires) { CfOut(OUTPUT_LEVEL_VERBOSE, "", " Persistent class %s expired\n", key); DBCursorDeleteEntry(dbcp); } else { CfOut(OUTPUT_LEVEL_VERBOSE, "", " Persistent class %s for %jd more minutes\n", key, (intmax_t)((q.expires - now) / 60)); CfOut(OUTPUT_LEVEL_VERBOSE, "", " Adding persistent class %s to heap\n", key); if (strchr(key, CF_NS)) { char ns[CF_MAXVARSIZE], name[CF_MAXVARSIZE]; ns[0] = '\0'; name[0] = '\0'; sscanf(key, "%[^:]:%[^\n]", ns, name); EvalContextHeapAddSoft(ctx, name, ns); } else { EvalContextHeapAddSoft(ctx, key, NULL); } } } DeleteDBCursor(dbcp); CloseDB(dbp); Banner("Loaded persistent memory"); }
static bool LastseenMigrationVersion0(DBHandle *db) { bool errors = false; DBCursor *cursor; if (!NewDBCursor(db, &cursor)) { return false; } char *key; void *value; int ksize, vsize; while (NextDB(cursor, &key, &ksize, &value, &vsize)) { if (ksize == 0) { Log(LOG_LEVEL_INFO, "LastseenMigrationVersion0: Database structure error -- zero-length key."); continue; } /* Only look for old [+-]kH -> IP<QPoint> entries */ if ((key[0] != '+') && (key[0] != '-')) { /* Warn about completely unexpected keys */ if ((key[0] != 'q') && (key[0] != 'k') && (key[0] != 'a')) { Log(LOG_LEVEL_INFO, "LastseenMigrationVersion0: Malformed key found '%s'", key); } continue; } bool incoming = key[0] == '-'; const char *hostkey = key + 1; /* Properly align the data */ const char *old_data_address = (const char *)value; QPoint0 old_data_q; memcpy(&old_data_q, (const char *)value + QPOINT0_OFFSET, sizeof(QPoint0)); /* Only migrate sane data */ if (vsize != QPOINT0_OFFSET + sizeof(QPoint0)) { Log(LOG_LEVEL_INFO, "LastseenMigrationVersion0: invalid value size for key '%s', entry is deleted", key); DBCursorDeleteEntry(cursor); continue; } char hostkey_key[CF_BUFSIZE]; snprintf(hostkey_key, CF_BUFSIZE, "k%s", hostkey); if (!WriteDB(db, hostkey_key, old_data_address, strlen(old_data_address) + 1)) { Log(LOG_LEVEL_INFO, "Unable to write version 1 lastseen entry for '%s'", key); errors = true; continue; } char address_key[CF_BUFSIZE]; snprintf(address_key, CF_BUFSIZE, "a%s", old_data_address); if (!WriteDB(db, address_key, hostkey, strlen(hostkey) + 1)) { Log(LOG_LEVEL_INFO, "Unable to write version 1 reverse lastseen entry for '%s'", key); errors = true; continue; } char quality_key[CF_BUFSIZE]; snprintf(quality_key, CF_BUFSIZE, "q%c%s", incoming ? 'i' : 'o', hostkey); /* Ignore malformed connection quality data */ if ((!isfinite(old_data_q.q)) || (old_data_q.q < 0) || (!isfinite(old_data_q.expect)) || (!isfinite(old_data_q.var))) { Log(LOG_LEVEL_INFO, "Ignoring malformed connection quality data for '%s'", key); DBCursorDeleteEntry(cursor); continue; } KeyHostSeen data = { .lastseen = (time_t)old_data_q.q, .Q = { /* Previously .q wasn't stored in database, but was calculated every time as a difference between previous timestamp and a new timestamp. Given we don't have this information during the database upgrade, just assume that last reading is an average one. */ .q = old_data_q.expect, .dq = 0, .expect = old_data_q.expect, .var = old_data_q.var, } }; if (!WriteDB(db, quality_key, &data, sizeof(data))) { Log(LOG_LEVEL_INFO, "Unable to write version 1 connection quality key for '%s'", key); errors = true; continue; } if (!DBCursorDeleteEntry(cursor)) { Log(LOG_LEVEL_INFO, "Unable to delete version 0 lastseen entry for '%s'", key); errors = true; } } if (DeleteDBCursor(cursor) == false) { Log(LOG_LEVEL_ERR, "LastseenMigrationVersion0: Unable to close cursor"); errors = true; } if ((!errors) && (!WriteDB(db, "version", "1", sizeof("1")))) { errors = true; } return !errors; }