Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

crypto.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1996-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * Some parts of this code originally written by Adam Stubblefield
00008  * -- [email protected]
00009  *
00010  * $Id: crypto.c,v 12.5 2005/07/20 16:50:56 bostic Exp $
00011  */
00012 
00013 #include "db_config.h"
00014 
00015 #ifndef NO_SYSTEM_INCLUDES
00016 #include <string.h>
00017 #endif
00018 
00019 #include "db_int.h"
00020 #include "dbinc/db_page.h"
00021 #include "dbinc/crypto.h"
00022 
00023 /*
00024  * __crypto_region_init --
00025  *      Initialize crypto.
00026  */
00027 int
00028 __crypto_region_init(dbenv)
00029         DB_ENV *dbenv;
00030 {
00031         REGENV *renv;
00032         REGINFO *infop;
00033         CIPHER *cipher;
00034         DB_CIPHER *db_cipher;
00035         char *sh_passwd;
00036         int ret;
00037 
00038         db_cipher = dbenv->crypto_handle;
00039 
00040         ret = 0;
00041         infop = dbenv->reginfo;
00042         renv = infop->primary;
00043         if (renv->cipher_off == INVALID_ROFF) {
00044                 if (!CRYPTO_ON(dbenv))
00045                         return (0);
00046                 if (!F_ISSET(infop, REGION_CREATE)) {
00047                         __db_err(dbenv,
00048     "Joining non-encrypted environment with encryption key");
00049                         return (EINVAL);
00050                 }
00051                 if (F_ISSET(db_cipher, CIPHER_ANY)) {
00052                         __db_err(dbenv, "Encryption algorithm not supplied");
00053                         return (EINVAL);
00054                 }
00055                 /*
00056                  * Must create the shared information.  We need: Shared cipher
00057                  * information that contains the passwd.  After we copy the
00058                  * passwd, we smash and free the one in the dbenv.
00059                  */
00060                 if ((ret =
00061                     __db_shalloc(infop, sizeof(CIPHER), 0, &cipher)) != 0)
00062                         return (ret);
00063                 memset(cipher, 0, sizeof(*cipher));
00064                 if ((ret = __db_shalloc(
00065                     infop, dbenv->passwd_len, 0, &sh_passwd)) != 0) {
00066                         __db_shalloc_free(infop, cipher);
00067                         return (ret);
00068                 }
00069                 memset(sh_passwd, 0, dbenv->passwd_len);
00070                 cipher->passwd = R_OFFSET(infop, sh_passwd);
00071                 cipher->passwd_len = dbenv->passwd_len;
00072                 cipher->flags = db_cipher->alg;
00073                 memcpy(sh_passwd, dbenv->passwd, cipher->passwd_len);
00074                 renv->cipher_off = R_OFFSET(infop, cipher);
00075         } else {
00076                 if (!CRYPTO_ON(dbenv)) {
00077                         __db_err(dbenv,
00078                     "Encrypted environment: no encryption key supplied");
00079                         return (EINVAL);
00080                 }
00081                 cipher = R_ADDR(infop, renv->cipher_off);
00082                 sh_passwd = R_ADDR(infop, cipher->passwd);
00083                 if ((cipher->passwd_len != dbenv->passwd_len) ||
00084                     memcmp(dbenv->passwd, sh_passwd, cipher->passwd_len) != 0) {
00085                         __db_err(dbenv, "Invalid password");
00086                         return (EPERM);
00087                 }
00088                 if (!F_ISSET(db_cipher, CIPHER_ANY) &&
00089                     db_cipher->alg != cipher->flags) {
00090                         __db_err(dbenv,
00091     "Environment encrypted using a different algorithm");
00092                         return (EINVAL);
00093                 }
00094                 if (F_ISSET(db_cipher, CIPHER_ANY))
00095                         /*
00096                          * We have CIPHER_ANY and we are joining the existing
00097                          * env.  Setup our cipher structure for whatever
00098                          * algorithm this env has.
00099                          */
00100                         if ((ret = __crypto_algsetup(dbenv, db_cipher,
00101                             cipher->flags, 0)) != 0)
00102                                 return (ret);
00103         }
00104         ret = db_cipher->init(dbenv, db_cipher);
00105 
00106         /*
00107          * On success, no matter if we allocated it or are using the already
00108          * existing one, we are done with the passwd in the dbenv.  We smash
00109          * N-1 bytes so that we don't overwrite the nul.
00110          */
00111         memset(dbenv->passwd, 0xff, dbenv->passwd_len-1);
00112         __os_free(dbenv, dbenv->passwd);
00113         dbenv->passwd = NULL;
00114         dbenv->passwd_len = 0;
00115 
00116         return (ret);
00117 }
00118 
00119 /*
00120  * __crypto_dbenv_close --
00121  *      Crypto-specific destruction of DB_ENV structure.
00122  *
00123  * PUBLIC: int __crypto_dbenv_close __P((DB_ENV *));
00124  */
00125 int
00126 __crypto_dbenv_close(dbenv)
00127         DB_ENV *dbenv;
00128 {
00129         DB_CIPHER *db_cipher;
00130         int ret;
00131 
00132         ret = 0;
00133         db_cipher = dbenv->crypto_handle;
00134         if (dbenv->passwd != NULL) {
00135                 memset(dbenv->passwd, 0xff, dbenv->passwd_len-1);
00136                 __os_free(dbenv, dbenv->passwd);
00137                 dbenv->passwd = NULL;
00138         }
00139         if (!CRYPTO_ON(dbenv))
00140                 return (0);
00141         if (!F_ISSET(db_cipher, CIPHER_ANY))
00142                 ret = db_cipher->close(dbenv, db_cipher->data);
00143         __os_free(dbenv, db_cipher);
00144         return (ret);
00145 }
00146 
00147 /*
00148  * __crypto_region_destroy --
00149  *      Destroy any system resources allocated in the primary region.
00150  *
00151  * PUBLIC: int __crypto_region_destroy __P((DB_ENV *));
00152  */
00153 int
00154 __crypto_region_destroy(dbenv)
00155         DB_ENV *dbenv;
00156 {
00157         CIPHER *cipher;
00158         REGENV *renv;
00159         REGINFO *infop;
00160 
00161         infop = dbenv->reginfo;
00162         renv = infop->primary;
00163         if (renv->cipher_off != INVALID_ROFF) {
00164                 cipher = R_ADDR(infop, renv->cipher_off);
00165                 __db_shalloc_free(infop, R_ADDR(infop, cipher->passwd));
00166                 __db_shalloc_free(infop, cipher);
00167         }
00168         return (0);
00169 }
00170 
00171 /*
00172  * __crypto_algsetup --
00173  *      Given a db_cipher structure and a valid algorithm flag, call
00174  * the specific algorithm setup function.
00175  *
00176  * PUBLIC: int __crypto_algsetup __P((DB_ENV *, DB_CIPHER *, u_int32_t, int));
00177  */
00178 int
00179 __crypto_algsetup(dbenv, db_cipher, alg, do_init)
00180         DB_ENV *dbenv;
00181         DB_CIPHER *db_cipher;
00182         u_int32_t alg;
00183         int do_init;
00184 {
00185         int ret;
00186 
00187         ret = 0;
00188         if (!CRYPTO_ON(dbenv)) {
00189                 __db_err(dbenv, "No cipher structure given");
00190                 return (EINVAL);
00191         }
00192         F_CLR(db_cipher, CIPHER_ANY);
00193         switch (alg) {
00194         case CIPHER_AES:
00195                 db_cipher->alg = CIPHER_AES;
00196                 ret = __aes_setup(dbenv, db_cipher);
00197                 break;
00198         default:
00199                 __db_panic(dbenv, EINVAL);
00200                 /* NOTREACHED */
00201         }
00202         if (do_init)
00203                 ret = db_cipher->init(dbenv, db_cipher);
00204         return (ret);
00205 }
00206 
00207 /*
00208  * __crypto_decrypt_meta --
00209  *      Perform decryption on a metapage if needed.
00210  *
00211  * PUBLIC:  int __crypto_decrypt_meta __P((DB_ENV *, DB *, u_int8_t *, int));
00212  */
00213 int
00214 __crypto_decrypt_meta(dbenv, dbp, mbuf, do_metachk)
00215         DB_ENV *dbenv;
00216         DB *dbp;
00217         u_int8_t *mbuf;
00218         int do_metachk;
00219 {
00220         DB_CIPHER *db_cipher;
00221         DB dummydb;
00222         DBMETA *meta;
00223         size_t pg_off;
00224         int ret;
00225         u_int8_t *iv;
00226 
00227         /*
00228          * If we weren't given a dbp, we just want to decrypt the page on
00229          * behalf of some internal subsystem, not on behalf of a user with
00230          * a dbp.  Therefore, set up a dummy dbp so that the call to
00231          * P_OVERHEAD below works.
00232          */
00233         if (dbp == NULL) {
00234                 memset(&dummydb, 0, sizeof(DB));
00235                 dbp = &dummydb;
00236         }
00237 
00238         ret = 0;
00239         meta = (DBMETA *)mbuf;
00240 
00241         /*
00242          * !!!
00243          * We used an "unused" field in the meta-data page to flag whether or
00244          * not the database is encrypted.  Unfortunately, that unused field
00245          * was used in Berkeley DB releases before 3.0 (for example, 2.7.7).
00246          * It would have been OK, except encryption doesn't follow the usual
00247          * rules of "upgrade before doing anything else", we check encryption
00248          * before checking for old versions of the database.
00249          *
00250          * We don't have to check Btree databases -- before 3.0, the field of
00251          * interest was the bt_maxkey field (which was never supported and has
00252          * since been removed).
00253          *
00254          * Ugly check to jump out if this format is older than what we support.
00255          * It assumes no encrypted page will have an unencrypted magic number,
00256          * but that seems relatively safe.  [#10920]
00257          */
00258         if (meta->magic == DB_HASHMAGIC && meta->version <= 5)
00259                 return (0);
00260 
00261         /*
00262          * Meta-pages may be encrypted for DBMETASIZE bytes.  If we have a
00263          * non-zero IV (that is written after encryption) then we decrypt (or
00264          * error if the user isn't set up for security).  We guarantee that
00265          * the IV space on non-encrypted pages will be zero and a zero-IV is
00266          * illegal for encryption.  Therefore any non-zero IV means an
00267          * encrypted database.  This basically checks the passwd on the file
00268          * if we cannot find a good magic number.  We walk through all the
00269          * algorithms we know about attempting to decrypt (and possibly
00270          * byteswap).
00271          *
00272          * !!!
00273          * All method meta pages have the IV and checksum at the exact same
00274          * location, but not in DBMETA, use BTMETA.
00275          */
00276         if (meta->encrypt_alg != 0) {
00277                 db_cipher = (DB_CIPHER *)dbenv->crypto_handle;
00278                 if (!F_ISSET(dbp, DB_AM_ENCRYPT)) {
00279                         if (!CRYPTO_ON(dbenv)) {
00280                                 __db_err(dbenv,
00281     "Encrypted database: no encryption flag specified");
00282                                 return (EINVAL);
00283                         }
00284                         /*
00285                          * User has a correct, secure env, but has encountered
00286                          * a database in that env that is secure, but user
00287                          * didn't dbp->set_flags.  Since it is existing, use
00288                          * encryption if it is that way already.
00289                          */
00290                         F_SET(dbp, DB_AM_ENCRYPT|DB_AM_CHKSUM);
00291                 }
00292                 /*
00293                  * This was checked in set_flags when DB_AM_ENCRYPT was set.
00294                  * So it better still be true here.
00295                  */
00296                 DB_ASSERT(CRYPTO_ON(dbenv));
00297                 if (!F_ISSET(db_cipher, CIPHER_ANY) &&
00298                     meta->encrypt_alg != db_cipher->alg) {
00299                         __db_err(dbenv,
00300                             "Database encrypted using a different algorithm");
00301                         return (EINVAL);
00302                 }
00303                 DB_ASSERT(F_ISSET(dbp, DB_AM_CHKSUM));
00304                 iv = ((BTMETA *)mbuf)->iv;
00305                 /*
00306                  * For ALL pages, we do not encrypt the beginning of the page
00307                  * that contains overhead information.  This is true of meta
00308                  * and all other pages.
00309                  */
00310                 pg_off = P_OVERHEAD(dbp);
00311 alg_retry:
00312                 /*
00313                  * If they asked for a specific algorithm, then
00314                  * use it.  Otherwise walk through those we know.
00315                  */
00316                 if (!F_ISSET(db_cipher, CIPHER_ANY)) {
00317                         if (do_metachk && (ret = db_cipher->decrypt(dbenv,
00318                             db_cipher->data, iv, mbuf + pg_off,
00319                             DBMETASIZE - pg_off)))
00320                                 return (ret);
00321                         if (((BTMETA *)meta)->crypto_magic !=
00322                             meta->magic) {
00323                                 __db_err(dbenv, "Invalid password");
00324                                 return (EINVAL);
00325                         }
00326                         /*
00327                          * Success here.  The algorithm asked for and the one
00328                          * on the file match.  We've just decrypted the meta
00329                          * page and checked the magic numbers.  They match,
00330                          * indicating the password is right.  All is right
00331                          * with the world.
00332                          */
00333                         return (0);
00334                 }
00335                 /*
00336                  * If we get here, CIPHER_ANY must be set.
00337                  */
00338                 ret = __crypto_algsetup(dbenv, db_cipher, meta->encrypt_alg, 1);
00339                 goto alg_retry;
00340         } else if (F_ISSET(dbp, DB_AM_ENCRYPT)) {
00341                 /*
00342                  * They gave us a passwd, but the database is not encrypted.
00343                  * This is an error.  We do NOT want to silently allow them
00344                  * to write data in the clear when the user set up and expects
00345                  * encrypted data.
00346                  *
00347                  * This covers at least the following scenario.
00348                  * 1.  User creates and sets up an encrypted database.
00349                  * 2.  Attacker cannot read the actual data in the database
00350                  * because it is encrypted, but can remove/replace the file
00351                  * with an empty, unencrypted database file.
00352                  * 3.  User sets encryption and we get to this code now.
00353                  * If we allowed the file to be used in the clear since
00354                  * it is that way on disk, the user would unsuspectingly
00355                  * write sensitive data in the clear.
00356                  * 4.  Attacker reads data that user thought was encrypted.
00357                  *
00358                  * Therefore, asking for encryption with a database that
00359                  * was not encrypted is an error.
00360                  */
00361                 __db_err(dbenv,
00362                     "Unencrypted database with a supplied encryption key");
00363                 return (EINVAL);
00364         }
00365         return (ret);
00366 }
00367 
00368 /*
00369  * __crypto_set_passwd --
00370  *      Get the password from the shared region; and set it in a new
00371  * environment handle.  Use this to duplicate environment handles.
00372  *
00373  * PUBLIC: int __crypto_set_passwd __P((DB_ENV *, DB_ENV *));
00374  */
00375 int
00376 __crypto_set_passwd(dbenv_src, dbenv_dest)
00377         DB_ENV *dbenv_src, *dbenv_dest;
00378 {
00379         CIPHER *cipher;
00380         REGENV *renv;
00381         REGINFO *infop;
00382         char *sh_passwd;
00383         int ret;
00384 
00385         ret = 0;
00386         infop = dbenv_src->reginfo;
00387         renv = infop->primary;
00388 
00389         DB_ASSERT(CRYPTO_ON(dbenv_src));
00390 
00391         cipher = R_ADDR(infop, renv->cipher_off);
00392         sh_passwd = R_ADDR(infop, cipher->passwd);
00393         return (__env_set_encrypt(dbenv_dest, sh_passwd, DB_ENCRYPT_AES));
00394 }

Generated on Sun Dec 25 12:14:17 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2