Esempio n. 1
0
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;
}
Esempio n. 3
0
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;
}
Esempio n. 4
0
/*
 * 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;
}
Esempio n. 5
0
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");
}
Esempio n. 6
0
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);
}
Esempio n. 7
0
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);
}
Esempio n. 8
0
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;
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
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);
}
Esempio n. 11
0
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;
}