source: trunk/third/rpm/db/crypto/crypto.c @ 19079

Revision 19079, 9.0 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19078, which included commits to RCS files with non-trunk default branches.
Line 
1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996-2001
5 *      Sleepycat Software.  All rights reserved.
6 *
7 * Some parts of this code originally written by Adam Stubblefield
8 * - astubble@rice.edu
9 */
10
11#include "db_config.h"
12
13#ifndef lint
14static const char revid[] = "Id: crypto.c,v 1.19 2002/08/15 15:27:53 bostic Exp ";
15#endif /* not lint */
16
17#ifndef NO_SYSTEM_INCLUDES
18#include <string.h>
19#endif
20
21#include "db_int.h"
22#include "dbinc/db_page.h"
23#include "dbinc/crypto.h"
24
25/*
26 * __crypto_region_init --
27 *      Initialize crypto.
28 *
29 * PUBLIC: int __crypto_region_init __P((DB_ENV *));
30 */
31int
32__crypto_region_init(dbenv)
33        DB_ENV *dbenv;
34{
35        REGENV *renv;
36        REGINFO *infop;
37        CIPHER *cipher;
38        DB_CIPHER *db_cipher;
39        char *sh_passwd;
40        int ret;
41
42        db_cipher = dbenv->crypto_handle;
43
44        ret = 0;
45        infop = dbenv->reginfo;
46        renv = infop->primary;
47        MUTEX_LOCK(dbenv, &renv->mutex);
48        if (renv->cipher_off == INVALID_ROFF) {
49                if (!CRYPTO_ON(dbenv))
50                        goto out;
51                if (!F_ISSET(infop, REGION_CREATE)) {
52                        __db_err(dbenv,
53    "Joining non-encrypted environment with encryption key");
54                        ret = EINVAL;
55                        goto out;
56                }
57                if (F_ISSET(db_cipher, CIPHER_ANY)) {
58                        __db_err(dbenv, "Encryption algorithm not supplied");
59                        ret = EINVAL;
60                        goto out;
61                }
62                /*
63                 * Must create the shared information.  We need:
64                 * Shared cipher information that contains the passwd.
65                 * After we copy the passwd, we smash and free the one in the
66                 * dbenv.
67                 */
68                if ((ret = __db_shalloc(infop->addr,
69                    sizeof(CIPHER), MUTEX_ALIGN, &cipher)) != 0)
70                        goto out;
71                memset(cipher, 0, sizeof(*cipher));
72                if ((ret = __db_shalloc(infop->addr,
73                    dbenv->passwd_len, 0, &sh_passwd)) != 0) {
74                        __db_shalloc_free(infop->addr, cipher);
75                        goto out;
76                }
77                memset(sh_passwd, 0, dbenv->passwd_len);
78                cipher->passwd = R_OFFSET(infop, sh_passwd);
79                cipher->passwd_len = dbenv->passwd_len;
80                cipher->flags = db_cipher->alg;
81                memcpy(sh_passwd, dbenv->passwd, cipher->passwd_len);
82                renv->cipher_off = R_OFFSET(infop, cipher);
83        } else {
84                if (!CRYPTO_ON(dbenv)) {
85                        __db_err(dbenv,
86    "Encrypted environment: no encryption key supplied");
87                        ret = EINVAL;
88                        goto out;
89                }
90                cipher = R_ADDR(infop, renv->cipher_off);
91                sh_passwd = R_ADDR(infop, cipher->passwd);
92                if ((cipher->passwd_len != dbenv->passwd_len) ||
93                    memcmp(dbenv->passwd, sh_passwd, cipher->passwd_len) != 0) {
94                        __db_err(dbenv, "Invalid password");
95                        ret = EPERM;
96                        goto out;
97                }
98                if (!F_ISSET(db_cipher, CIPHER_ANY) &&
99                    db_cipher->alg != cipher->flags) {
100                        __db_err(dbenv,
101    "Environment encrypted using a different algorithm");
102                        ret = EINVAL;
103                        goto out;
104                }
105                if (F_ISSET(db_cipher, CIPHER_ANY))
106                        /*
107                         * We have CIPHER_ANY and we are joining the
108                         * existing env.  Setup our cipher structure
109                         * for whatever algorithm this env has.
110                         */
111                        if ((ret = __crypto_algsetup(dbenv, db_cipher,
112                            cipher->flags, 0)) != 0)
113                                goto out;
114        }
115        MUTEX_UNLOCK(dbenv, &renv->mutex);
116        ret = db_cipher->init(dbenv, db_cipher);
117
118        /*
119         * On success, no matter if we allocated it or are using the
120         * already existing one, we are done with the passwd in the dbenv.
121         * We smash N-1 bytes so that we don't overwrite the nul.
122         */
123        memset(dbenv->passwd, 0xff, dbenv->passwd_len-1);
124        __os_free(dbenv, dbenv->passwd);
125        dbenv->passwd = NULL;
126        dbenv->passwd_len = 0;
127
128        if (0) {
129out:            MUTEX_UNLOCK(dbenv, &renv->mutex);
130        }
131        return (ret);
132}
133
134/*
135 * __crypto_dbenv_close --
136 *      Crypto-specific destruction of DB_ENV structure.
137 *
138 * PUBLIC: int __crypto_dbenv_close __P((DB_ENV *));
139 */
140int
141__crypto_dbenv_close(dbenv)
142        DB_ENV *dbenv;
143{
144        DB_CIPHER *db_cipher;
145        int ret;
146
147        ret = 0;
148        db_cipher = dbenv->crypto_handle;
149        if (dbenv->passwd != NULL) {
150                memset(dbenv->passwd, 0xff, dbenv->passwd_len-1);
151                __os_free(dbenv, dbenv->passwd);
152                dbenv->passwd = NULL;
153        }
154        if (!CRYPTO_ON(dbenv))
155                return (0);
156        if (!F_ISSET(db_cipher, CIPHER_ANY))
157                ret = db_cipher->close(dbenv, db_cipher->data);
158        __os_free(dbenv, db_cipher);
159        return (ret);
160}
161
162/*
163 * __crypto_algsetup --
164 *      Given a db_cipher structure and a valid algorithm flag, call
165 * the specific algorithm setup function.
166 *
167 * PUBLIC: int __crypto_algsetup __P((DB_ENV *, DB_CIPHER *, u_int32_t, int));
168 */
169int
170__crypto_algsetup(dbenv, db_cipher, alg, do_init)
171        DB_ENV *dbenv;
172        DB_CIPHER *db_cipher;
173        u_int32_t alg;
174        int do_init;
175{
176        int ret;
177
178        ret = 0;
179        if (!CRYPTO_ON(dbenv)) {
180                __db_err(dbenv, "No cipher structure given");
181                return (EINVAL);
182        }
183        F_CLR(db_cipher, CIPHER_ANY);
184        switch (alg) {
185        case CIPHER_AES:
186                db_cipher->alg = CIPHER_AES;
187                ret = __aes_setup(dbenv, db_cipher);
188                break;
189        default:
190                __db_panic(dbenv, EINVAL);
191                /* NOTREACHED */
192        }
193        if (do_init)
194                ret = db_cipher->init(dbenv, db_cipher);
195        return (ret);
196}
197
198/*
199 * __crypto_decrypt_meta --
200 *      Perform decryption on a metapage if needed.
201 *
202 * PUBLIC:  int __crypto_decrypt_meta __P((DB_ENV *, DB *, u_int8_t *, int));
203 */
204int
205__crypto_decrypt_meta(dbenv, dbp, mbuf, do_metachk)
206        DB_ENV *dbenv;
207        DB *dbp;
208        u_int8_t *mbuf;
209        int do_metachk;
210{
211        DB_CIPHER *db_cipher;
212        DB dummydb;
213        DBMETA *meta;
214        size_t pg_off;
215        int ret;
216        u_int8_t *iv;
217
218        /*
219         * If we weren't given a dbp, we just want to decrypt the page
220         * on behalf of some internal subsystem, not on behalf of a user
221         * with a dbp.  Therefore, set up a dummy dbp so that the call
222         * to P_OVERHEAD below works.
223         */
224        if (dbp == NULL) {
225                memset(&dummydb, 0, sizeof(DB));
226                dbp = &dummydb;
227        }
228        /*
229         * Meta-pages may be encrypted for DBMETASIZE bytes.  If
230         * we have a non-zero IV (that is written after encryption)
231         * then we decrypt (or error if the user isn't set up for
232         * security).  We guarantee that the IV space on non-encrypted
233         * pages will be zero and a zero-IV is illegal for encryption.
234         * Therefore any non-zero IV means an encrypted database.
235         * This basically checks the passwd on the file
236         * if we cannot find a good magic number.
237         * We walk through all the algorithms we know about attempting
238         * to decrypt (and possibly byteswap).
239         *
240         * !!!
241         * All method meta pages have the IV and checksum at the
242         * exact same location, but not in DBMETA, use BTMETA.
243         */
244        ret = 0;
245        meta = (DBMETA *)mbuf;
246        if (meta->encrypt_alg != 0) {
247                db_cipher = (DB_CIPHER *)dbenv->crypto_handle;
248                if (!F_ISSET(dbp, DB_AM_ENCRYPT)) {
249                        if (!CRYPTO_ON(dbenv)) {
250                                __db_err(dbenv,
251    "Encrypted database: no encryption flag specified");
252                                return (EINVAL);
253                        }
254                        /*
255                         * User has a correct, secure env, but has
256                         * encountered a database in that env that is
257                         * secure, but user didn't dbp->set_flags.  Since
258                         * it is existing, use encryption if it is that
259                         * way already.
260                         */
261                        F_SET(dbp, DB_AM_ENCRYPT|DB_AM_CHKSUM);
262                }
263                /*
264                 * This was checked in set_flags when DB_AM_ENCRYPT was set.
265                 * So it better still be true here.
266                 */
267                DB_ASSERT(CRYPTO_ON(dbenv));
268                if (!F_ISSET(db_cipher, CIPHER_ANY) &&
269                    meta->encrypt_alg != db_cipher->alg) {
270                        __db_err(dbenv,
271                            "Database encrypted using a different algorithm");
272                        return (EINVAL);
273                }
274                DB_ASSERT(F_ISSET(dbp, DB_AM_CHKSUM));
275                iv = ((BTMETA *)mbuf)->iv;
276                /*
277                 * For ALL pages, we do not encrypt the beginning
278                 * of the page that contains overhead information.
279                 * This is true of meta and all other pages.
280                 */
281                pg_off = P_OVERHEAD(dbp);
282alg_retry:
283                /*
284                 * If they asked for a specific algorithm, then
285                 * use it.  Otherwise walk through those we know.
286                 */
287                if (!F_ISSET(db_cipher, CIPHER_ANY)) {
288                        if (do_metachk && (ret = db_cipher->decrypt(dbenv,
289                            db_cipher->data, iv, mbuf + pg_off,
290                            DBMETASIZE - pg_off)))
291                                return (ret);
292                        if (((BTMETA *)meta)->crypto_magic !=
293                            meta->magic) {
294                                __db_err(dbenv, "Invalid password");
295                                return (EINVAL);
296                        }
297                        /*
298                         * Success here.  The algorithm asked for and the one
299                         * on the file match.  We've just decrypted the meta
300                         * page and checked the magic numbers.  They match,
301                         * indicating the password is right.  All is right
302                         * with the world.
303                         */
304                        return (0);
305                }
306                /*
307                 * If we get here, CIPHER_ANY must be set.
308                 */
309                ret = __crypto_algsetup(dbenv, db_cipher, meta->encrypt_alg, 1);
310                goto alg_retry;
311        } else if (F_ISSET(dbp, DB_AM_ENCRYPT)) {
312                /*
313                 * They gave us a passwd, but the database is not
314                 * encrypted.  This is an error.  We do NOT want to
315                 * silently allow them to write data in the clear when
316                 * the user set up and expects encrypted data.
317                 *
318                 * This covers at least the following scenario.
319                 * 1.  User creates and sets up an encrypted database.
320                 * 2.  Attacker cannot read the actual data in the database
321                 * because it is encrypted, but can remove/replace the file
322                 * with an empty, unencrypted database file.
323                 * 3.  User sets encryption and we get to this code now.
324                 * If we allowed the file to be used in the clear since
325                 * it is that way on disk, the user would unsuspectingly
326                 * write sensitive data in the clear.
327                 * 4.  Attacker reads data that user thought was encrypted.
328                 *
329                 * Therefore, asking for encryption with a database that
330                 * was not encrypted is an error.
331                 */
332                __db_err(dbenv,
333                    "Unencrypted database with a supplied encryption key");
334                return (EINVAL);
335        }
336        return (ret);
337}
Note: See TracBrowser for help on using the repository browser.