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 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 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; }
/* * 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; }
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"); }
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); }
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; }
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); }
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; }