xref: /freebsd/crypto/krb5/src/plugins/kdb/lmdb/kdb_lmdb.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /* plugins/kdb/lmdb/klmdb.c - KDB module using LMDB */
3*7f2fe78bSCy Schubert /*
4*7f2fe78bSCy Schubert  * Copyright (C) 2018 by the Massachusetts Institute of Technology.
5*7f2fe78bSCy Schubert  * All rights reserved.
6*7f2fe78bSCy Schubert  *
7*7f2fe78bSCy Schubert  * Redistribution and use in source and binary forms, with or without
8*7f2fe78bSCy Schubert  * modification, are permitted provided that the following conditions
9*7f2fe78bSCy Schubert  * are met:
10*7f2fe78bSCy Schubert  *
11*7f2fe78bSCy Schubert  * * Redistributions of source code must retain the above copyright
12*7f2fe78bSCy Schubert  *   notice, this list of conditions and the following disclaimer.
13*7f2fe78bSCy Schubert  *
14*7f2fe78bSCy Schubert  * * Redistributions in binary form must reproduce the above copyright
15*7f2fe78bSCy Schubert  *   notice, this list of conditions and the following disclaimer in
16*7f2fe78bSCy Schubert  *   the documentation and/or other materials provided with the
17*7f2fe78bSCy Schubert  *   distribution.
18*7f2fe78bSCy Schubert  *
19*7f2fe78bSCy Schubert  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20*7f2fe78bSCy Schubert  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21*7f2fe78bSCy Schubert  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22*7f2fe78bSCy Schubert  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23*7f2fe78bSCy Schubert  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24*7f2fe78bSCy Schubert  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25*7f2fe78bSCy Schubert  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26*7f2fe78bSCy Schubert  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27*7f2fe78bSCy Schubert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28*7f2fe78bSCy Schubert  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29*7f2fe78bSCy Schubert  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30*7f2fe78bSCy Schubert  * OF THE POSSIBILITY OF SUCH DAMAGE.
31*7f2fe78bSCy Schubert  */
32*7f2fe78bSCy Schubert 
33*7f2fe78bSCy Schubert /*
34*7f2fe78bSCy Schubert  * Thread-safety note: unlike the other two in-tree KDB modules, this module
35*7f2fe78bSCy Schubert  * performs no mutex locking to ensure thread safety.  As the KDC and kadmind
36*7f2fe78bSCy Schubert  * are single-threaded, and applications are not allowed to access the same
37*7f2fe78bSCy Schubert  * krb5_context in multiple threads simultaneously, there is no current need
38*7f2fe78bSCy Schubert  * for this code to be thread-safe.  If a need arises in the future, mutex
39*7f2fe78bSCy Schubert  * locking should be added around the read_txn and load_txn fields of
40*7f2fe78bSCy Schubert  * lmdb_context to ensure that only one thread at a time accesses those
41*7f2fe78bSCy Schubert  * transactions.
42*7f2fe78bSCy Schubert  */
43*7f2fe78bSCy Schubert 
44*7f2fe78bSCy Schubert /*
45*7f2fe78bSCy Schubert  * This KDB module stores principal and policy data using LMDB (Lightning
46*7f2fe78bSCy Schubert  * Memory-Mapped Database).  We use two LMDB environments, the first to hold
47*7f2fe78bSCy Schubert  * the majority of principal and policy data (suffix ".mdb") in the "principal"
48*7f2fe78bSCy Schubert  * and "policy" databases, and the second to hold the three non-replicated
49*7f2fe78bSCy Schubert  * account lockout attributes (suffix ".lockout.mdb") in the "lockout"
50*7f2fe78bSCy Schubert  * database.  The KDC only needs to write to the lockout database.
51*7f2fe78bSCy Schubert  *
52*7f2fe78bSCy Schubert  * For iteration we create a read transaction in the main environment for the
53*7f2fe78bSCy Schubert  * cursor.  Because the iteration callback might need to create its own
54*7f2fe78bSCy Schubert  * transactions for write operations (e.g. for kdb5_util
55*7f2fe78bSCy Schubert  * update_princ_encryption), we set the MDB_NOTLS flag on the main environment,
56*7f2fe78bSCy Schubert  * so that a thread can hold multiple transactions.
57*7f2fe78bSCy Schubert  *
58*7f2fe78bSCy Schubert  * To mitigate the overhead from MDB_NOTLS, we keep around a read_txn handle
59*7f2fe78bSCy Schubert  * in the database context for get operations, using mdb_txn_reset() and
60*7f2fe78bSCy Schubert  * mdb_txn_renew() between calls.
61*7f2fe78bSCy Schubert  *
62*7f2fe78bSCy Schubert  * For database loads, kdb5_util calls the create() method with the "temporary"
63*7f2fe78bSCy Schubert  * db_arg, and then promotes the finished contents at the end with the
64*7f2fe78bSCy Schubert  * promote_db() method.  In this case we create or open the same LMDB
65*7f2fe78bSCy Schubert  * environments as above, open a write_txn handle for the lifetime of the
66*7f2fe78bSCy Schubert  * context, and empty out the principal and policy databases.  On promote_db()
67*7f2fe78bSCy Schubert  * we commit the transaction.  We do not empty the lockout database and write
68*7f2fe78bSCy Schubert  * to it non-transactionally during the load so that we don't block writes by
69*7f2fe78bSCy Schubert  * the KDC; this isn't ideal if the load is aborted, but it shouldn't cause any
70*7f2fe78bSCy Schubert  * practical issues.
71*7f2fe78bSCy Schubert  *
72*7f2fe78bSCy Schubert  * For iprop loads, kdb5_util also includes the "merge_nra" db_arg, signifying
73*7f2fe78bSCy Schubert  * that the lockout attributes from existing principal entries should be
74*7f2fe78bSCy Schubert  * preserved.  This attribute is noted in the LMDB context, and put_principal
75*7f2fe78bSCy Schubert  * operations will not write to the lockout database if an existing lockout
76*7f2fe78bSCy Schubert  * entry is already present for the principal.
77*7f2fe78bSCy Schubert  */
78*7f2fe78bSCy Schubert 
79*7f2fe78bSCy Schubert #include "k5-int.h"
80*7f2fe78bSCy Schubert #include <kadm5/admin.h>
81*7f2fe78bSCy Schubert #include "kdb5.h"
82*7f2fe78bSCy Schubert #include "klmdb-int.h"
83*7f2fe78bSCy Schubert #include <lmdb.h>
84*7f2fe78bSCy Schubert 
85*7f2fe78bSCy Schubert /* The presence of any of these mask bits indicates a change to one of the
86*7f2fe78bSCy Schubert  * three principal lockout attributes. */
87*7f2fe78bSCy Schubert #define LOCKOUT_MASK (KADM5_LAST_SUCCESS | KADM5_LAST_FAILED |  \
88*7f2fe78bSCy Schubert                       KADM5_FAIL_AUTH_COUNT)
89*7f2fe78bSCy Schubert 
90*7f2fe78bSCy Schubert /* The default map size (for both environments) in megabytes. */
91*7f2fe78bSCy Schubert #define DEFAULT_MAPSIZE 128
92*7f2fe78bSCy Schubert 
93*7f2fe78bSCy Schubert #ifndef O_CLOEXEC
94*7f2fe78bSCy Schubert #define O_CLOEXEC 0
95*7f2fe78bSCy Schubert #endif
96*7f2fe78bSCy Schubert 
97*7f2fe78bSCy Schubert typedef struct {
98*7f2fe78bSCy Schubert     char *path;
99*7f2fe78bSCy Schubert     char *lockout_path;
100*7f2fe78bSCy Schubert     krb5_boolean temporary;     /* save changes until promote_db */
101*7f2fe78bSCy Schubert     krb5_boolean merge_nra;     /* preserve existing lockout attributes */
102*7f2fe78bSCy Schubert     krb5_boolean disable_last_success;
103*7f2fe78bSCy Schubert     krb5_boolean disable_lockout;
104*7f2fe78bSCy Schubert     krb5_boolean nosync;
105*7f2fe78bSCy Schubert     size_t mapsize;
106*7f2fe78bSCy Schubert     unsigned int maxreaders;
107*7f2fe78bSCy Schubert 
108*7f2fe78bSCy Schubert     MDB_env *env;
109*7f2fe78bSCy Schubert     MDB_env *lockout_env;
110*7f2fe78bSCy Schubert     MDB_dbi princ_db;
111*7f2fe78bSCy Schubert     MDB_dbi policy_db;
112*7f2fe78bSCy Schubert     MDB_dbi lockout_db;
113*7f2fe78bSCy Schubert 
114*7f2fe78bSCy Schubert     /* Used for get operations; each transaction is short-lived but we save the
115*7f2fe78bSCy Schubert      * handle between calls to reduce overhead from MDB_NOTLS. */
116*7f2fe78bSCy Schubert     MDB_txn *read_txn;
117*7f2fe78bSCy Schubert 
118*7f2fe78bSCy Schubert     /* Write transaction for load operations (create() with the "temporary"
119*7f2fe78bSCy Schubert      * db_arg).  */
120*7f2fe78bSCy Schubert     MDB_txn *load_txn;
121*7f2fe78bSCy Schubert } klmdb_context;
122*7f2fe78bSCy Schubert 
123*7f2fe78bSCy Schubert static krb5_error_code
klerr(krb5_context context,int err,const char * msg)124*7f2fe78bSCy Schubert klerr(krb5_context context, int err, const char *msg)
125*7f2fe78bSCy Schubert {
126*7f2fe78bSCy Schubert     krb5_error_code ret;
127*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
128*7f2fe78bSCy Schubert 
129*7f2fe78bSCy Schubert     /* Pass through system errors; map MDB errors to a com_err code. */
130*7f2fe78bSCy Schubert     ret = (err > 0) ? err : KRB5_KDB_ACCESS_ERROR;
131*7f2fe78bSCy Schubert 
132*7f2fe78bSCy Schubert     k5_setmsg(context, ret, _("%s (path: %s): %s"), msg, dbc->path,
133*7f2fe78bSCy Schubert               mdb_strerror(err));
134*7f2fe78bSCy Schubert     return ret;
135*7f2fe78bSCy Schubert }
136*7f2fe78bSCy Schubert 
137*7f2fe78bSCy Schubert /* Using db_args and the profile, create a DB context inside context and
138*7f2fe78bSCy Schubert  * initialize its configurable parameters. */
139*7f2fe78bSCy Schubert static krb5_error_code
configure_context(krb5_context context,const char * conf_section,char * const * db_args)140*7f2fe78bSCy Schubert configure_context(krb5_context context, const char *conf_section,
141*7f2fe78bSCy Schubert                   char *const *db_args)
142*7f2fe78bSCy Schubert {
143*7f2fe78bSCy Schubert     krb5_error_code ret;
144*7f2fe78bSCy Schubert     klmdb_context *dbc;
145*7f2fe78bSCy Schubert     char *pval = NULL;
146*7f2fe78bSCy Schubert     const char *path = NULL;
147*7f2fe78bSCy Schubert     profile_t profile = context->profile;
148*7f2fe78bSCy Schubert     int i, bval, ival;
149*7f2fe78bSCy Schubert 
150*7f2fe78bSCy Schubert     dbc = k5alloc(sizeof(*dbc), &ret);
151*7f2fe78bSCy Schubert     if (dbc == NULL)
152*7f2fe78bSCy Schubert         return ret;
153*7f2fe78bSCy Schubert     context->dal_handle->db_context = dbc;
154*7f2fe78bSCy Schubert 
155*7f2fe78bSCy Schubert     for (i = 0; db_args != NULL && db_args[i] != NULL; i++) {
156*7f2fe78bSCy Schubert         if (strcmp(db_args[i], "temporary") == 0) {
157*7f2fe78bSCy Schubert             dbc->temporary = TRUE;
158*7f2fe78bSCy Schubert         } else if (strcmp(db_args[i], "merge_nra") == 0) {
159*7f2fe78bSCy Schubert             dbc->merge_nra = TRUE;
160*7f2fe78bSCy Schubert         } else if (strncmp(db_args[i], "dbname=", 7) == 0) {
161*7f2fe78bSCy Schubert             path = db_args[i] + 7;
162*7f2fe78bSCy Schubert         } else {
163*7f2fe78bSCy Schubert             ret = EINVAL;
164*7f2fe78bSCy Schubert             k5_setmsg(context, ret, _("Unsupported argument \"%s\" for LMDB"),
165*7f2fe78bSCy Schubert                       db_args[i]);
166*7f2fe78bSCy Schubert             goto cleanup;
167*7f2fe78bSCy Schubert         }
168*7f2fe78bSCy Schubert     }
169*7f2fe78bSCy Schubert 
170*7f2fe78bSCy Schubert     if (path == NULL) {
171*7f2fe78bSCy Schubert         /* Check for database_name in the db_module section. */
172*7f2fe78bSCy Schubert         ret = profile_get_string(profile, KDB_MODULE_SECTION, conf_section,
173*7f2fe78bSCy Schubert                                  KRB5_CONF_DATABASE_NAME, NULL, &pval);
174*7f2fe78bSCy Schubert         if (!ret && pval == NULL) {
175*7f2fe78bSCy Schubert             /* For compatibility, check for database_name in the realm. */
176*7f2fe78bSCy Schubert             ret = profile_get_string(profile, KDB_REALM_SECTION,
177*7f2fe78bSCy Schubert                                      KRB5_DB_GET_REALM(context),
178*7f2fe78bSCy Schubert                                      KRB5_CONF_DATABASE_NAME, DEFAULT_KDB_FILE,
179*7f2fe78bSCy Schubert                                      &pval);
180*7f2fe78bSCy Schubert         }
181*7f2fe78bSCy Schubert         if (ret)
182*7f2fe78bSCy Schubert             goto cleanup;
183*7f2fe78bSCy Schubert         path = pval;
184*7f2fe78bSCy Schubert     }
185*7f2fe78bSCy Schubert 
186*7f2fe78bSCy Schubert     if (asprintf(&dbc->path, "%s.mdb", path) < 0) {
187*7f2fe78bSCy Schubert         dbc->path = NULL;
188*7f2fe78bSCy Schubert         ret = ENOMEM;
189*7f2fe78bSCy Schubert         goto cleanup;
190*7f2fe78bSCy Schubert     }
191*7f2fe78bSCy Schubert     if (asprintf(&dbc->lockout_path, "%s.lockout.mdb", path) < 0) {
192*7f2fe78bSCy Schubert         dbc->lockout_path = NULL;
193*7f2fe78bSCy Schubert         ret = ENOMEM;
194*7f2fe78bSCy Schubert         goto cleanup;
195*7f2fe78bSCy Schubert     }
196*7f2fe78bSCy Schubert 
197*7f2fe78bSCy Schubert     ret = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section,
198*7f2fe78bSCy Schubert                               KRB5_CONF_DISABLE_LAST_SUCCESS, FALSE, &bval);
199*7f2fe78bSCy Schubert     if (ret)
200*7f2fe78bSCy Schubert         goto cleanup;
201*7f2fe78bSCy Schubert     dbc->disable_last_success = bval;
202*7f2fe78bSCy Schubert 
203*7f2fe78bSCy Schubert     ret = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section,
204*7f2fe78bSCy Schubert                               KRB5_CONF_DISABLE_LOCKOUT, FALSE, &bval);
205*7f2fe78bSCy Schubert     if (ret)
206*7f2fe78bSCy Schubert         goto cleanup;
207*7f2fe78bSCy Schubert     dbc->disable_lockout = bval;
208*7f2fe78bSCy Schubert 
209*7f2fe78bSCy Schubert     ret = profile_get_integer(profile, KDB_MODULE_SECTION, conf_section,
210*7f2fe78bSCy Schubert                               KRB5_CONF_MAPSIZE, DEFAULT_MAPSIZE, &ival);
211*7f2fe78bSCy Schubert     if (ret)
212*7f2fe78bSCy Schubert         goto cleanup;
213*7f2fe78bSCy Schubert     dbc->mapsize = (size_t)ival * 1024 * 1024;
214*7f2fe78bSCy Schubert 
215*7f2fe78bSCy Schubert     ret = profile_get_integer(profile, KDB_MODULE_SECTION, conf_section,
216*7f2fe78bSCy Schubert                               KRB5_CONF_MAX_READERS, 0, &ival);
217*7f2fe78bSCy Schubert     if (ret)
218*7f2fe78bSCy Schubert         goto cleanup;
219*7f2fe78bSCy Schubert     dbc->maxreaders = ival;
220*7f2fe78bSCy Schubert 
221*7f2fe78bSCy Schubert     ret = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section,
222*7f2fe78bSCy Schubert                               KRB5_CONF_NOSYNC, FALSE, &bval);
223*7f2fe78bSCy Schubert     if (ret)
224*7f2fe78bSCy Schubert         goto cleanup;
225*7f2fe78bSCy Schubert     dbc->nosync = bval;
226*7f2fe78bSCy Schubert 
227*7f2fe78bSCy Schubert cleanup:
228*7f2fe78bSCy Schubert     profile_release_string(pval);
229*7f2fe78bSCy Schubert     return ret;
230*7f2fe78bSCy Schubert }
231*7f2fe78bSCy Schubert 
232*7f2fe78bSCy Schubert static krb5_error_code
open_lmdb_env(krb5_context context,klmdb_context * dbc,krb5_boolean is_lockout,krb5_boolean readonly,MDB_env ** env_out)233*7f2fe78bSCy Schubert open_lmdb_env(krb5_context context, klmdb_context *dbc,
234*7f2fe78bSCy Schubert               krb5_boolean is_lockout, krb5_boolean readonly,
235*7f2fe78bSCy Schubert               MDB_env **env_out)
236*7f2fe78bSCy Schubert {
237*7f2fe78bSCy Schubert     krb5_error_code ret;
238*7f2fe78bSCy Schubert     const char *path = is_lockout ? dbc->lockout_path : dbc->path;
239*7f2fe78bSCy Schubert     unsigned int flags;
240*7f2fe78bSCy Schubert     MDB_env *env = NULL;
241*7f2fe78bSCy Schubert     int err;
242*7f2fe78bSCy Schubert 
243*7f2fe78bSCy Schubert     *env_out = NULL;
244*7f2fe78bSCy Schubert 
245*7f2fe78bSCy Schubert     err = mdb_env_create(&env);
246*7f2fe78bSCy Schubert     if (err)
247*7f2fe78bSCy Schubert         goto lmdb_error;
248*7f2fe78bSCy Schubert 
249*7f2fe78bSCy Schubert     /* Use a pair of files instead of a subdirectory. */
250*7f2fe78bSCy Schubert     flags = MDB_NOSUBDIR;
251*7f2fe78bSCy Schubert 
252*7f2fe78bSCy Schubert     /*
253*7f2fe78bSCy Schubert      * For the primary database, tie read transaction locktable slots to the
254*7f2fe78bSCy Schubert      * transaction and not the thread, so read transactions for iteration
255*7f2fe78bSCy Schubert      * cursors can coexist with short-lived transactions for operations invoked
256*7f2fe78bSCy Schubert      * by the iteration callback..
257*7f2fe78bSCy Schubert      */
258*7f2fe78bSCy Schubert     if (!is_lockout)
259*7f2fe78bSCy Schubert         flags |= MDB_NOTLS;
260*7f2fe78bSCy Schubert 
261*7f2fe78bSCy Schubert     if (readonly)
262*7f2fe78bSCy Schubert         flags |= MDB_RDONLY;
263*7f2fe78bSCy Schubert 
264*7f2fe78bSCy Schubert     /* Durability for lockout records is never worth the performance penalty.
265*7f2fe78bSCy Schubert      * For the primary environment it might be, so we make it configurable. */
266*7f2fe78bSCy Schubert     if (is_lockout || dbc->nosync)
267*7f2fe78bSCy Schubert         flags |= MDB_NOSYNC;
268*7f2fe78bSCy Schubert 
269*7f2fe78bSCy Schubert     /* We use one database in the lockout env, two in the primary env. */
270*7f2fe78bSCy Schubert     err = mdb_env_set_maxdbs(env, is_lockout ? 1 : 2);
271*7f2fe78bSCy Schubert     if (err)
272*7f2fe78bSCy Schubert         goto lmdb_error;
273*7f2fe78bSCy Schubert 
274*7f2fe78bSCy Schubert     if (dbc->mapsize) {
275*7f2fe78bSCy Schubert         err = mdb_env_set_mapsize(env, dbc->mapsize);
276*7f2fe78bSCy Schubert         if (err)
277*7f2fe78bSCy Schubert             goto lmdb_error;
278*7f2fe78bSCy Schubert     }
279*7f2fe78bSCy Schubert 
280*7f2fe78bSCy Schubert     if (dbc->maxreaders) {
281*7f2fe78bSCy Schubert         err = mdb_env_set_maxreaders(env, dbc->maxreaders);
282*7f2fe78bSCy Schubert         if (err)
283*7f2fe78bSCy Schubert             goto lmdb_error;
284*7f2fe78bSCy Schubert     }
285*7f2fe78bSCy Schubert 
286*7f2fe78bSCy Schubert     err = mdb_env_open(env, path, flags, S_IRUSR | S_IWUSR);
287*7f2fe78bSCy Schubert     if (err)
288*7f2fe78bSCy Schubert         goto lmdb_error;
289*7f2fe78bSCy Schubert 
290*7f2fe78bSCy Schubert     *env_out = env;
291*7f2fe78bSCy Schubert     return 0;
292*7f2fe78bSCy Schubert 
293*7f2fe78bSCy Schubert lmdb_error:
294*7f2fe78bSCy Schubert     ret = klerr(context, err, _("LMDB environment open failure"));
295*7f2fe78bSCy Schubert     mdb_env_close(env);
296*7f2fe78bSCy Schubert     return ret;
297*7f2fe78bSCy Schubert }
298*7f2fe78bSCy Schubert 
299*7f2fe78bSCy Schubert /* Read a key from the primary environment, using a saved read transaction from
300*7f2fe78bSCy Schubert  * the database context.  Return KRB5_KDB_NOENTRY if the key is not found. */
301*7f2fe78bSCy Schubert static krb5_error_code
fetch(krb5_context context,MDB_dbi db,MDB_val * key,MDB_val * val_out)302*7f2fe78bSCy Schubert fetch(krb5_context context, MDB_dbi db, MDB_val *key, MDB_val *val_out)
303*7f2fe78bSCy Schubert {
304*7f2fe78bSCy Schubert     krb5_error_code ret = 0;
305*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
306*7f2fe78bSCy Schubert     int err;
307*7f2fe78bSCy Schubert 
308*7f2fe78bSCy Schubert     if (dbc->read_txn == NULL)
309*7f2fe78bSCy Schubert         err = mdb_txn_begin(dbc->env, NULL, MDB_RDONLY, &dbc->read_txn);
310*7f2fe78bSCy Schubert     else
311*7f2fe78bSCy Schubert         err = mdb_txn_renew(dbc->read_txn);
312*7f2fe78bSCy Schubert 
313*7f2fe78bSCy Schubert     if (!err)
314*7f2fe78bSCy Schubert         err = mdb_get(dbc->read_txn, db, key, val_out);
315*7f2fe78bSCy Schubert 
316*7f2fe78bSCy Schubert     if (err == MDB_NOTFOUND)
317*7f2fe78bSCy Schubert         ret = KRB5_KDB_NOENTRY;
318*7f2fe78bSCy Schubert     else if (err)
319*7f2fe78bSCy Schubert         ret = klerr(context, err, _("LMDB read failure"));
320*7f2fe78bSCy Schubert 
321*7f2fe78bSCy Schubert     mdb_txn_reset(dbc->read_txn);
322*7f2fe78bSCy Schubert     return ret;
323*7f2fe78bSCy Schubert }
324*7f2fe78bSCy Schubert 
325*7f2fe78bSCy Schubert /* If we are using a lockout database, try to fetch the lockout attributes for
326*7f2fe78bSCy Schubert  * key and set them in entry. */
327*7f2fe78bSCy Schubert static void
fetch_lockout(krb5_context context,MDB_val * key,krb5_db_entry * entry)328*7f2fe78bSCy Schubert fetch_lockout(krb5_context context, MDB_val *key, krb5_db_entry *entry)
329*7f2fe78bSCy Schubert {
330*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
331*7f2fe78bSCy Schubert     MDB_txn *txn = NULL;
332*7f2fe78bSCy Schubert     MDB_val val;
333*7f2fe78bSCy Schubert     int err;
334*7f2fe78bSCy Schubert 
335*7f2fe78bSCy Schubert     if (dbc->lockout_env == NULL)
336*7f2fe78bSCy Schubert         return;
337*7f2fe78bSCy Schubert     err = mdb_txn_begin(dbc->lockout_env, NULL, MDB_RDONLY, &txn);
338*7f2fe78bSCy Schubert     if (!err)
339*7f2fe78bSCy Schubert         err = mdb_get(txn, dbc->lockout_db, key, &val);
340*7f2fe78bSCy Schubert     if (!err && val.mv_size >= LOCKOUT_RECORD_LEN)
341*7f2fe78bSCy Schubert         klmdb_decode_princ_lockout(context, entry, val.mv_data);
342*7f2fe78bSCy Schubert     mdb_txn_abort(txn);
343*7f2fe78bSCy Schubert }
344*7f2fe78bSCy Schubert 
345*7f2fe78bSCy Schubert /*
346*7f2fe78bSCy Schubert  * Store a value for key in the specified database within the primary
347*7f2fe78bSCy Schubert  * environment.  Use the saved load transaction if one is present, or a
348*7f2fe78bSCy Schubert  * temporary write transaction if not.  If no_overwrite is true and the key
349*7f2fe78bSCy Schubert  * already exists, return KRB5_KDB_INUSE.  If must_overwrite is true and the
350*7f2fe78bSCy Schubert  * key does not already exist, return KRB5_KDB_NOENTRY.
351*7f2fe78bSCy Schubert  */
352*7f2fe78bSCy Schubert static krb5_error_code
put(krb5_context context,MDB_dbi db,char * keystr,uint8_t * bytes,size_t len,krb5_boolean no_overwrite,krb5_boolean must_overwrite)353*7f2fe78bSCy Schubert put(krb5_context context, MDB_dbi db, char *keystr, uint8_t *bytes, size_t len,
354*7f2fe78bSCy Schubert     krb5_boolean no_overwrite, krb5_boolean must_overwrite)
355*7f2fe78bSCy Schubert {
356*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
357*7f2fe78bSCy Schubert     unsigned int putflags = no_overwrite ? MDB_NOOVERWRITE : 0;
358*7f2fe78bSCy Schubert     MDB_txn *temp_txn = NULL, *txn;
359*7f2fe78bSCy Schubert     MDB_val key = { strlen(keystr), keystr }, val = { len, bytes }, dummy;
360*7f2fe78bSCy Schubert     int err;
361*7f2fe78bSCy Schubert 
362*7f2fe78bSCy Schubert     if (dbc->load_txn != NULL) {
363*7f2fe78bSCy Schubert         txn = dbc->load_txn;
364*7f2fe78bSCy Schubert     } else {
365*7f2fe78bSCy Schubert         err = mdb_txn_begin(dbc->env, NULL, 0, &temp_txn);
366*7f2fe78bSCy Schubert         if (err)
367*7f2fe78bSCy Schubert             goto error;
368*7f2fe78bSCy Schubert         txn = temp_txn;
369*7f2fe78bSCy Schubert     }
370*7f2fe78bSCy Schubert 
371*7f2fe78bSCy Schubert     if (must_overwrite && mdb_get(txn, db, &key, &dummy) == MDB_NOTFOUND) {
372*7f2fe78bSCy Schubert         mdb_txn_abort(temp_txn);
373*7f2fe78bSCy Schubert         return KRB5_KDB_NOENTRY;
374*7f2fe78bSCy Schubert     }
375*7f2fe78bSCy Schubert 
376*7f2fe78bSCy Schubert     err = mdb_put(txn, db, &key, &val, putflags);
377*7f2fe78bSCy Schubert     if (err)
378*7f2fe78bSCy Schubert         goto error;
379*7f2fe78bSCy Schubert 
380*7f2fe78bSCy Schubert     if (temp_txn != NULL) {
381*7f2fe78bSCy Schubert         err = mdb_txn_commit(temp_txn);
382*7f2fe78bSCy Schubert         temp_txn = NULL;
383*7f2fe78bSCy Schubert         if (err)
384*7f2fe78bSCy Schubert             goto error;
385*7f2fe78bSCy Schubert     }
386*7f2fe78bSCy Schubert 
387*7f2fe78bSCy Schubert     return 0;
388*7f2fe78bSCy Schubert 
389*7f2fe78bSCy Schubert error:
390*7f2fe78bSCy Schubert     mdb_txn_abort(temp_txn);
391*7f2fe78bSCy Schubert     if (err == MDB_KEYEXIST)
392*7f2fe78bSCy Schubert         return KRB5_KDB_INUSE;
393*7f2fe78bSCy Schubert     else
394*7f2fe78bSCy Schubert         return klerr(context, err, _("LMDB write failure"));
395*7f2fe78bSCy Schubert }
396*7f2fe78bSCy Schubert 
397*7f2fe78bSCy Schubert /* Delete an entry from the specified env and database, using a temporary write
398*7f2fe78bSCy Schubert  * transaction.  Return KRB5_KDB_NOENTRY if the key does not exist. */
399*7f2fe78bSCy Schubert static krb5_error_code
del(krb5_context context,MDB_env * env,MDB_dbi db,char * keystr)400*7f2fe78bSCy Schubert del(krb5_context context, MDB_env *env, MDB_dbi db, char *keystr)
401*7f2fe78bSCy Schubert {
402*7f2fe78bSCy Schubert     krb5_error_code ret = 0;
403*7f2fe78bSCy Schubert     MDB_txn *txn = NULL;
404*7f2fe78bSCy Schubert     MDB_val key = { strlen(keystr), keystr };
405*7f2fe78bSCy Schubert     int err;
406*7f2fe78bSCy Schubert 
407*7f2fe78bSCy Schubert     err = mdb_txn_begin(env, NULL, 0, &txn);
408*7f2fe78bSCy Schubert     if (!err)
409*7f2fe78bSCy Schubert         err = mdb_del(txn, db, &key, NULL);
410*7f2fe78bSCy Schubert     if (!err) {
411*7f2fe78bSCy Schubert         err = mdb_txn_commit(txn);
412*7f2fe78bSCy Schubert         txn = NULL;
413*7f2fe78bSCy Schubert     }
414*7f2fe78bSCy Schubert 
415*7f2fe78bSCy Schubert     if (err == MDB_NOTFOUND)
416*7f2fe78bSCy Schubert         ret = KRB5_KDB_NOENTRY;
417*7f2fe78bSCy Schubert     else if (err)
418*7f2fe78bSCy Schubert         ret = klerr(context, err, _("LMDB delete failure"));
419*7f2fe78bSCy Schubert 
420*7f2fe78bSCy Schubert     mdb_txn_abort(txn);
421*7f2fe78bSCy Schubert     return ret;
422*7f2fe78bSCy Schubert }
423*7f2fe78bSCy Schubert 
424*7f2fe78bSCy Schubert /* Zero out and unlink filename. */
425*7f2fe78bSCy Schubert static krb5_error_code
destroy_file(const char * filename)426*7f2fe78bSCy Schubert destroy_file(const char *filename)
427*7f2fe78bSCy Schubert {
428*7f2fe78bSCy Schubert     krb5_error_code ret;
429*7f2fe78bSCy Schubert     struct stat st;
430*7f2fe78bSCy Schubert     ssize_t len;
431*7f2fe78bSCy Schubert     off_t pos;
432*7f2fe78bSCy Schubert     uint8_t buf[BUFSIZ], zbuf[BUFSIZ] = { 0 };
433*7f2fe78bSCy Schubert     int fd;
434*7f2fe78bSCy Schubert 
435*7f2fe78bSCy Schubert     fd = open(filename, O_RDWR | O_CLOEXEC, 0);
436*7f2fe78bSCy Schubert     if (fd < 0)
437*7f2fe78bSCy Schubert         return errno;
438*7f2fe78bSCy Schubert     set_cloexec_fd(fd);
439*7f2fe78bSCy Schubert     if (fstat(fd, &st) == -1)
440*7f2fe78bSCy Schubert         goto error;
441*7f2fe78bSCy Schubert 
442*7f2fe78bSCy Schubert     memset(zbuf, 0, BUFSIZ);
443*7f2fe78bSCy Schubert     pos = 0;
444*7f2fe78bSCy Schubert     while (pos < st.st_size) {
445*7f2fe78bSCy Schubert         len = read(fd, buf, BUFSIZ);
446*7f2fe78bSCy Schubert         if (len < 0)
447*7f2fe78bSCy Schubert             goto error;
448*7f2fe78bSCy Schubert         /* Only rewrite the block if it's not already zeroed, in case the file
449*7f2fe78bSCy Schubert          * is sparse. */
450*7f2fe78bSCy Schubert         if (memcmp(buf, zbuf, len) != 0) {
451*7f2fe78bSCy Schubert             (void)lseek(fd, pos, SEEK_SET);
452*7f2fe78bSCy Schubert             len = write(fd, zbuf, len);
453*7f2fe78bSCy Schubert             if (len < 0)
454*7f2fe78bSCy Schubert                 goto error;
455*7f2fe78bSCy Schubert         }
456*7f2fe78bSCy Schubert         pos += len;
457*7f2fe78bSCy Schubert     }
458*7f2fe78bSCy Schubert     close(fd);
459*7f2fe78bSCy Schubert 
460*7f2fe78bSCy Schubert     if (unlink(filename) != 0)
461*7f2fe78bSCy Schubert         return errno;
462*7f2fe78bSCy Schubert     return 0;
463*7f2fe78bSCy Schubert 
464*7f2fe78bSCy Schubert error:
465*7f2fe78bSCy Schubert     ret = errno;
466*7f2fe78bSCy Schubert     close(fd);
467*7f2fe78bSCy Schubert     return ret;
468*7f2fe78bSCy Schubert }
469*7f2fe78bSCy Schubert 
470*7f2fe78bSCy Schubert static krb5_error_code
klmdb_lib_init()471*7f2fe78bSCy Schubert klmdb_lib_init()
472*7f2fe78bSCy Schubert {
473*7f2fe78bSCy Schubert     return 0;
474*7f2fe78bSCy Schubert }
475*7f2fe78bSCy Schubert 
476*7f2fe78bSCy Schubert static krb5_error_code
klmdb_lib_cleanup()477*7f2fe78bSCy Schubert klmdb_lib_cleanup()
478*7f2fe78bSCy Schubert {
479*7f2fe78bSCy Schubert     return 0;
480*7f2fe78bSCy Schubert }
481*7f2fe78bSCy Schubert 
482*7f2fe78bSCy Schubert static krb5_error_code
klmdb_fini(krb5_context context)483*7f2fe78bSCy Schubert klmdb_fini(krb5_context context)
484*7f2fe78bSCy Schubert {
485*7f2fe78bSCy Schubert     klmdb_context *dbc;
486*7f2fe78bSCy Schubert 
487*7f2fe78bSCy Schubert     dbc = context->dal_handle->db_context;
488*7f2fe78bSCy Schubert     if (dbc == NULL)
489*7f2fe78bSCy Schubert         return 0;
490*7f2fe78bSCy Schubert     mdb_txn_abort(dbc->read_txn);
491*7f2fe78bSCy Schubert     mdb_txn_abort(dbc->load_txn);
492*7f2fe78bSCy Schubert     mdb_env_close(dbc->env);
493*7f2fe78bSCy Schubert     mdb_env_close(dbc->lockout_env);
494*7f2fe78bSCy Schubert     free(dbc->path);
495*7f2fe78bSCy Schubert     free(dbc->lockout_path);
496*7f2fe78bSCy Schubert     free(dbc);
497*7f2fe78bSCy Schubert     context->dal_handle->db_context = NULL;
498*7f2fe78bSCy Schubert     return 0;
499*7f2fe78bSCy Schubert }
500*7f2fe78bSCy Schubert 
501*7f2fe78bSCy Schubert static krb5_error_code
klmdb_open(krb5_context context,char * conf_section,char ** db_args,int mode)502*7f2fe78bSCy Schubert klmdb_open(krb5_context context, char *conf_section, char **db_args, int mode)
503*7f2fe78bSCy Schubert {
504*7f2fe78bSCy Schubert     krb5_error_code ret;
505*7f2fe78bSCy Schubert     klmdb_context *dbc;
506*7f2fe78bSCy Schubert     krb5_boolean readonly;
507*7f2fe78bSCy Schubert     MDB_txn *txn = NULL;
508*7f2fe78bSCy Schubert     struct stat st;
509*7f2fe78bSCy Schubert     int err;
510*7f2fe78bSCy Schubert 
511*7f2fe78bSCy Schubert     if (context->dal_handle->db_context != NULL)
512*7f2fe78bSCy Schubert         return 0;
513*7f2fe78bSCy Schubert 
514*7f2fe78bSCy Schubert     ret = configure_context(context, conf_section, db_args);
515*7f2fe78bSCy Schubert     if (ret)
516*7f2fe78bSCy Schubert         return ret;
517*7f2fe78bSCy Schubert     dbc = context->dal_handle->db_context;
518*7f2fe78bSCy Schubert 
519*7f2fe78bSCy Schubert     if (stat(dbc->path, &st) != 0) {
520*7f2fe78bSCy Schubert         ret = ENOENT;
521*7f2fe78bSCy Schubert         k5_setmsg(context, ret, _("LMDB file %s does not exist"), dbc->path);
522*7f2fe78bSCy Schubert         goto error;
523*7f2fe78bSCy Schubert     }
524*7f2fe78bSCy Schubert 
525*7f2fe78bSCy Schubert     /* Open the primary environment and databases.  The KDC can open this
526*7f2fe78bSCy Schubert      * environment read-only. */
527*7f2fe78bSCy Schubert     readonly = (mode & KRB5_KDB_OPEN_RO) || (mode & KRB5_KDB_SRV_TYPE_KDC);
528*7f2fe78bSCy Schubert     ret = open_lmdb_env(context, dbc, FALSE, readonly, &dbc->env);
529*7f2fe78bSCy Schubert     if (ret)
530*7f2fe78bSCy Schubert         goto error;
531*7f2fe78bSCy Schubert     err = mdb_txn_begin(dbc->env, NULL, MDB_RDONLY, &txn);
532*7f2fe78bSCy Schubert     if (err)
533*7f2fe78bSCy Schubert         goto lmdb_error;
534*7f2fe78bSCy Schubert     err = mdb_dbi_open(txn, "principal", 0, &dbc->princ_db);
535*7f2fe78bSCy Schubert     if (err)
536*7f2fe78bSCy Schubert         goto lmdb_error;
537*7f2fe78bSCy Schubert     err = mdb_dbi_open(txn, "policy", 0, &dbc->policy_db);
538*7f2fe78bSCy Schubert     if (err)
539*7f2fe78bSCy Schubert         goto lmdb_error;
540*7f2fe78bSCy Schubert     err = mdb_txn_commit(txn);
541*7f2fe78bSCy Schubert     txn = NULL;
542*7f2fe78bSCy Schubert     if (err)
543*7f2fe78bSCy Schubert         goto lmdb_error;
544*7f2fe78bSCy Schubert 
545*7f2fe78bSCy Schubert     /* Open the lockout environment and database if we will need it. */
546*7f2fe78bSCy Schubert     if (!dbc->disable_last_success || !dbc->disable_lockout) {
547*7f2fe78bSCy Schubert         readonly = !!(mode & KRB5_KDB_OPEN_RO);
548*7f2fe78bSCy Schubert         ret = open_lmdb_env(context, dbc, TRUE, readonly, &dbc->lockout_env);
549*7f2fe78bSCy Schubert         if (ret)
550*7f2fe78bSCy Schubert             goto error;
551*7f2fe78bSCy Schubert         err = mdb_txn_begin(dbc->lockout_env, NULL, MDB_RDONLY, &txn);
552*7f2fe78bSCy Schubert         if (err)
553*7f2fe78bSCy Schubert             goto lmdb_error;
554*7f2fe78bSCy Schubert         err = mdb_dbi_open(txn, "lockout", 0, &dbc->lockout_db);
555*7f2fe78bSCy Schubert         if (err)
556*7f2fe78bSCy Schubert             goto lmdb_error;
557*7f2fe78bSCy Schubert         err = mdb_txn_commit(txn);
558*7f2fe78bSCy Schubert         txn = NULL;
559*7f2fe78bSCy Schubert         if (err)
560*7f2fe78bSCy Schubert             goto lmdb_error;
561*7f2fe78bSCy Schubert     }
562*7f2fe78bSCy Schubert 
563*7f2fe78bSCy Schubert     return 0;
564*7f2fe78bSCy Schubert 
565*7f2fe78bSCy Schubert lmdb_error:
566*7f2fe78bSCy Schubert     ret = klerr(context, err, _("LMDB open failure"));
567*7f2fe78bSCy Schubert error:
568*7f2fe78bSCy Schubert     mdb_txn_abort(txn);
569*7f2fe78bSCy Schubert     klmdb_fini(context);
570*7f2fe78bSCy Schubert     return ret;
571*7f2fe78bSCy Schubert }
572*7f2fe78bSCy Schubert 
573*7f2fe78bSCy Schubert static krb5_error_code
klmdb_create(krb5_context context,char * conf_section,char ** db_args)574*7f2fe78bSCy Schubert klmdb_create(krb5_context context, char *conf_section, char **db_args)
575*7f2fe78bSCy Schubert {
576*7f2fe78bSCy Schubert     krb5_error_code ret;
577*7f2fe78bSCy Schubert     klmdb_context *dbc;
578*7f2fe78bSCy Schubert     MDB_txn *txn = NULL;
579*7f2fe78bSCy Schubert     struct stat st;
580*7f2fe78bSCy Schubert     int err;
581*7f2fe78bSCy Schubert 
582*7f2fe78bSCy Schubert     if (context->dal_handle->db_context != NULL)
583*7f2fe78bSCy Schubert         return 0;
584*7f2fe78bSCy Schubert 
585*7f2fe78bSCy Schubert     ret = configure_context(context, conf_section, db_args);
586*7f2fe78bSCy Schubert     if (ret)
587*7f2fe78bSCy Schubert         return ret;
588*7f2fe78bSCy Schubert     dbc = context->dal_handle->db_context;
589*7f2fe78bSCy Schubert 
590*7f2fe78bSCy Schubert     if (!dbc->temporary) {
591*7f2fe78bSCy Schubert         if (stat(dbc->path, &st) == 0) {
592*7f2fe78bSCy Schubert             ret = ENOENT;
593*7f2fe78bSCy Schubert             k5_setmsg(context, ret, _("LMDB file %s already exists"),
594*7f2fe78bSCy Schubert                       dbc->path);
595*7f2fe78bSCy Schubert             goto error;
596*7f2fe78bSCy Schubert         }
597*7f2fe78bSCy Schubert     }
598*7f2fe78bSCy Schubert 
599*7f2fe78bSCy Schubert     /* Open (and create if necessary) the LMDB environments. */
600*7f2fe78bSCy Schubert     ret = open_lmdb_env(context, dbc, FALSE, FALSE, &dbc->env);
601*7f2fe78bSCy Schubert     if (ret)
602*7f2fe78bSCy Schubert         goto error;
603*7f2fe78bSCy Schubert     ret = open_lmdb_env(context, dbc, TRUE, FALSE, &dbc->lockout_env);
604*7f2fe78bSCy Schubert     if (ret)
605*7f2fe78bSCy Schubert         goto error;
606*7f2fe78bSCy Schubert 
607*7f2fe78bSCy Schubert     /* Open the primary databases, creating them if they don't exist. */
608*7f2fe78bSCy Schubert     err = mdb_txn_begin(dbc->env, NULL, 0, &txn);
609*7f2fe78bSCy Schubert     if (err)
610*7f2fe78bSCy Schubert         goto lmdb_error;
611*7f2fe78bSCy Schubert     err = mdb_dbi_open(txn, "principal", MDB_CREATE, &dbc->princ_db);
612*7f2fe78bSCy Schubert     if (err)
613*7f2fe78bSCy Schubert         goto lmdb_error;
614*7f2fe78bSCy Schubert     err = mdb_dbi_open(txn, "policy", MDB_CREATE, &dbc->policy_db);
615*7f2fe78bSCy Schubert     if (err)
616*7f2fe78bSCy Schubert         goto lmdb_error;
617*7f2fe78bSCy Schubert     err = mdb_txn_commit(txn);
618*7f2fe78bSCy Schubert     txn = NULL;
619*7f2fe78bSCy Schubert     if (err)
620*7f2fe78bSCy Schubert         goto lmdb_error;
621*7f2fe78bSCy Schubert 
622*7f2fe78bSCy Schubert     /* Create the lockout database if it doesn't exist. */
623*7f2fe78bSCy Schubert     err = mdb_txn_begin(dbc->lockout_env, NULL, 0, &txn);
624*7f2fe78bSCy Schubert     if (err)
625*7f2fe78bSCy Schubert         goto lmdb_error;
626*7f2fe78bSCy Schubert     err = mdb_dbi_open(txn, "lockout", MDB_CREATE, &dbc->lockout_db);
627*7f2fe78bSCy Schubert     if (err)
628*7f2fe78bSCy Schubert         goto lmdb_error;
629*7f2fe78bSCy Schubert     err = mdb_txn_commit(txn);
630*7f2fe78bSCy Schubert     txn = NULL;
631*7f2fe78bSCy Schubert     if (err)
632*7f2fe78bSCy Schubert         goto lmdb_error;
633*7f2fe78bSCy Schubert 
634*7f2fe78bSCy Schubert     if (dbc->temporary) {
635*7f2fe78bSCy Schubert         /* Create a load transaction and empty the primary databases within
636*7f2fe78bSCy Schubert          * it. */
637*7f2fe78bSCy Schubert         err = mdb_txn_begin(dbc->env, NULL, 0, &dbc->load_txn);
638*7f2fe78bSCy Schubert         if (err)
639*7f2fe78bSCy Schubert             goto lmdb_error;
640*7f2fe78bSCy Schubert         err = mdb_drop(dbc->load_txn, dbc->princ_db, 0);
641*7f2fe78bSCy Schubert         if (err)
642*7f2fe78bSCy Schubert             goto lmdb_error;
643*7f2fe78bSCy Schubert         err = mdb_drop(dbc->load_txn, dbc->policy_db, 0);
644*7f2fe78bSCy Schubert         if (err)
645*7f2fe78bSCy Schubert             goto lmdb_error;
646*7f2fe78bSCy Schubert     }
647*7f2fe78bSCy Schubert 
648*7f2fe78bSCy Schubert     /* Close the lockout environment if we won't need it. */
649*7f2fe78bSCy Schubert     if (dbc->disable_last_success && dbc->disable_lockout) {
650*7f2fe78bSCy Schubert         mdb_env_close(dbc->lockout_env);
651*7f2fe78bSCy Schubert         dbc->lockout_env = NULL;
652*7f2fe78bSCy Schubert         dbc->lockout_db = 0;
653*7f2fe78bSCy Schubert     }
654*7f2fe78bSCy Schubert 
655*7f2fe78bSCy Schubert     return 0;
656*7f2fe78bSCy Schubert 
657*7f2fe78bSCy Schubert lmdb_error:
658*7f2fe78bSCy Schubert     ret = klerr(context, err, _("LMDB create error"));
659*7f2fe78bSCy Schubert error:
660*7f2fe78bSCy Schubert     mdb_txn_abort(txn);
661*7f2fe78bSCy Schubert     klmdb_fini(context);
662*7f2fe78bSCy Schubert     return ret;
663*7f2fe78bSCy Schubert }
664*7f2fe78bSCy Schubert 
665*7f2fe78bSCy Schubert /* Unlink the "-lock" extension of path. */
666*7f2fe78bSCy Schubert static krb5_error_code
unlink_lock_file(krb5_context context,const char * path)667*7f2fe78bSCy Schubert unlink_lock_file(krb5_context context, const char *path)
668*7f2fe78bSCy Schubert {
669*7f2fe78bSCy Schubert     char *lock_path;
670*7f2fe78bSCy Schubert     int st;
671*7f2fe78bSCy Schubert 
672*7f2fe78bSCy Schubert     if (asprintf(&lock_path, "%s-lock", path) < 0)
673*7f2fe78bSCy Schubert         return ENOMEM;
674*7f2fe78bSCy Schubert     st = unlink(lock_path);
675*7f2fe78bSCy Schubert     if (st)
676*7f2fe78bSCy Schubert         k5_prependmsg(context, st, _("Could not unlink %s"), lock_path);
677*7f2fe78bSCy Schubert     free(lock_path);
678*7f2fe78bSCy Schubert     return st;
679*7f2fe78bSCy Schubert }
680*7f2fe78bSCy Schubert 
681*7f2fe78bSCy Schubert static krb5_error_code
klmdb_destroy(krb5_context context,char * conf_section,char ** db_args)682*7f2fe78bSCy Schubert klmdb_destroy(krb5_context context, char *conf_section, char **db_args)
683*7f2fe78bSCy Schubert {
684*7f2fe78bSCy Schubert     krb5_error_code ret;
685*7f2fe78bSCy Schubert     klmdb_context *dbc;
686*7f2fe78bSCy Schubert 
687*7f2fe78bSCy Schubert     if (context->dal_handle->db_context != NULL)
688*7f2fe78bSCy Schubert         klmdb_fini(context);
689*7f2fe78bSCy Schubert     ret = configure_context(context, conf_section, db_args);
690*7f2fe78bSCy Schubert     if (ret)
691*7f2fe78bSCy Schubert         goto cleanup;
692*7f2fe78bSCy Schubert     dbc = context->dal_handle->db_context;
693*7f2fe78bSCy Schubert 
694*7f2fe78bSCy Schubert     ret = destroy_file(dbc->path);
695*7f2fe78bSCy Schubert     if (ret)
696*7f2fe78bSCy Schubert         goto cleanup;
697*7f2fe78bSCy Schubert     ret = unlink_lock_file(context, dbc->path);
698*7f2fe78bSCy Schubert     if (ret)
699*7f2fe78bSCy Schubert         goto cleanup;
700*7f2fe78bSCy Schubert 
701*7f2fe78bSCy Schubert     ret = destroy_file(dbc->lockout_path);
702*7f2fe78bSCy Schubert     if (ret)
703*7f2fe78bSCy Schubert         goto cleanup;
704*7f2fe78bSCy Schubert     ret = unlink_lock_file(context, dbc->lockout_path);
705*7f2fe78bSCy Schubert 
706*7f2fe78bSCy Schubert cleanup:
707*7f2fe78bSCy Schubert     klmdb_fini(context);
708*7f2fe78bSCy Schubert     return ret;
709*7f2fe78bSCy Schubert }
710*7f2fe78bSCy Schubert 
711*7f2fe78bSCy Schubert static krb5_error_code
klmdb_get_principal(krb5_context context,krb5_const_principal searchfor,unsigned int flags,krb5_db_entry ** entry_out)712*7f2fe78bSCy Schubert klmdb_get_principal(krb5_context context, krb5_const_principal searchfor,
713*7f2fe78bSCy Schubert                     unsigned int flags, krb5_db_entry **entry_out)
714*7f2fe78bSCy Schubert {
715*7f2fe78bSCy Schubert     krb5_error_code ret;
716*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
717*7f2fe78bSCy Schubert     MDB_val key, val;
718*7f2fe78bSCy Schubert     char *name = NULL;
719*7f2fe78bSCy Schubert 
720*7f2fe78bSCy Schubert     *entry_out = NULL;
721*7f2fe78bSCy Schubert     if (dbc == NULL)
722*7f2fe78bSCy Schubert         return KRB5_KDB_DBNOTINITED;
723*7f2fe78bSCy Schubert 
724*7f2fe78bSCy Schubert     ret = krb5_unparse_name(context, searchfor, &name);
725*7f2fe78bSCy Schubert     if (ret)
726*7f2fe78bSCy Schubert         goto cleanup;
727*7f2fe78bSCy Schubert 
728*7f2fe78bSCy Schubert     key.mv_data = name;
729*7f2fe78bSCy Schubert     key.mv_size = strlen(name);
730*7f2fe78bSCy Schubert     ret = fetch(context, dbc->princ_db, &key, &val);
731*7f2fe78bSCy Schubert     if (ret)
732*7f2fe78bSCy Schubert         goto cleanup;
733*7f2fe78bSCy Schubert 
734*7f2fe78bSCy Schubert     ret = klmdb_decode_princ(context, name, strlen(name),
735*7f2fe78bSCy Schubert                              val.mv_data, val.mv_size, entry_out);
736*7f2fe78bSCy Schubert     if (ret)
737*7f2fe78bSCy Schubert         goto cleanup;
738*7f2fe78bSCy Schubert 
739*7f2fe78bSCy Schubert     fetch_lockout(context, &key, *entry_out);
740*7f2fe78bSCy Schubert 
741*7f2fe78bSCy Schubert cleanup:
742*7f2fe78bSCy Schubert     krb5_free_unparsed_name(context, name);
743*7f2fe78bSCy Schubert     return ret;
744*7f2fe78bSCy Schubert }
745*7f2fe78bSCy Schubert 
746*7f2fe78bSCy Schubert static krb5_error_code
klmdb_put_principal(krb5_context context,krb5_db_entry * entry,char ** db_args)747*7f2fe78bSCy Schubert klmdb_put_principal(krb5_context context, krb5_db_entry *entry, char **db_args)
748*7f2fe78bSCy Schubert {
749*7f2fe78bSCy Schubert     krb5_error_code ret;
750*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
751*7f2fe78bSCy Schubert     MDB_val key, val, dummy;
752*7f2fe78bSCy Schubert     MDB_txn *txn = NULL;
753*7f2fe78bSCy Schubert     uint8_t lockout[LOCKOUT_RECORD_LEN], *enc;
754*7f2fe78bSCy Schubert     size_t len;
755*7f2fe78bSCy Schubert     char *name = NULL;
756*7f2fe78bSCy Schubert     int err;
757*7f2fe78bSCy Schubert 
758*7f2fe78bSCy Schubert     if (db_args != NULL) {
759*7f2fe78bSCy Schubert         /* This module does not support DB arguments for put_principal. */
760*7f2fe78bSCy Schubert         k5_setmsg(context, EINVAL, _("Unsupported argument \"%s\" for lmdb"),
761*7f2fe78bSCy Schubert                   db_args[0]);
762*7f2fe78bSCy Schubert         return EINVAL;
763*7f2fe78bSCy Schubert     }
764*7f2fe78bSCy Schubert 
765*7f2fe78bSCy Schubert     if (dbc == NULL)
766*7f2fe78bSCy Schubert         return KRB5_KDB_DBNOTINITED;
767*7f2fe78bSCy Schubert 
768*7f2fe78bSCy Schubert     ret = krb5_unparse_name(context, entry->princ, &name);
769*7f2fe78bSCy Schubert     if (ret)
770*7f2fe78bSCy Schubert         goto cleanup;
771*7f2fe78bSCy Schubert 
772*7f2fe78bSCy Schubert     ret = klmdb_encode_princ(context, entry, &enc, &len);
773*7f2fe78bSCy Schubert     if (ret)
774*7f2fe78bSCy Schubert         goto cleanup;
775*7f2fe78bSCy Schubert     ret = put(context, dbc->princ_db, name, enc, len, FALSE, FALSE);
776*7f2fe78bSCy Schubert     free(enc);
777*7f2fe78bSCy Schubert     if (ret)
778*7f2fe78bSCy Schubert         goto cleanup;
779*7f2fe78bSCy Schubert 
780*7f2fe78bSCy Schubert     /*
781*7f2fe78bSCy Schubert      * Write the lockout attributes to the lockout database if we are using
782*7f2fe78bSCy Schubert      * one.  During a load operation, changes to lockout attributes will become
783*7f2fe78bSCy Schubert      * visible before the load is finished, which is an acceptable compromise
784*7f2fe78bSCy Schubert      * on load atomicity.
785*7f2fe78bSCy Schubert      */
786*7f2fe78bSCy Schubert     if (dbc->lockout_env != NULL &&
787*7f2fe78bSCy Schubert         (entry->mask & (LOCKOUT_MASK | KADM5_PRINCIPAL))) {
788*7f2fe78bSCy Schubert         key.mv_data = name;
789*7f2fe78bSCy Schubert         key.mv_size = strlen(name);
790*7f2fe78bSCy Schubert         klmdb_encode_princ_lockout(context, entry, lockout);
791*7f2fe78bSCy Schubert         val.mv_data = lockout;
792*7f2fe78bSCy Schubert         val.mv_size = sizeof(lockout);
793*7f2fe78bSCy Schubert         err = mdb_txn_begin(dbc->lockout_env, NULL, 0, &txn);
794*7f2fe78bSCy Schubert         if (!err && dbc->merge_nra) {
795*7f2fe78bSCy Schubert             /* During an iprop load, do not change existing lockout entries. */
796*7f2fe78bSCy Schubert             if (mdb_get(txn, dbc->lockout_db, &key, &dummy) == 0)
797*7f2fe78bSCy Schubert                 goto cleanup;
798*7f2fe78bSCy Schubert         }
799*7f2fe78bSCy Schubert         if (!err)
800*7f2fe78bSCy Schubert             err = mdb_put(txn, dbc->lockout_db, &key, &val, 0);
801*7f2fe78bSCy Schubert         if (!err) {
802*7f2fe78bSCy Schubert             err = mdb_txn_commit(txn);
803*7f2fe78bSCy Schubert             txn = NULL;
804*7f2fe78bSCy Schubert         }
805*7f2fe78bSCy Schubert         if (err) {
806*7f2fe78bSCy Schubert             ret = klerr(context, err, _("LMDB lockout write failure"));
807*7f2fe78bSCy Schubert             goto cleanup;
808*7f2fe78bSCy Schubert         }
809*7f2fe78bSCy Schubert     }
810*7f2fe78bSCy Schubert 
811*7f2fe78bSCy Schubert cleanup:
812*7f2fe78bSCy Schubert     mdb_txn_abort(txn);
813*7f2fe78bSCy Schubert     krb5_free_unparsed_name(context, name);
814*7f2fe78bSCy Schubert     return ret;
815*7f2fe78bSCy Schubert }
816*7f2fe78bSCy Schubert 
817*7f2fe78bSCy Schubert static krb5_error_code
klmdb_delete_principal(krb5_context context,krb5_const_principal searchfor)818*7f2fe78bSCy Schubert klmdb_delete_principal(krb5_context context, krb5_const_principal searchfor)
819*7f2fe78bSCy Schubert {
820*7f2fe78bSCy Schubert     krb5_error_code ret;
821*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
822*7f2fe78bSCy Schubert     char *name;
823*7f2fe78bSCy Schubert 
824*7f2fe78bSCy Schubert     if (dbc == NULL)
825*7f2fe78bSCy Schubert         return KRB5_KDB_DBNOTINITED;
826*7f2fe78bSCy Schubert 
827*7f2fe78bSCy Schubert     ret = krb5_unparse_name(context, searchfor, &name);
828*7f2fe78bSCy Schubert     if (ret)
829*7f2fe78bSCy Schubert         return ret;
830*7f2fe78bSCy Schubert 
831*7f2fe78bSCy Schubert     ret = del(context, dbc->env, dbc->princ_db, name);
832*7f2fe78bSCy Schubert     if (!ret && dbc->lockout_env != NULL)
833*7f2fe78bSCy Schubert         (void)del(context, dbc->lockout_env, dbc->lockout_db, name);
834*7f2fe78bSCy Schubert 
835*7f2fe78bSCy Schubert     krb5_free_unparsed_name(context, name);
836*7f2fe78bSCy Schubert     return ret;
837*7f2fe78bSCy Schubert }
838*7f2fe78bSCy Schubert 
839*7f2fe78bSCy Schubert static krb5_error_code
klmdb_iterate(krb5_context context,char * match_expr,krb5_error_code (* func)(void *,krb5_db_entry *),void * arg,krb5_flags iterflags)840*7f2fe78bSCy Schubert klmdb_iterate(krb5_context context, char *match_expr,
841*7f2fe78bSCy Schubert               krb5_error_code (*func)(void *, krb5_db_entry *), void *arg,
842*7f2fe78bSCy Schubert               krb5_flags iterflags)
843*7f2fe78bSCy Schubert {
844*7f2fe78bSCy Schubert     krb5_error_code ret;
845*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
846*7f2fe78bSCy Schubert     krb5_db_entry *entry;
847*7f2fe78bSCy Schubert     MDB_txn *txn = NULL;
848*7f2fe78bSCy Schubert     MDB_cursor *cursor = NULL;
849*7f2fe78bSCy Schubert     MDB_val key, val;
850*7f2fe78bSCy Schubert     MDB_cursor_op op = (iterflags & KRB5_DB_ITER_REV) ? MDB_PREV : MDB_NEXT;
851*7f2fe78bSCy Schubert     int err;
852*7f2fe78bSCy Schubert 
853*7f2fe78bSCy Schubert     if (dbc == NULL)
854*7f2fe78bSCy Schubert         return KRB5_KDB_DBNOTINITED;
855*7f2fe78bSCy Schubert 
856*7f2fe78bSCy Schubert     err = mdb_txn_begin(dbc->env, NULL, MDB_RDONLY, &txn);
857*7f2fe78bSCy Schubert     if (err)
858*7f2fe78bSCy Schubert         goto lmdb_error;
859*7f2fe78bSCy Schubert     err = mdb_cursor_open(txn, dbc->princ_db, &cursor);
860*7f2fe78bSCy Schubert     if (err)
861*7f2fe78bSCy Schubert         goto lmdb_error;
862*7f2fe78bSCy Schubert     for (;;) {
863*7f2fe78bSCy Schubert         err = mdb_cursor_get(cursor, &key, &val, op);
864*7f2fe78bSCy Schubert         if (err == MDB_NOTFOUND)
865*7f2fe78bSCy Schubert             break;
866*7f2fe78bSCy Schubert         if (err)
867*7f2fe78bSCy Schubert             goto lmdb_error;
868*7f2fe78bSCy Schubert         ret = klmdb_decode_princ(context, key.mv_data, key.mv_size,
869*7f2fe78bSCy Schubert                                  val.mv_data, val.mv_size, &entry);
870*7f2fe78bSCy Schubert         if (ret)
871*7f2fe78bSCy Schubert             goto cleanup;
872*7f2fe78bSCy Schubert         fetch_lockout(context, &key, entry);
873*7f2fe78bSCy Schubert         ret = (*func)(arg, entry);
874*7f2fe78bSCy Schubert         krb5_db_free_principal(context, entry);
875*7f2fe78bSCy Schubert         if (ret)
876*7f2fe78bSCy Schubert             goto cleanup;
877*7f2fe78bSCy Schubert     }
878*7f2fe78bSCy Schubert     ret = 0;
879*7f2fe78bSCy Schubert     goto cleanup;
880*7f2fe78bSCy Schubert 
881*7f2fe78bSCy Schubert lmdb_error:
882*7f2fe78bSCy Schubert     ret = klerr(context, err, _("LMDB principal iteration failure"));
883*7f2fe78bSCy Schubert cleanup:
884*7f2fe78bSCy Schubert     mdb_cursor_close(cursor);
885*7f2fe78bSCy Schubert     mdb_txn_abort(txn);
886*7f2fe78bSCy Schubert     return ret;
887*7f2fe78bSCy Schubert }
888*7f2fe78bSCy Schubert 
889*7f2fe78bSCy Schubert krb5_error_code
klmdb_get_policy(krb5_context context,char * name,osa_policy_ent_t * policy)890*7f2fe78bSCy Schubert klmdb_get_policy(krb5_context context, char *name, osa_policy_ent_t *policy)
891*7f2fe78bSCy Schubert {
892*7f2fe78bSCy Schubert     krb5_error_code ret;
893*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
894*7f2fe78bSCy Schubert     MDB_val key, val;
895*7f2fe78bSCy Schubert 
896*7f2fe78bSCy Schubert     *policy = NULL;
897*7f2fe78bSCy Schubert     if (dbc == NULL)
898*7f2fe78bSCy Schubert         return KRB5_KDB_DBNOTINITED;
899*7f2fe78bSCy Schubert 
900*7f2fe78bSCy Schubert     key.mv_data = name;
901*7f2fe78bSCy Schubert     key.mv_size = strlen(name);
902*7f2fe78bSCy Schubert     ret = fetch(context, dbc->policy_db, &key, &val);
903*7f2fe78bSCy Schubert     if (ret)
904*7f2fe78bSCy Schubert         return ret;
905*7f2fe78bSCy Schubert     return klmdb_decode_policy(context, name, strlen(name),
906*7f2fe78bSCy Schubert                                val.mv_data, val.mv_size, policy);
907*7f2fe78bSCy Schubert }
908*7f2fe78bSCy Schubert 
909*7f2fe78bSCy Schubert static krb5_error_code
klmdb_create_policy(krb5_context context,osa_policy_ent_t policy)910*7f2fe78bSCy Schubert klmdb_create_policy(krb5_context context, osa_policy_ent_t policy)
911*7f2fe78bSCy Schubert {
912*7f2fe78bSCy Schubert     krb5_error_code ret;
913*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
914*7f2fe78bSCy Schubert     uint8_t *enc;
915*7f2fe78bSCy Schubert     size_t len;
916*7f2fe78bSCy Schubert 
917*7f2fe78bSCy Schubert     if (dbc == NULL)
918*7f2fe78bSCy Schubert         return KRB5_KDB_DBNOTINITED;
919*7f2fe78bSCy Schubert 
920*7f2fe78bSCy Schubert     ret = klmdb_encode_policy(context, policy, &enc, &len);
921*7f2fe78bSCy Schubert     if (ret)
922*7f2fe78bSCy Schubert         return ret;
923*7f2fe78bSCy Schubert     ret = put(context, dbc->policy_db, policy->name, enc, len, TRUE, FALSE);
924*7f2fe78bSCy Schubert     free(enc);
925*7f2fe78bSCy Schubert     return ret;
926*7f2fe78bSCy Schubert }
927*7f2fe78bSCy Schubert 
928*7f2fe78bSCy Schubert static krb5_error_code
klmdb_put_policy(krb5_context context,osa_policy_ent_t policy)929*7f2fe78bSCy Schubert klmdb_put_policy(krb5_context context, osa_policy_ent_t policy)
930*7f2fe78bSCy Schubert {
931*7f2fe78bSCy Schubert     krb5_error_code ret;
932*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
933*7f2fe78bSCy Schubert     uint8_t *enc;
934*7f2fe78bSCy Schubert     size_t len;
935*7f2fe78bSCy Schubert 
936*7f2fe78bSCy Schubert     if (dbc == NULL)
937*7f2fe78bSCy Schubert         return KRB5_KDB_DBNOTINITED;
938*7f2fe78bSCy Schubert 
939*7f2fe78bSCy Schubert     ret = klmdb_encode_policy(context, policy, &enc, &len);
940*7f2fe78bSCy Schubert     if (ret)
941*7f2fe78bSCy Schubert         return ret;
942*7f2fe78bSCy Schubert     ret = put(context, dbc->policy_db, policy->name, enc, len, FALSE, TRUE);
943*7f2fe78bSCy Schubert     free(enc);
944*7f2fe78bSCy Schubert     return ret;
945*7f2fe78bSCy Schubert }
946*7f2fe78bSCy Schubert 
947*7f2fe78bSCy Schubert static krb5_error_code
klmdb_iter_policy(krb5_context context,char * match_entry,osa_adb_iter_policy_func func,void * arg)948*7f2fe78bSCy Schubert klmdb_iter_policy(krb5_context context, char *match_entry,
949*7f2fe78bSCy Schubert                   osa_adb_iter_policy_func func, void *arg)
950*7f2fe78bSCy Schubert {
951*7f2fe78bSCy Schubert     krb5_error_code ret;
952*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
953*7f2fe78bSCy Schubert     osa_policy_ent_t pol;
954*7f2fe78bSCy Schubert     MDB_txn *txn = NULL;
955*7f2fe78bSCy Schubert     MDB_cursor *cursor = NULL;
956*7f2fe78bSCy Schubert     MDB_val key, val;
957*7f2fe78bSCy Schubert     int err;
958*7f2fe78bSCy Schubert 
959*7f2fe78bSCy Schubert     if (dbc == NULL)
960*7f2fe78bSCy Schubert         return KRB5_KDB_DBNOTINITED;
961*7f2fe78bSCy Schubert 
962*7f2fe78bSCy Schubert     err = mdb_txn_begin(dbc->env, NULL, MDB_RDONLY, &txn);
963*7f2fe78bSCy Schubert     if (err)
964*7f2fe78bSCy Schubert         goto lmdb_error;
965*7f2fe78bSCy Schubert     err = mdb_cursor_open(txn, dbc->policy_db, &cursor);
966*7f2fe78bSCy Schubert     if (err)
967*7f2fe78bSCy Schubert         goto lmdb_error;
968*7f2fe78bSCy Schubert     for (;;) {
969*7f2fe78bSCy Schubert         err = mdb_cursor_get(cursor, &key, &val, MDB_NEXT);
970*7f2fe78bSCy Schubert         if (err == MDB_NOTFOUND)
971*7f2fe78bSCy Schubert             break;
972*7f2fe78bSCy Schubert         if (err)
973*7f2fe78bSCy Schubert             goto lmdb_error;
974*7f2fe78bSCy Schubert         ret = klmdb_decode_policy(context, key.mv_data, key.mv_size,
975*7f2fe78bSCy Schubert                                   val.mv_data, val.mv_size, &pol);
976*7f2fe78bSCy Schubert         if (ret)
977*7f2fe78bSCy Schubert             goto cleanup;
978*7f2fe78bSCy Schubert         (*func)(arg, pol);
979*7f2fe78bSCy Schubert         krb5_db_free_policy(context, pol);
980*7f2fe78bSCy Schubert     }
981*7f2fe78bSCy Schubert     ret = 0;
982*7f2fe78bSCy Schubert     goto cleanup;
983*7f2fe78bSCy Schubert 
984*7f2fe78bSCy Schubert lmdb_error:
985*7f2fe78bSCy Schubert     ret = klerr(context, err, _("LMDB policy iteration failure"));
986*7f2fe78bSCy Schubert cleanup:
987*7f2fe78bSCy Schubert     mdb_cursor_close(cursor);
988*7f2fe78bSCy Schubert     mdb_txn_abort(txn);
989*7f2fe78bSCy Schubert     return ret;
990*7f2fe78bSCy Schubert }
991*7f2fe78bSCy Schubert 
992*7f2fe78bSCy Schubert static krb5_error_code
klmdb_delete_policy(krb5_context context,char * policy)993*7f2fe78bSCy Schubert klmdb_delete_policy(krb5_context context, char *policy)
994*7f2fe78bSCy Schubert {
995*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
996*7f2fe78bSCy Schubert 
997*7f2fe78bSCy Schubert     if (dbc == NULL)
998*7f2fe78bSCy Schubert         return KRB5_KDB_DBNOTINITED;
999*7f2fe78bSCy Schubert     return del(context, dbc->env, dbc->policy_db, policy);
1000*7f2fe78bSCy Schubert }
1001*7f2fe78bSCy Schubert 
1002*7f2fe78bSCy Schubert static krb5_error_code
klmdb_promote_db(krb5_context context,char * conf_section,char ** db_args)1003*7f2fe78bSCy Schubert klmdb_promote_db(krb5_context context, char *conf_section, char **db_args)
1004*7f2fe78bSCy Schubert {
1005*7f2fe78bSCy Schubert     krb5_error_code ret = 0;
1006*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
1007*7f2fe78bSCy Schubert     int err;
1008*7f2fe78bSCy Schubert 
1009*7f2fe78bSCy Schubert     if (dbc == NULL)
1010*7f2fe78bSCy Schubert         return KRB5_KDB_DBNOTINITED;
1011*7f2fe78bSCy Schubert     if (dbc->load_txn == NULL)
1012*7f2fe78bSCy Schubert         return EINVAL;
1013*7f2fe78bSCy Schubert     err = mdb_txn_commit(dbc->load_txn);
1014*7f2fe78bSCy Schubert     dbc->load_txn = NULL;
1015*7f2fe78bSCy Schubert     if (err)
1016*7f2fe78bSCy Schubert         ret = klerr(context, err, _("LMDB transaction commit failure"));
1017*7f2fe78bSCy Schubert     klmdb_fini(context);
1018*7f2fe78bSCy Schubert     return ret;
1019*7f2fe78bSCy Schubert }
1020*7f2fe78bSCy Schubert 
1021*7f2fe78bSCy Schubert static krb5_error_code
klmdb_check_policy_as(krb5_context context,krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,krb5_timestamp kdc_time,const char ** status,krb5_pa_data *** e_data)1022*7f2fe78bSCy Schubert klmdb_check_policy_as(krb5_context context, krb5_kdc_req *request,
1023*7f2fe78bSCy Schubert                       krb5_db_entry *client, krb5_db_entry *server,
1024*7f2fe78bSCy Schubert                       krb5_timestamp kdc_time, const char **status,
1025*7f2fe78bSCy Schubert                       krb5_pa_data ***e_data)
1026*7f2fe78bSCy Schubert {
1027*7f2fe78bSCy Schubert     krb5_error_code ret;
1028*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
1029*7f2fe78bSCy Schubert 
1030*7f2fe78bSCy Schubert     if (dbc->disable_lockout)
1031*7f2fe78bSCy Schubert         return 0;
1032*7f2fe78bSCy Schubert 
1033*7f2fe78bSCy Schubert     ret = klmdb_lockout_check_policy(context, client, kdc_time);
1034*7f2fe78bSCy Schubert     if (ret == KRB5KDC_ERR_CLIENT_REVOKED)
1035*7f2fe78bSCy Schubert         *status = "LOCKED_OUT";
1036*7f2fe78bSCy Schubert     return ret;
1037*7f2fe78bSCy Schubert }
1038*7f2fe78bSCy Schubert 
1039*7f2fe78bSCy Schubert static void
klmdb_audit_as_req(krb5_context context,krb5_kdc_req * request,const krb5_address * local_addr,const krb5_address * remote_addr,krb5_db_entry * client,krb5_db_entry * server,krb5_timestamp authtime,krb5_error_code status)1040*7f2fe78bSCy Schubert klmdb_audit_as_req(krb5_context context, krb5_kdc_req *request,
1041*7f2fe78bSCy Schubert                    const krb5_address *local_addr,
1042*7f2fe78bSCy Schubert                    const krb5_address *remote_addr, krb5_db_entry *client,
1043*7f2fe78bSCy Schubert                    krb5_db_entry *server, krb5_timestamp authtime,
1044*7f2fe78bSCy Schubert                    krb5_error_code status)
1045*7f2fe78bSCy Schubert {
1046*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
1047*7f2fe78bSCy Schubert 
1048*7f2fe78bSCy Schubert     (void)klmdb_lockout_audit(context, client, authtime, status,
1049*7f2fe78bSCy Schubert                               dbc->disable_last_success, dbc->disable_lockout);
1050*7f2fe78bSCy Schubert }
1051*7f2fe78bSCy Schubert 
1052*7f2fe78bSCy Schubert krb5_error_code
klmdb_update_lockout(krb5_context context,krb5_db_entry * entry,krb5_timestamp stamp,krb5_boolean zero_fail_count,krb5_boolean set_last_success,krb5_boolean set_last_failure)1053*7f2fe78bSCy Schubert klmdb_update_lockout(krb5_context context, krb5_db_entry *entry,
1054*7f2fe78bSCy Schubert                      krb5_timestamp stamp, krb5_boolean zero_fail_count,
1055*7f2fe78bSCy Schubert                      krb5_boolean set_last_success,
1056*7f2fe78bSCy Schubert                      krb5_boolean set_last_failure)
1057*7f2fe78bSCy Schubert {
1058*7f2fe78bSCy Schubert     krb5_error_code ret;
1059*7f2fe78bSCy Schubert     klmdb_context *dbc = context->dal_handle->db_context;
1060*7f2fe78bSCy Schubert     krb5_db_entry dummy = { 0 };
1061*7f2fe78bSCy Schubert     uint8_t lockout[LOCKOUT_RECORD_LEN];
1062*7f2fe78bSCy Schubert     MDB_txn *txn = NULL;
1063*7f2fe78bSCy Schubert     MDB_val key, val;
1064*7f2fe78bSCy Schubert     char *name = NULL;
1065*7f2fe78bSCy Schubert     int err;
1066*7f2fe78bSCy Schubert 
1067*7f2fe78bSCy Schubert     if (dbc == NULL)
1068*7f2fe78bSCy Schubert         return KRB5_KDB_DBNOTINITED;
1069*7f2fe78bSCy Schubert     if (dbc->lockout_env == NULL)
1070*7f2fe78bSCy Schubert         return 0;
1071*7f2fe78bSCy Schubert     if (!zero_fail_count && !set_last_success && !set_last_failure)
1072*7f2fe78bSCy Schubert         return 0;
1073*7f2fe78bSCy Schubert 
1074*7f2fe78bSCy Schubert     ret = krb5_unparse_name(context, entry->princ, &name);
1075*7f2fe78bSCy Schubert     if (ret)
1076*7f2fe78bSCy Schubert         goto cleanup;
1077*7f2fe78bSCy Schubert     key.mv_data = name;
1078*7f2fe78bSCy Schubert     key.mv_size = strlen(name);
1079*7f2fe78bSCy Schubert 
1080*7f2fe78bSCy Schubert     err = mdb_txn_begin(dbc->lockout_env, NULL, 0, &txn);
1081*7f2fe78bSCy Schubert     if (err)
1082*7f2fe78bSCy Schubert         goto lmdb_error;
1083*7f2fe78bSCy Schubert     /* Fetch base lockout info within txn so we update transactionally. */
1084*7f2fe78bSCy Schubert     err = mdb_get(txn, dbc->lockout_db, &key, &val);
1085*7f2fe78bSCy Schubert     if (!err && val.mv_size >= LOCKOUT_RECORD_LEN) {
1086*7f2fe78bSCy Schubert         klmdb_decode_princ_lockout(context, &dummy, val.mv_data);
1087*7f2fe78bSCy Schubert     } else {
1088*7f2fe78bSCy Schubert         dummy.last_success = entry->last_success;
1089*7f2fe78bSCy Schubert         dummy.last_failed = entry->last_failed;
1090*7f2fe78bSCy Schubert         dummy.fail_auth_count = entry->fail_auth_count;
1091*7f2fe78bSCy Schubert     }
1092*7f2fe78bSCy Schubert 
1093*7f2fe78bSCy Schubert     if (zero_fail_count)
1094*7f2fe78bSCy Schubert         dummy.fail_auth_count = 0;
1095*7f2fe78bSCy Schubert     if (set_last_success)
1096*7f2fe78bSCy Schubert         dummy.last_success = stamp;
1097*7f2fe78bSCy Schubert     if (set_last_failure) {
1098*7f2fe78bSCy Schubert         dummy.last_failed = stamp;
1099*7f2fe78bSCy Schubert         dummy.fail_auth_count++;
1100*7f2fe78bSCy Schubert     }
1101*7f2fe78bSCy Schubert 
1102*7f2fe78bSCy Schubert     klmdb_encode_princ_lockout(context, &dummy, lockout);
1103*7f2fe78bSCy Schubert     val.mv_data = lockout;
1104*7f2fe78bSCy Schubert     val.mv_size = sizeof(lockout);
1105*7f2fe78bSCy Schubert     err = mdb_put(txn, dbc->lockout_db, &key, &val, 0);
1106*7f2fe78bSCy Schubert     if (err)
1107*7f2fe78bSCy Schubert         goto lmdb_error;
1108*7f2fe78bSCy Schubert     err = mdb_txn_commit(txn);
1109*7f2fe78bSCy Schubert     txn = NULL;
1110*7f2fe78bSCy Schubert     if (err)
1111*7f2fe78bSCy Schubert         goto lmdb_error;
1112*7f2fe78bSCy Schubert     goto cleanup;
1113*7f2fe78bSCy Schubert 
1114*7f2fe78bSCy Schubert lmdb_error:
1115*7f2fe78bSCy Schubert     ret = klerr(context, err, _("LMDB lockout update failure"));
1116*7f2fe78bSCy Schubert cleanup:
1117*7f2fe78bSCy Schubert     krb5_free_unparsed_name(context, name);
1118*7f2fe78bSCy Schubert     mdb_txn_abort(txn);
1119*7f2fe78bSCy Schubert     return 0;
1120*7f2fe78bSCy Schubert }
1121*7f2fe78bSCy Schubert 
1122*7f2fe78bSCy Schubert kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_lmdb, kdb_function_table) = {
1123*7f2fe78bSCy Schubert     .maj_ver = KRB5_KDB_DAL_MAJOR_VERSION,
1124*7f2fe78bSCy Schubert     .min_ver = 0,
1125*7f2fe78bSCy Schubert     .init_library = klmdb_lib_init,
1126*7f2fe78bSCy Schubert     .fini_library = klmdb_lib_cleanup,
1127*7f2fe78bSCy Schubert     .init_module = klmdb_open,
1128*7f2fe78bSCy Schubert     .fini_module = klmdb_fini,
1129*7f2fe78bSCy Schubert     .create = klmdb_create,
1130*7f2fe78bSCy Schubert     .destroy = klmdb_destroy,
1131*7f2fe78bSCy Schubert     .get_principal = klmdb_get_principal,
1132*7f2fe78bSCy Schubert     .put_principal = klmdb_put_principal,
1133*7f2fe78bSCy Schubert     .delete_principal = klmdb_delete_principal,
1134*7f2fe78bSCy Schubert     .iterate = klmdb_iterate,
1135*7f2fe78bSCy Schubert     .create_policy = klmdb_create_policy,
1136*7f2fe78bSCy Schubert     .get_policy = klmdb_get_policy,
1137*7f2fe78bSCy Schubert     .put_policy = klmdb_put_policy,
1138*7f2fe78bSCy Schubert     .iter_policy = klmdb_iter_policy,
1139*7f2fe78bSCy Schubert     .delete_policy = klmdb_delete_policy,
1140*7f2fe78bSCy Schubert     .promote_db = klmdb_promote_db,
1141*7f2fe78bSCy Schubert     .check_policy_as = klmdb_check_policy_as,
1142*7f2fe78bSCy Schubert     .audit_as_req = klmdb_audit_as_req
1143*7f2fe78bSCy Schubert };
1144