1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * Some parts of this code originally written by Adam Stubblefield
7 * -- astubble@rice.edu
8 *
9 * $Id: crypto.c,v 12.24 2008/01/08 20:58:09 bostic Exp $
10 */
11
12#include "db_config.h"
13
14#include "db_int.h"
15#include "dbinc/db_page.h"
16#include "dbinc/crypto.h"
17
18/*
19 * __crypto_region_init --
20 *	Initialize crypto.
21 */
22int
23__crypto_region_init(env)
24	ENV *env;
25{
26	CIPHER *cipher;
27	DB_CIPHER *db_cipher;
28	DB_ENV *dbenv;
29	REGENV *renv;
30	REGINFO *infop;
31	char *sh_passwd;
32	int ret;
33
34	dbenv = env->dbenv;
35	infop = env->reginfo;
36	renv = infop->primary;
37	db_cipher = env->crypto_handle;
38	ret = 0;
39
40	if (renv->cipher_off == INVALID_ROFF) {
41		if (!CRYPTO_ON(env))
42			return (0);
43		if (!F_ISSET(infop, REGION_CREATE)) {
44			__db_errx(env,
45    "Joining non-encrypted environment with encryption key");
46			return (EINVAL);
47		}
48		if (F_ISSET(db_cipher, CIPHER_ANY)) {
49			__db_errx(env, "Encryption algorithm not supplied");
50			return (EINVAL);
51		}
52		/*
53		 * Must create the shared information.  We need: Shared cipher
54		 * information that contains the passwd.  After we copy the
55		 * passwd, we smash and free the one in the env.
56		 */
57		if ((ret = __env_alloc(infop, sizeof(CIPHER), &cipher)) != 0)
58			return (ret);
59		memset(cipher, 0, sizeof(*cipher));
60		if ((ret =
61		    __env_alloc(infop, dbenv->passwd_len, &sh_passwd)) != 0) {
62			__env_alloc_free(infop, cipher);
63			return (ret);
64		}
65		memset(sh_passwd, 0, dbenv->passwd_len);
66		cipher->passwd = R_OFFSET(infop, sh_passwd);
67		cipher->passwd_len = dbenv->passwd_len;
68		cipher->flags = db_cipher->alg;
69		memcpy(sh_passwd, dbenv->passwd, cipher->passwd_len);
70		renv->cipher_off = R_OFFSET(infop, cipher);
71	} else {
72		if (!CRYPTO_ON(env)) {
73			__db_errx(env,
74		    "Encrypted environment: no encryption key supplied");
75			return (EINVAL);
76		}
77		cipher = R_ADDR(infop, renv->cipher_off);
78		sh_passwd = R_ADDR(infop, cipher->passwd);
79		if ((cipher->passwd_len != dbenv->passwd_len) ||
80		    memcmp(dbenv->passwd, sh_passwd, cipher->passwd_len) != 0) {
81			__db_errx(env, "Invalid password");
82			return (EPERM);
83		}
84		if (!F_ISSET(db_cipher, CIPHER_ANY) &&
85		    db_cipher->alg != cipher->flags) {
86			__db_errx(env,
87    "Environment encrypted using a different algorithm");
88			return (EINVAL);
89		}
90		if (F_ISSET(db_cipher, CIPHER_ANY))
91			/*
92			 * We have CIPHER_ANY and we are joining the existing
93			 * env.  Setup our cipher structure for whatever
94			 * algorithm this env has.
95			 */
96			if ((ret = __crypto_algsetup(env, db_cipher,
97			    cipher->flags, 0)) != 0)
98				return (ret);
99	}
100	ret = db_cipher->init(env, db_cipher);
101
102	/*
103	 * On success, no matter if we allocated it or are using the already
104	 * existing one, we are done with the passwd in the env.  We smash
105	 * N-1 bytes so that we don't overwrite the nul.
106	 */
107	memset(dbenv->passwd, 0xff, dbenv->passwd_len-1);
108	__os_free(env, dbenv->passwd);
109	dbenv->passwd = NULL;
110	dbenv->passwd_len = 0;
111
112	return (ret);
113}
114
115/*
116 * __crypto_env_close --
117 *	Crypto-specific destruction of ENV structure.
118 *
119 * PUBLIC: int __crypto_env_close __P((ENV *));
120 */
121int
122__crypto_env_close(env)
123	ENV *env;
124{
125	DB_CIPHER *db_cipher;
126	DB_ENV *dbenv;
127	int ret;
128
129	dbenv = env->dbenv;
130
131	if (dbenv->passwd != NULL) {
132		memset(dbenv->passwd, 0xff, dbenv->passwd_len-1);
133		__os_free(env, dbenv->passwd);
134		dbenv->passwd = NULL;
135	}
136
137	if (!CRYPTO_ON(env))
138		return (0);
139
140	ret = 0;
141	db_cipher = env->crypto_handle;
142	if (!F_ISSET(db_cipher, CIPHER_ANY))
143		ret = db_cipher->close(env, db_cipher->data);
144	__os_free(env, db_cipher);
145
146	env->crypto_handle = NULL;
147	return (ret);
148}
149
150/*
151 * __crypto_env_refresh --
152 *	Clean up after the crpto system on a close or failed open.
153 *
154 * PUBLIC: int __crypto_env_refresh __P((ENV *));
155 */
156int
157__crypto_env_refresh(env)
158	ENV *env;
159{
160	CIPHER *cipher;
161	REGENV *renv;
162	REGINFO *infop;
163
164	/*
165	 * If a private region, return the memory to the heap.  Not needed for
166	 * filesystem-backed or system shared memory regions, that memory isn't
167	 * owned by any particular process.
168	 */
169	if (F_ISSET(env, ENV_PRIVATE)) {
170		infop = env->reginfo;
171		renv = infop->primary;
172		if (renv->cipher_off != INVALID_ROFF) {
173			cipher = R_ADDR(infop, renv->cipher_off);
174			__env_alloc_free(infop, R_ADDR(infop, cipher->passwd));
175			__env_alloc_free(infop, cipher);
176		}
177	}
178	return (0);
179}
180
181/*
182 * __crypto_algsetup --
183 *	Given a db_cipher structure and a valid algorithm flag, call
184 * the specific algorithm setup function.
185 *
186 * PUBLIC: int __crypto_algsetup __P((ENV *, DB_CIPHER *, u_int32_t, int));
187 */
188int
189__crypto_algsetup(env, db_cipher, alg, do_init)
190	ENV *env;
191	DB_CIPHER *db_cipher;
192	u_int32_t alg;
193	int do_init;
194{
195	int ret;
196
197	ret = 0;
198	if (!CRYPTO_ON(env)) {
199		__db_errx(env, "No cipher structure given");
200		return (EINVAL);
201	}
202	F_CLR(db_cipher, CIPHER_ANY);
203	switch (alg) {
204	case CIPHER_AES:
205		db_cipher->alg = CIPHER_AES;
206		ret = __aes_setup(env, db_cipher);
207		break;
208	default:
209		ret = __env_panic(env, EINVAL);
210		break;
211	}
212	if (ret == 0 && do_init)
213		ret = db_cipher->init(env, db_cipher);
214	return (ret);
215}
216
217/*
218 * __crypto_decrypt_meta --
219 *	Perform decryption on a metapage if needed.
220 *
221 * PUBLIC:  int __crypto_decrypt_meta __P((ENV *, DB *, u_int8_t *, int));
222 */
223int
224__crypto_decrypt_meta(env, dbp, mbuf, do_metachk)
225	ENV *env;
226	DB *dbp;
227	u_int8_t *mbuf;
228	int do_metachk;
229{
230	DB dummydb;
231	DBMETA *meta;
232	DB_CIPHER *db_cipher;
233	size_t pg_off;
234	int ret;
235	u_int8_t *iv;
236
237	/*
238	 * If we weren't given a dbp, we just want to decrypt the page on
239	 * behalf of some internal subsystem, not on behalf of a user with
240	 * a dbp.  Therefore, set up a dummy dbp so that the call to
241	 * P_OVERHEAD below works.
242	 */
243	if (dbp == NULL) {
244		memset(&dummydb, 0, sizeof(DB));
245		dbp = &dummydb;
246	}
247
248	ret = 0;
249	meta = (DBMETA *)mbuf;
250
251	/*
252	 * !!!
253	 * We used an "unused" field in the meta-data page to flag whether or
254	 * not the database is encrypted.  Unfortunately, that unused field
255	 * was used in Berkeley DB releases before 3.0 (for example, 2.7.7).
256	 * It would have been OK, except encryption doesn't follow the usual
257	 * rules of "upgrade before doing anything else", we check encryption
258	 * before checking for old versions of the database.
259	 *
260	 * We don't have to check Btree databases -- before 3.0, the field of
261	 * interest was the bt_maxkey field (which was never supported and has
262	 * since been removed).
263	 *
264	 * Ugly check to jump out if this format is older than what we support.
265	 * This works because we do not encrypt the page header.
266	 */
267	if (meta->magic == DB_HASHMAGIC && meta->version <= 5)
268		return (0);
269
270	/*
271	 * Meta-pages may be encrypted for DBMETASIZE bytes.  If we have a
272	 * non-zero IV (that is written after encryption) then we decrypt (or
273	 * error if the user isn't set up for security).  We guarantee that
274	 * the IV space on non-encrypted pages will be zero and a zero-IV is
275	 * illegal for encryption.  Therefore any non-zero IV means an
276	 * encrypted database.  This basically checks the passwd on the file
277	 * if we cannot find a good magic number.  We walk through all the
278	 * algorithms we know about attempting to decrypt (and possibly
279	 * byteswap).
280	 *
281	 * !!!
282	 * All method meta pages have the IV and checksum at the exact same
283	 * location, but not in DBMETA, use BTMETA.
284	 */
285	if (meta->encrypt_alg != 0) {
286		db_cipher = env->crypto_handle;
287		if (!F_ISSET(dbp, DB_AM_ENCRYPT)) {
288			if (!CRYPTO_ON(env)) {
289				__db_errx(env,
290    "Encrypted database: no encryption flag specified");
291				return (EINVAL);
292			}
293			/*
294			 * User has a correct, secure env, but has encountered
295			 * a database in that env that is secure, but user
296			 * didn't dbp->set_flags.  Since it is existing, use
297			 * encryption if it is that way already.
298			 */
299			F_SET(dbp, DB_AM_ENCRYPT|DB_AM_CHKSUM);
300		}
301		/*
302		 * This was checked in set_flags when DB_AM_ENCRYPT was set.
303		 * So it better still be true here.
304		 */
305		DB_ASSERT(env, CRYPTO_ON(env));
306		if (!F_ISSET(db_cipher, CIPHER_ANY) &&
307		    meta->encrypt_alg != db_cipher->alg) {
308			__db_errx(env,
309			    "Database encrypted using a different algorithm");
310			return (EINVAL);
311		}
312		DB_ASSERT(env, F_ISSET(dbp, DB_AM_CHKSUM));
313		iv = ((BTMETA *)mbuf)->iv;
314		/*
315		 * For ALL pages, we do not encrypt the beginning of the page
316		 * that contains overhead information.  This is true of meta
317		 * and all other pages.
318		 */
319		pg_off = P_OVERHEAD(dbp);
320alg_retry:
321		/*
322		 * If they asked for a specific algorithm, then
323		 * use it.  Otherwise walk through those we know.
324		 */
325		if (!F_ISSET(db_cipher, CIPHER_ANY)) {
326			if (do_metachk && (ret = db_cipher->decrypt(env,
327			    db_cipher->data, iv, mbuf + pg_off,
328			    DBMETASIZE - pg_off)))
329				return (ret);
330			if (((BTMETA *)meta)->crypto_magic !=
331			    meta->magic) {
332				__db_errx(env, "Invalid password");
333				return (EINVAL);
334			}
335			/*
336			 * Success here.  The algorithm asked for and the one
337			 * on the file match.  We've just decrypted the meta
338			 * page and checked the magic numbers.  They match,
339			 * indicating the password is right.  All is right
340			 * with the world.
341			 */
342			return (0);
343		}
344		/*
345		 * If we get here, CIPHER_ANY must be set.
346		 */
347		ret = __crypto_algsetup(env, db_cipher, meta->encrypt_alg, 1);
348		goto alg_retry;
349	} else if (F_ISSET(dbp, DB_AM_ENCRYPT)) {
350		/*
351		 * They gave us a passwd, but the database is not encrypted.
352		 * This is an error.  We do NOT want to silently allow them
353		 * to write data in the clear when the user set up and expects
354		 * encrypted data.
355		 *
356		 * This covers at least the following scenario.
357		 * 1.  User creates and sets up an encrypted database.
358		 * 2.  Attacker cannot read the actual data in the database
359		 * because it is encrypted, but can remove/replace the file
360		 * with an empty, unencrypted database file.
361		 * 3.  User sets encryption and we get to this code now.
362		 * If we allowed the file to be used in the clear since
363		 * it is that way on disk, the user would unsuspectingly
364		 * write sensitive data in the clear.
365		 * 4.  Attacker reads data that user thought was encrypted.
366		 *
367		 * Therefore, asking for encryption with a database that
368		 * was not encrypted is an error.
369		 */
370		__db_errx(env,
371		    "Unencrypted database with a supplied encryption key");
372		return (EINVAL);
373	}
374	return (ret);
375}
376
377/*
378 * __crypto_set_passwd --
379 *	Get the password from the shared region; and set it in a new
380 * environment handle.  Use this to duplicate environment handles.
381 *
382 * PUBLIC: int __crypto_set_passwd __P((ENV *, ENV *));
383 */
384int
385__crypto_set_passwd(env_src, env_dest)
386	ENV *env_src, *env_dest;
387{
388	CIPHER *cipher;
389	REGENV *renv;
390	REGINFO *infop;
391	char *sh_passwd;
392
393	infop = env_src->reginfo;
394	renv = infop->primary;
395
396	DB_ASSERT(env_src, CRYPTO_ON(env_src));
397
398	cipher = R_ADDR(infop, renv->cipher_off);
399	sh_passwd = R_ADDR(infop, cipher->passwd);
400	return (__env_set_encrypt(env_dest->dbenv, sh_passwd, DB_ENCRYPT_AES));
401}
402