Exemple #1
0
bool WriteAmPolicyHubFile(const char *workdir, bool am_policy_hub)
{
    char *filename = AmPolicyHubFilename(workdir);
    if (am_policy_hub)
    {
        if (!GetAmPolicyHub(workdir))
        {
            if (creat(filename, 0600) == -1)
            {
                Log(LOG_LEVEL_ERR, "Error writing marker file '%s'", filename);
                free(filename);
                return false;
            }
        }
    }
    else
    {
        if (GetAmPolicyHub(workdir))
        {
            if (unlink(filename) != 0)
            {
                Log(LOG_LEVEL_ERR, "Error removing marker file '%s'", filename);
                free(filename);
                return false;
            }
        }
    }
    free(filename);
    return true;
}
static void KeepHardClasses(EvalContext *ctx)
{
    char name[CF_BUFSIZE];
    if (name != NULL)
    {
        char *existing_policy_server = PolicyServerReadFile(GetWorkDir());
        if (existing_policy_server)
        {
            free(existing_policy_server);
            if (GetAmPolicyHub())
            {
                MarkAsPolicyServer(ctx);
            }
        }
    }

    /* FIXME: why is it not in generic_agent?! */
    GenericAgentAddEditionClasses(ctx);
}
Exemple #3
0
static void KeepHardClasses(EvalContext *ctx)
{
    char name[CF_BUFSIZE];
    if (name != NULL)
    {
        char *existing_policy_server = ReadPolicyServerFile(CFWORKDIR);
        if (existing_policy_server)
        {
            if (GetAmPolicyHub(CFWORKDIR))
            {
                EvalContextClassPutHard(ctx, "am_policy_hub", "source=bootstrap");
            }
            free(existing_policy_server);
        }
    }

    /* FIXME: why is it not in generic_agent?! */
    GenericAgentAddEditionClasses(ctx);
}
Exemple #4
0
bool GenericAgentCheckPolicy(GenericAgentConfig *config, bool force_validation, bool write_validated_file)
{
    if (!MissingInputFile(config->input_file))
    {
        {
            if (config->agent_type == AGENT_TYPE_SERVER ||
                config->agent_type == AGENT_TYPE_MONITOR ||
                config->agent_type == AGENT_TYPE_EXECUTOR)
            {
                time_t validated_at = ReadTimestampFromPolicyValidatedFile(config, NULL);
                config->agent_specific.daemon.last_validated_at = validated_at;
            }
        }

        if (IsPolicyPrecheckNeeded(config, force_validation))
        {
            bool policy_check_ok = GenericAgentArePromisesValid(config);
            if (policy_check_ok && write_validated_file)
            {
                GenericAgentTagReleaseDirectory(config,
                                                NULL, // use GetAutotagDir
                                                write_validated_file, // true
                                                GetAmPolicyHub(GetWorkDir())); // write release ID?
            }

            if (config->agent_specific.agent.bootstrap_policy_server && !policy_check_ok)
            {
                Log(LOG_LEVEL_VERBOSE, "Policy is not valid, but proceeding with bootstrap");
                return true;
            }

            return policy_check_ok;
        }
        else
        {
            Log(LOG_LEVEL_VERBOSE, "Policy is already validated");
            return true;
        }
    }
    return false;
}
Exemple #5
0
void PolicyHubUpdateKeys(const char *policy_server)
{
    if (GetAmPolicyHub(CFWORKDIR)
        && NULL != PUBKEY)
    {
        unsigned char digest[EVP_MAX_MD_SIZE + 1];

        char dst_public_key_filename[CF_BUFSIZE] = "";
        {
            char buffer[CF_HOSTKEY_STRING_SIZE];
            HashPubKey(PUBKEY, digest, CF_DEFAULT_DIGEST);
            snprintf(dst_public_key_filename, sizeof(dst_public_key_filename),
                     "%s/ppkeys/%s-%s.pub",
                     CFWORKDIR, "root",
                     HashPrintSafe(buffer, sizeof(buffer), digest,
                                   CF_DEFAULT_DIGEST, true));
            MapName(dst_public_key_filename);
        }

        struct stat sb;
        if ((stat(dst_public_key_filename, &sb) == -1))
        {
            char src_public_key_filename[CF_BUFSIZE] = "";
            snprintf(src_public_key_filename, CF_MAXVARSIZE, "%s/ppkeys/localhost.pub", CFWORKDIR);
            MapName(src_public_key_filename);

            // copy localhost.pub to root-HASH.pub on policy server
            if (!LinkOrCopy(src_public_key_filename, dst_public_key_filename, false))
            {
                Log(LOG_LEVEL_ERR, "Unable to copy policy server's own public key from '%s' to '%s'", src_public_key_filename, dst_public_key_filename);
            }

            if (policy_server)
            {
                LastSaw(policy_server, digest, LAST_SEEN_ROLE_CONNECT);
            }
        }
    }
}
static void KeepHardClasses(EvalContext *ctx)
{
    char name[CF_BUFSIZE];
    if (name != NULL)
    {
        char *existing_policy_server = ReadPolicyServerFile(CFWORKDIR);
        if (existing_policy_server)
        {
            if (GetAmPolicyHub(CFWORKDIR))
            {
                EvalContextHeapAddHard(ctx, "am_policy_hub");
            }
            free(existing_policy_server);
        }
    }

    /* FIXME: why is it not in generic_agent?! */
#if defined HAVE_NOVA
    EvalContextHeapAddHard(ctx, "nova_edition");
    EvalContextHeapAddHard(ctx, "enterprise_edition");
#else
    EvalContextHeapAddHard(ctx, "community_edition");
#endif
}
Exemple #7
0
void GenericAgentDiscoverContext(EvalContext *ctx, GenericAgentConfig *config)
{
    GenericAgentSetDefaultDigest(&CF_DEFAULT_DIGEST, &CF_DEFAULT_DIGEST_LEN);

    GenericAgentInitialize(ctx, config);

    time_t t = SetReferenceTime();
    UpdateTimeClasses(ctx, t);
    SanitizeEnvironment();

    THIS_AGENT_TYPE = config->agent_type;
    EvalContextClassPutHard(ctx, CF_AGENTTYPES[config->agent_type], "cfe_internal,source=agent");

    DetectEnvironment(ctx);

    EvalContextHeapPersistentLoadAll(ctx);
    LoadSystemConstants(ctx);

    if (config->agent_type == AGENT_TYPE_AGENT && config->agent_specific.agent.bootstrap_policy_server)
    {
        if (!RemoveAllExistingPolicyInInputs(GetInputDir()))
        {
            Log(LOG_LEVEL_ERR, "Error removing existing input files prior to bootstrap");
            exit(EXIT_FAILURE);
        }

        if (!WriteBuiltinFailsafePolicy(GetInputDir()))
        {
            Log(LOG_LEVEL_ERR, "Error writing builtin failsafe to inputs prior to bootstrap");
            exit(EXIT_FAILURE);
        }

        bool am_policy_server = false;
        {
            const char *canonified_bootstrap_policy_server = CanonifyName(config->agent_specific.agent.bootstrap_policy_server);
            am_policy_server = NULL != EvalContextClassGet(ctx, NULL, canonified_bootstrap_policy_server);
            {
                char policy_server_ipv4_class[CF_BUFSIZE];
                snprintf(policy_server_ipv4_class, CF_MAXVARSIZE, "ipv4_%s", canonified_bootstrap_policy_server);
                am_policy_server |= NULL != EvalContextClassGet(ctx, NULL, policy_server_ipv4_class);
            }

            if (am_policy_server)
            {
                Log(LOG_LEVEL_INFO, "Assuming role as policy server, with policy distribution point at %s", GetMasterDir());
                EvalContextClassPutHard(ctx, "am_policy_hub", "source=bootstrap");

                if (!MasterfileExists(GetMasterDir()))
                {
                    Log(LOG_LEVEL_ERR, "In order to bootstrap as a policy server, the file '%s/promises.cf' must exist.", GetMasterDir());
                    exit(EXIT_FAILURE);
                }
            }
            else
            {
                Log(LOG_LEVEL_INFO, "Not assuming role as policy server");
            }

            WriteAmPolicyHubFile(CFWORKDIR, am_policy_server);
        }

        WritePolicyServerFile(GetWorkDir(), config->agent_specific.agent.bootstrap_policy_server);
        SetPolicyServer(ctx, config->agent_specific.agent.bootstrap_policy_server);
        /* FIXME: Why it is called here? Can't we move both invocations to before if? */
        UpdateLastPolicyUpdateTime(ctx);
        Log(LOG_LEVEL_INFO, "Bootstrapping to '%s'", POLICY_SERVER);
    }
    else
    {
        char *existing_policy_server = ReadPolicyServerFile(GetWorkDir());
        if (existing_policy_server)
        {
            Log(LOG_LEVEL_VERBOSE, "This agent is bootstrapped to '%s'", existing_policy_server);
            SetPolicyServer(ctx, existing_policy_server);
            free(existing_policy_server);
            UpdateLastPolicyUpdateTime(ctx);
        }
        else
        {
            Log(LOG_LEVEL_VERBOSE, "This agent is not bootstrapped");
            return;
        }

        if (GetAmPolicyHub(GetWorkDir()))
        {
            EvalContextClassPutHard(ctx, "am_policy_hub", "source=bootstrap,deprecated,alias=policy_server");
            Log(LOG_LEVEL_VERBOSE, "Additional class defined: am_policy_hub");
            EvalContextClassPutHard(ctx, "policy_server", "inventory,attribute_name=CFEngine roles,source=bootstrap");
            Log(LOG_LEVEL_VERBOSE, "Additional class defined: policy_server");
        }
    }
}
Exemple #8
0
void GenericAgentDiscoverContext(EvalContext *ctx, GenericAgentConfig *config)
{
    GenericAgentInitialize(ctx, config);

    SetReferenceTime(ctx, true);
    SetStartTime();
    SanitizeEnvironment();

    THIS_AGENT_TYPE = config->agent_type;
    EvalContextHeapAddHard(ctx, CF_AGENTTYPES[config->agent_type]);

    GenericAgentSetDefaultDigest(&CF_DEFAULT_DIGEST, &CF_DEFAULT_DIGEST_LEN);

    GetNameInfo3(ctx, config->agent_type);
    GetInterfacesInfo(ctx);

    Get3Environment(ctx, config->agent_type);
    BuiltinClasses(ctx);
    OSClasses(ctx);

    EvalContextHeapPersistentLoadAll(ctx);
    LoadSystemConstants(ctx);

    if (config->agent_type == AGENT_TYPE_AGENT && config->agent_specific.agent.bootstrap_policy_server)
    {
        if (!RemoveAllExistingPolicyInInputs(GetWorkDir()))
        {
            Log(LOG_LEVEL_ERR, "Error removing existing input files prior to bootstrap");
            exit(EXIT_FAILURE);
        }

        if (!WriteBuiltinFailsafePolicy(GetWorkDir()))
        {
            Log(LOG_LEVEL_ERR, "Error writing builtin failsafe to inputs prior to bootstrap");
            exit(EXIT_FAILURE);
        }

        bool am_policy_server = false;
        {
            const char *canonified_bootstrap_policy_server = CanonifyName(config->agent_specific.agent.bootstrap_policy_server);
            am_policy_server = IsDefinedClass(ctx, canonified_bootstrap_policy_server, NULL);
            {
                char policy_server_ipv4_class[CF_BUFSIZE];
                snprintf(policy_server_ipv4_class, CF_MAXVARSIZE, "ipv4_%s", canonified_bootstrap_policy_server);
                am_policy_server |= IsDefinedClass(ctx, policy_server_ipv4_class, NULL);
            }

            if (am_policy_server)
            {
                Log(LOG_LEVEL_INFO, "Assuming role as policy server, with policy distribution point at %s/masterfiles", GetWorkDir());
                EvalContextHeapAddHard(ctx, "am_policy_hub");

                if (!MasterfileExists(GetWorkDir()))
                {
                    Log(LOG_LEVEL_ERR, "In order to bootstrap as a policy server, the file '%s/masterfiles/promises.cf' must exist.", GetWorkDir());
                    exit(EXIT_FAILURE);
                }
            }
            else
            {
                Log(LOG_LEVEL_INFO, "Not assuming role as policy server");
            }

            WriteAmPolicyHubFile(CFWORKDIR, am_policy_server);
        }

        WritePolicyServerFile(GetWorkDir(), config->agent_specific.agent.bootstrap_policy_server);
        SetPolicyServer(ctx, config->agent_specific.agent.bootstrap_policy_server);
        Log(LOG_LEVEL_INFO, "Bootstrapping to '%s'", POLICY_SERVER);
    }
    else
    {
        char *existing_policy_server = ReadPolicyServerFile(GetWorkDir());
        if (existing_policy_server)
        {
            Log(LOG_LEVEL_VERBOSE, "This agent is bootstrapped to '%s'", existing_policy_server);
        }
        else
        {
            Log(LOG_LEVEL_VERBOSE, "This agent is not bootstrapped");
        }
        SetPolicyServer(ctx, existing_policy_server);

        if (GetAmPolicyHub(GetWorkDir()))
        {
            EvalContextHeapAddHard(ctx, "am_policy_hub");  // DEPRECATED: use policy_server instead
            Log(LOG_LEVEL_VERBOSE, "Additional class defined: am_policy_hub");
            EvalContextHeapAddHard(ctx, "policy_server");
            Log(LOG_LEVEL_VERBOSE, "Additional class defined: policy_server");
        }
    }
}
Exemple #9
0
/**
 * @return true the error is not so severe that we must stop
 */
