#include "db_config.h"
#include "db_int.h"
#include "dbinc/db_page.h"
#include "dbinc/crypto.h"
int
__crypto_region_init(env)
ENV *env;
{
CIPHER *cipher;
DB_CIPHER *db_cipher;
DB_ENV *dbenv;
REGENV *renv;
REGINFO *infop;
char *sh_passwd;
int ret;
dbenv = env->dbenv;
infop = env->reginfo;
renv = infop->primary;
db_cipher = env->crypto_handle;
ret = 0;
if (renv->cipher_off == INVALID_ROFF) {
if (!CRYPTO_ON(env))
return (0);
if (!F_ISSET(infop, REGION_CREATE)) {
__db_errx(env,
"Joining non-encrypted environment with encryption key");
return (EINVAL);
}
if (F_ISSET(db_cipher, CIPHER_ANY)) {
__db_errx(env, "Encryption algorithm not supplied");
return (EINVAL);
}
if ((ret = __env_alloc(infop, sizeof(CIPHER), &cipher)) != 0)
return (ret);
memset(cipher, 0, sizeof(*cipher));
if ((ret =
__env_alloc(infop, dbenv->passwd_len, &sh_passwd)) != 0) {
__env_alloc_free(infop, cipher);
return (ret);
}
memset(sh_passwd, 0, dbenv->passwd_len);
cipher->passwd = R_OFFSET(infop, sh_passwd);
cipher->passwd_len = dbenv->passwd_len;
cipher->flags = db_cipher->alg;
memcpy(sh_passwd, dbenv->passwd, cipher->passwd_len);
renv->cipher_off = R_OFFSET(infop, cipher);
} else {
if (!CRYPTO_ON(env)) {
__db_errx(env,
"Encrypted environment: no encryption key supplied");
return (EINVAL);
}
cipher = R_ADDR(infop, renv->cipher_off);
sh_passwd = R_ADDR(infop, cipher->passwd);
if ((cipher->passwd_len != dbenv->passwd_len) ||
memcmp(dbenv->passwd, sh_passwd, cipher->passwd_len) != 0) {
__db_errx(env, "Invalid password");
return (EPERM);
}
if (!F_ISSET(db_cipher, CIPHER_ANY) &&
db_cipher->alg != cipher->flags) {
__db_errx(env,
"Environment encrypted using a different algorithm");
return (EINVAL);
}
if (F_ISSET(db_cipher, CIPHER_ANY))
if ((ret = __crypto_algsetup(env, db_cipher,
cipher->flags, 0)) != 0)
return (ret);
}
ret = db_cipher->init(env, db_cipher);
memset(dbenv->passwd, 0xff, dbenv->passwd_len-1);
__os_free(env, dbenv->passwd);
dbenv->passwd = NULL;
dbenv->passwd_len = 0;
return (ret);
}
int
__crypto_env_close(env)
ENV *env;
{
DB_CIPHER *db_cipher;
DB_ENV *dbenv;
int ret;
dbenv = env->dbenv;
if (dbenv->passwd != NULL) {
memset(dbenv->passwd, 0xff, dbenv->passwd_len-1);
__os_free(env, dbenv->passwd);
dbenv->passwd = NULL;
}
if (!CRYPTO_ON(env))
return (0);
ret = 0;
db_cipher = env->crypto_handle;
if (!F_ISSET(db_cipher, CIPHER_ANY))
ret = db_cipher->close(env, db_cipher->data);
__os_free(env, db_cipher);
env->crypto_handle = NULL;
return (ret);
}
int
__crypto_env_refresh(env)
ENV *env;
{
CIPHER *cipher;
REGENV *renv;
REGINFO *infop;
if (F_ISSET(env, ENV_PRIVATE)) {
infop = env->reginfo;
renv = infop->primary;
if (renv->cipher_off != INVALID_ROFF) {
cipher = R_ADDR(infop, renv->cipher_off);
__env_alloc_free(infop, R_ADDR(infop, cipher->passwd));
__env_alloc_free(infop, cipher);
}
}
return (0);
}
int
__crypto_algsetup(env, db_cipher, alg, do_init)
ENV *env;
DB_CIPHER *db_cipher;
u_int32_t alg;
int do_init;
{
int ret;
ret = 0;
if (!CRYPTO_ON(env)) {
__db_errx(env, "No cipher structure given");
return (EINVAL);
}
F_CLR(db_cipher, CIPHER_ANY);
switch (alg) {
case CIPHER_AES:
db_cipher->alg = CIPHER_AES;
ret = __aes_setup(env, db_cipher);
break;
default:
ret = __env_panic(env, EINVAL);
break;
}
if (ret == 0 && do_init)
ret = db_cipher->init(env, db_cipher);
return (ret);
}
int
__crypto_decrypt_meta(env, dbp, mbuf, do_metachk)
ENV *env;
DB *dbp;
u_int8_t *mbuf;
int do_metachk;
{
DB dummydb;
DBMETA *meta;
DB_CIPHER *db_cipher;
size_t pg_off;
int ret;
u_int8_t *iv;
if (dbp == NULL) {
memset(&dummydb, 0, sizeof(DB));
dbp = &dummydb;
}
ret = 0;
meta = (DBMETA *)mbuf;
if (meta->magic == DB_HASHMAGIC && meta->version <= 5)
return (0);
if (meta->encrypt_alg != 0) {
db_cipher = env->crypto_handle;
if (!F_ISSET(dbp, DB_AM_ENCRYPT)) {
if (!CRYPTO_ON(env)) {
__db_errx(env,
"Encrypted database: no encryption flag specified");
return (EINVAL);
}
F_SET(dbp, DB_AM_ENCRYPT|DB_AM_CHKSUM);
}
DB_ASSERT(env, CRYPTO_ON(env));
if (!F_ISSET(db_cipher, CIPHER_ANY) &&
meta->encrypt_alg != db_cipher->alg) {
__db_errx(env,
"Database encrypted using a different algorithm");
return (EINVAL);
}
DB_ASSERT(env, F_ISSET(dbp, DB_AM_CHKSUM));
iv = ((BTMETA *)mbuf)->iv;
pg_off = P_OVERHEAD(dbp);
alg_retry:
if (!F_ISSET(db_cipher, CIPHER_ANY)) {
if (do_metachk && (ret = db_cipher->decrypt(env,
db_cipher->data, iv, mbuf + pg_off,
DBMETASIZE - pg_off)))
return (ret);
if (((BTMETA *)meta)->crypto_magic !=
meta->magic) {
__db_errx(env, "Invalid password");
return (EINVAL);
}
return (0);
}
ret = __crypto_algsetup(env, db_cipher, meta->encrypt_alg, 1);
goto alg_retry;
} else if (F_ISSET(dbp, DB_AM_ENCRYPT)) {
__db_errx(env,
"Unencrypted database with a supplied encryption key");
return (EINVAL);
}
return (ret);
}
int
__crypto_set_passwd(env_src, env_dest)
ENV *env_src, *env_dest;
{
CIPHER *cipher;
REGENV *renv;
REGINFO *infop;
char *sh_passwd;
infop = env_src->reginfo;
renv = infop->primary;
DB_ASSERT(env_src, CRYPTO_ON(env_src));
cipher = R_ADDR(infop, renv->cipher_off);
sh_passwd = R_ADDR(infop, cipher->passwd);
return (__env_set_encrypt(env_dest->dbenv, sh_passwd, DB_ENCRYPT_AES));
}