bool LoadSecretKeys(const char *policy_server)
{
    static char *passphrase = "Cfengine passphrase";

    {
        FILE *fp = fopen(PrivateKeyFile(GetWorkDir()), "r");
        if (!fp)
        {
            Log(LOG_LEVEL_INFO, "Couldn't find a private key at '%s', use cf-key to get one. (fopen: %s)", PrivateKeyFile(GetWorkDir()), GetErrorStr());
            return true;
        }

        if ((PRIVKEY = PEM_read_RSAPrivateKey(fp, (RSA **) NULL, NULL, passphrase)) == NULL)
        {
            unsigned long err = ERR_get_error();
            Log(LOG_LEVEL_ERR, "Error reading private key. (PEM_read_RSAPrivateKey: %s)", ERR_reason_error_string(err));
            PRIVKEY = NULL;
            fclose(fp);
            return true;
        }

        fclose(fp);
        Log(LOG_LEVEL_VERBOSE, "Loaded private key at '%s'", PrivateKeyFile(GetWorkDir()));
    }

    {
        FILE *fp = fopen(PublicKeyFile(GetWorkDir()), "r");
        if (!fp)
        {
            Log(LOG_LEVEL_ERR, "Couldn't find a public key at '%s', use cf-key to get one (fopen: %s)", PublicKeyFile(GetWorkDir()), GetErrorStr());
            return true;
        }

        if ((PUBKEY = PEM_read_RSAPublicKey(fp, NULL, NULL, passphrase)) == NULL)
        {
            unsigned long err = ERR_get_error();
            Log(LOG_LEVEL_ERR, "Error reading public key at '%s'. (PEM_read_RSAPublicKey: %s)", PublicKeyFile(GetWorkDir()), ERR_reason_error_string(err));
            PUBKEY = NULL;
            fclose(fp);
            return true;
        }

        Log(LOG_LEVEL_VERBOSE, "Loaded public key '%s'", PublicKeyFile(GetWorkDir()));
        fclose(fp);
    }

    if ((BN_num_bits(PUBKEY->e) < 2) || (!BN_is_odd(PUBKEY->e)))
    {
        Log(LOG_LEVEL_ERR, "The public key RSA exponent is too small or not odd");
        return false;
    }

    if (GetAmPolicyHub(CFWORKDIR))
    {
        unsigned char digest[EVP_MAX_MD_SIZE + 1];

        char dst_public_key_filename[CF_BUFSIZE] = "";
        {
            char buffer[EVP_MAX_MD_SIZE * 4];
            HashPubKey(PUBKEY, digest, CF_DEFAULT_DIGEST);
            snprintf(dst_public_key_filename, CF_MAXVARSIZE, "%s/ppkeys/%s-%s.pub", CFWORKDIR, "root", HashPrintSafe(CF_DEFAULT_DIGEST, digest, buffer));
            MapName(dst_public_key_filename);
        }

        struct stat sb;
        if ((stat(dst_public_key_filename, &sb) == -1))
        {
            char src_public_key_filename[CF_BUFSIZE] = "";
            snprintf(src_public_key_filename, CF_MAXVARSIZE, "%s/ppkeys/localhost.pub", CFWORKDIR);
            MapName(src_public_key_filename);

            // copy localhost.pub to root-HASH.pub on policy server
            if (!LinkOrCopy(src_public_key_filename, dst_public_key_filename, false))
            {
                Log(LOG_LEVEL_ERR, "Unable to copy policy server's own public key from '%s' to '%s'", src_public_key_filename, dst_public_key_filename);
            }

            if (policy_server)
            {
                LastSaw(policy_server, digest, LAST_SEEN_ROLE_CONNECT);
            }
        }
    }

    return true;
}
Exemple #10
0
void GenericAgentDiscoverContext(EvalContext *ctx, GenericAgentConfig *config)
{
    strcpy(VPREFIX, "");

    Log(LOG_LEVEL_VERBOSE, " %s", NameVersion());
    Banner("Initialization preamble");

    GenericAgentSetDefaultDigest(&CF_DEFAULT_DIGEST, &CF_DEFAULT_DIGEST_LEN);
    GenericAgentInitialize(ctx, config);

    time_t t = SetReferenceTime();
    UpdateTimeClasses(ctx, t);
    SanitizeEnvironment();

    THIS_AGENT_TYPE = config->agent_type;
    LoggingSetAgentType(CF_AGENTTYPES[config->agent_type]);
    EvalContextClassPutHard(ctx, CF_AGENTTYPES[config->agent_type],
                            "cfe_internal,source=agent");

    DetectEnvironment(ctx);

    EvalContextHeapPersistentLoadAll(ctx);
    LoadSystemConstants(ctx);

    const char *bootstrap_arg =
        config->agent_specific.agent.bootstrap_policy_server;

    /* Are we bootstrapping the agent? */
    if (config->agent_type == AGENT_TYPE_AGENT && bootstrap_arg != NULL)
    {
        EvalContextClassPutHard(ctx, "bootstrap_mode", "source=environment");

        if (!RemoveAllExistingPolicyInInputs(GetInputDir()))
        {
            Log(LOG_LEVEL_ERR,
                "Error removing existing input files prior to bootstrap");
            exit(EXIT_FAILURE);
        }

        if (!WriteBuiltinFailsafePolicy(GetInputDir()))
        {
            Log(LOG_LEVEL_ERR,
                "Error writing builtin failsafe to inputs prior to bootstrap");
            exit(EXIT_FAILURE);
        }

        char canonified_ipaddr[strlen(bootstrap_arg) + 1];
        StringCanonify(canonified_ipaddr, bootstrap_arg);

        bool am_policy_server =
            EvalContextClassGet(ctx, NULL, canonified_ipaddr) != NULL;

        if (am_policy_server)
        {
            Log(LOG_LEVEL_INFO, "Assuming role as policy server,"
                " with policy distribution point at: %s", GetMasterDir());
            MarkAsPolicyServer(ctx);

            if (!MasterfileExists(GetMasterDir()))
            {
                Log(LOG_LEVEL_ERR, "In order to bootstrap as a policy server,"
                    " the file '%s/promises.cf' must exist.", GetMasterDir());
                exit(EXIT_FAILURE);
            }

            CheckAndSetHAState(GetWorkDir(), ctx);
        }
        else
        {
            Log(LOG_LEVEL_INFO, "Assuming role as regular client,"
                " bootstrapping to policy server: %s", bootstrap_arg);

            if (config->agent_specific.agent.bootstrap_trust_server)
            {
                EvalContextClassPutHard(ctx, "trust_server", "source=agent");
                Log(LOG_LEVEL_NOTICE,
                    "Bootstrap mode: implicitly trusting server, "
                    "use --trust-server=no if server trust is already established");
            }
        }

        WriteAmPolicyHubFile(am_policy_server);

        WritePolicyServerFile(GetWorkDir(), bootstrap_arg);
        SetPolicyServer(ctx, bootstrap_arg);

        /* FIXME: Why it is called here? Can't we move both invocations to before if? */
        UpdateLastPolicyUpdateTime(ctx);
    }
    else
    {
        char *existing_policy_server = ReadPolicyServerFile(GetWorkDir());
        if (existing_policy_server)
        {
            Log(LOG_LEVEL_VERBOSE, "This agent is bootstrapped to: %s",
                existing_policy_server);
            SetPolicyServer(ctx, existing_policy_server);
            free(existing_policy_server);
            UpdateLastPolicyUpdateTime(ctx);
        }
        else
        {
            Log(LOG_LEVEL_VERBOSE, "This agent is not bootstrapped -"
                " can't find policy_server.dat in: %s", GetWorkDir());
            return;
        }

        if (GetAmPolicyHub())
        {
            MarkAsPolicyServer(ctx);

            /* Should this go in MarkAsPolicyServer() ? */
            CheckAndSetHAState(GetWorkDir(), ctx);
        }
    }
}
AgentDiagnosticsResult AgentDiagnosticsCheckAmPolicyServer(ARG_UNUSED const char *workdir)
{
    bool am_policy_server = GetAmPolicyHub();
    return AgentDiagnosticsResultNew(am_policy_server,
                                     am_policy_server ? xstrdup("Acting as a policy server") : xstrdup("Not acting as a policy server"));
}
Exemple #12
0
DBPriv *DBPrivOpenDB(const char *dbpath, dbid id)
{
    DBPriv *db = xcalloc(1, sizeof(DBPriv));
    MDB_txn *txn = NULL;
    int rc;

    rc = pthread_key_create(&db->txn_key, &DestroyTransaction);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not create transaction key. (pthread_key_create: '%s')",
            GetErrorStrFromCode(rc));
        free(db);
        return NULL;
    }

    rc = mdb_env_create(&db->env);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not create handle for database %s: %s",
              dbpath, mdb_strerror(rc));
        goto err;
    }
    rc = mdb_env_set_mapsize(db->env, LMDB_MAXSIZE);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not set mapsize for database %s: %s",
              dbpath, mdb_strerror(rc));
        goto err;
    }
    if (DB_MAX_READERS > 0)
    {
        rc = mdb_env_set_maxreaders(db->env, DB_MAX_READERS);
        if (rc)
        {
            Log(LOG_LEVEL_ERR, "Could not set maxreaders for database %s: %s",
                dbpath, mdb_strerror(rc));
            goto err;
        }
    }

    unsigned int open_flags = MDB_NOSUBDIR;
    if (id == dbid_locks
        || (GetAmPolicyHub() && id == dbid_lastseen))
    {
        open_flags |= MDB_NOSYNC;
    }

#ifdef __hpux
    /*
     * On HP-UX, a unified file cache was not introduced until version 11.31.
     * This means that on 11.23 there are separate file caches for mmap()'ed
     * files and open()'ed files. When these two are mixed, changes made using
     * one mode won't be immediately seen by the other mode, which is an
     * assumption LMDB is relying on. The MDB_WRITEMAP flag causes LMDB to use
     * mmap() only, so that we stay within one file cache.
     */
    open_flags |= MDB_WRITEMAP;
#endif

    rc = mdb_env_open(db->env, dbpath, open_flags, 0644);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not open database %s: %s",
              dbpath, mdb_strerror(rc));
        goto err;
    }
    if (DB_MAX_READERS > 0)
    {
        int max_readers;
        rc = mdb_env_get_maxreaders(db->env, &max_readers);
        if (rc)
        {
            Log(LOG_LEVEL_ERR, "Could not get maxreaders for database %s: %s",
                dbpath, mdb_strerror(rc));
            goto err;
        }
        if (max_readers < DB_MAX_READERS)
        {
            // LMDB will only reinitialize maxreaders if no database handles are
            // open, including in other processes, which is how we might end up
            // here.
            Log(LOG_LEVEL_VERBOSE, "Failed to set LMDB max reader limit on database '%s', "
                "consider restarting CFEngine",
                dbpath);
        }
    }
    rc = mdb_txn_begin(db->env, NULL, MDB_RDONLY, &txn);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not open database txn %s: %s",
              dbpath, mdb_strerror(rc));
        goto err;
    }
    rc = mdb_open(txn, NULL, 0, &db->dbi);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not open database dbi %s: %s",
              dbpath, mdb_strerror(rc));
        mdb_txn_abort(txn);
        goto err;
    }
    rc = mdb_txn_commit(txn);
    if (rc)
    {
        Log(LOG_LEVEL_ERR, "Could not commit database dbi %s: %s",
              dbpath, mdb_strerror(rc));
        goto err;
    }

    return db;

err:
    if (db->env)
    {
        mdb_env_close(db->env);
    }
    pthread_key_delete(db->txn_key);
    free(db);
    if (rc == MDB_INVALID)
    {
        return DB_PRIV_DATABASE_BROKEN;
    }
    return NULL;
}