Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/lib/hdb/hdb.c
34878 views
1
/*
2
* Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3
* (Royal Institute of Technology, Stockholm, Sweden).
4
* All rights reserved.
5
*
6
* Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
10
* are met:
11
*
12
* 1. Redistributions of source code must retain the above copyright
13
* notice, this list of conditions and the following disclaimer.
14
*
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
*
19
* 3. Neither the name of the Institute nor the names of its contributors
20
* may be used to endorse or promote products derived from this software
21
* without specific prior written permission.
22
*
23
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33
* SUCH DAMAGE.
34
*/
35
36
#include "krb5_locl.h"
37
#include "hdb_locl.h"
38
39
#ifdef HAVE_DLFCN_H
40
#include <dlfcn.h>
41
#endif
42
43
/*! @mainpage Heimdal database backend library
44
*
45
* @section intro Introduction
46
*
47
* Heimdal libhdb library provides the backend support for Heimdal kdc
48
* and kadmind. Its here where plugins for diffrent database engines
49
* can be pluged in and extend support for here Heimdal get the
50
* principal and policy data from.
51
*
52
* Example of Heimdal backend are:
53
* - Berkeley DB 1.85
54
* - Berkeley DB 3.0
55
* - Berkeley DB 4.0
56
* - New Berkeley DB
57
* - LDAP
58
*
59
*
60
* The project web page: http://www.h5l.org/
61
*
62
*/
63
64
const int hdb_interface_version = HDB_INTERFACE_VERSION;
65
66
static struct hdb_method methods[] = {
67
#if HAVE_DB1 || HAVE_DB3
68
{ HDB_INTERFACE_VERSION, "db:", hdb_db_create},
69
#endif
70
#if HAVE_DB1
71
{ HDB_INTERFACE_VERSION, "mit-db:", hdb_mdb_create},
72
#endif
73
#if HAVE_NDBM
74
{ HDB_INTERFACE_VERSION, "ndbm:", hdb_ndbm_create},
75
#endif
76
{ HDB_INTERFACE_VERSION, "keytab:", hdb_keytab_create},
77
#if defined(OPENLDAP) && !defined(OPENLDAP_MODULE)
78
{ HDB_INTERFACE_VERSION, "ldap:", hdb_ldap_create},
79
{ HDB_INTERFACE_VERSION, "ldapi:", hdb_ldapi_create},
80
#endif
81
#ifdef HAVE_SQLITE3
82
{ HDB_INTERFACE_VERSION, "sqlite:", hdb_sqlite_create},
83
#endif
84
{0, NULL, NULL}
85
};
86
87
#if HAVE_DB1 || HAVE_DB3
88
static struct hdb_method dbmetod =
89
{ HDB_INTERFACE_VERSION, "", hdb_db_create };
90
#elif defined(HAVE_NDBM)
91
static struct hdb_method dbmetod =
92
{ HDB_INTERFACE_VERSION, "", hdb_ndbm_create };
93
#endif
94
95
96
krb5_error_code
97
hdb_next_enctype2key(krb5_context context,
98
const hdb_entry *e,
99
krb5_enctype enctype,
100
Key **key)
101
{
102
Key *k;
103
104
for (k = *key ? (*key) + 1 : e->keys.val;
105
k < e->keys.val + e->keys.len;
106
k++)
107
{
108
if(k->key.keytype == enctype){
109
*key = k;
110
return 0;
111
}
112
}
113
krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
114
"No next enctype %d for hdb-entry",
115
(int)enctype);
116
return KRB5_PROG_ETYPE_NOSUPP; /* XXX */
117
}
118
119
krb5_error_code
120
hdb_enctype2key(krb5_context context,
121
hdb_entry *e,
122
krb5_enctype enctype,
123
Key **key)
124
{
125
*key = NULL;
126
return hdb_next_enctype2key(context, e, enctype, key);
127
}
128
129
void
130
hdb_free_key(Key *key)
131
{
132
memset(key->key.keyvalue.data,
133
0,
134
key->key.keyvalue.length);
135
free_Key(key);
136
free(key);
137
}
138
139
140
krb5_error_code
141
hdb_lock(int fd, int operation)
142
{
143
int i, code = 0;
144
145
for(i = 0; i < 3; i++){
146
code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB);
147
if(code == 0 || errno != EWOULDBLOCK)
148
break;
149
sleep(1);
150
}
151
if(code == 0)
152
return 0;
153
if(errno == EWOULDBLOCK)
154
return HDB_ERR_DB_INUSE;
155
return HDB_ERR_CANT_LOCK_DB;
156
}
157
158
krb5_error_code
159
hdb_unlock(int fd)
160
{
161
int code;
162
code = flock(fd, LOCK_UN);
163
if(code)
164
return 4711 /* XXX */;
165
return 0;
166
}
167
168
void
169
hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
170
{
171
size_t i;
172
173
if (ent->free_entry)
174
(*ent->free_entry)(context, ent);
175
176
for(i = 0; i < ent->entry.keys.len; ++i) {
177
Key *k = &ent->entry.keys.val[i];
178
179
memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
180
}
181
free_hdb_entry(&ent->entry);
182
}
183
184
krb5_error_code
185
hdb_foreach(krb5_context context,
186
HDB *db,
187
unsigned flags,
188
hdb_foreach_func_t func,
189
void *data)
190
{
191
krb5_error_code ret;
192
hdb_entry_ex entry;
193
ret = db->hdb_firstkey(context, db, flags, &entry);
194
if (ret == 0)
195
krb5_clear_error_message(context);
196
while(ret == 0){
197
ret = (*func)(context, db, &entry, data);
198
hdb_free_entry(context, &entry);
199
if(ret == 0)
200
ret = db->hdb_nextkey(context, db, flags, &entry);
201
}
202
if(ret == HDB_ERR_NOENTRY)
203
ret = 0;
204
return ret;
205
}
206
207
krb5_error_code
208
hdb_check_db_format(krb5_context context, HDB *db)
209
{
210
krb5_data tag;
211
krb5_data version;
212
krb5_error_code ret, ret2;
213
unsigned ver;
214
int foo;
215
216
ret = db->hdb_lock(context, db, HDB_RLOCK);
217
if (ret)
218
return ret;
219
220
tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
221
tag.length = strlen(tag.data);
222
ret = (*db->hdb__get)(context, db, tag, &version);
223
ret2 = db->hdb_unlock(context, db);
224
if(ret)
225
return ret;
226
if (ret2)
227
return ret2;
228
foo = sscanf(version.data, "%u", &ver);
229
krb5_data_free (&version);
230
if (foo != 1)
231
return HDB_ERR_BADVERSION;
232
if(ver != HDB_DB_FORMAT)
233
return HDB_ERR_BADVERSION;
234
return 0;
235
}
236
237
krb5_error_code
238
hdb_init_db(krb5_context context, HDB *db)
239
{
240
krb5_error_code ret, ret2;
241
krb5_data tag;
242
krb5_data version;
243
char ver[32];
244
245
ret = hdb_check_db_format(context, db);
246
if(ret != HDB_ERR_NOENTRY)
247
return ret;
248
249
ret = db->hdb_lock(context, db, HDB_WLOCK);
250
if (ret)
251
return ret;
252
253
tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
254
tag.length = strlen(tag.data);
255
snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT);
256
version.data = ver;
257
version.length = strlen(version.data) + 1; /* zero terminated */
258
ret = (*db->hdb__put)(context, db, 0, tag, version);
259
ret2 = db->hdb_unlock(context, db);
260
if (ret) {
261
if (ret2)
262
krb5_clear_error_message(context);
263
return ret;
264
}
265
return ret2;
266
}
267
268
#ifdef HAVE_DLOPEN
269
270
/*
271
* Load a dynamic backend from /usr/heimdal/lib/hdb_NAME.so,
272
* looking for the hdb_NAME_create symbol.
273
*/
274
275
static const struct hdb_method *
276
find_dynamic_method (krb5_context context,
277
const char *filename,
278
const char **rest)
279
{
280
static struct hdb_method method;
281
struct hdb_so_method *mso;
282
char *prefix, *path, *symbol;
283
const char *p;
284
void *dl;
285
size_t len;
286
287
p = strchr(filename, ':');
288
289
/* if no prefix, don't know what module to load, just ignore it */
290
if (p == NULL)
291
return NULL;
292
293
len = p - filename;
294
*rest = filename + len + 1;
295
296
prefix = malloc(len + 1);
297
if (prefix == NULL)
298
krb5_errx(context, 1, "out of memory");
299
strlcpy(prefix, filename, len + 1);
300
301
if (asprintf(&path, LIBDIR "/hdb_%s.so", prefix) == -1)
302
krb5_errx(context, 1, "out of memory");
303
304
#ifndef RTLD_NOW
305
#define RTLD_NOW 0
306
#endif
307
#ifndef RTLD_GLOBAL
308
#define RTLD_GLOBAL 0
309
#endif
310
311
dl = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
312
if (dl == NULL) {
313
krb5_warnx(context, "error trying to load dynamic module %s: %s\n",
314
path, dlerror());
315
free(prefix);
316
free(path);
317
return NULL;
318
}
319
320
if (asprintf(&symbol, "hdb_%s_interface", prefix) == -1)
321
krb5_errx(context, 1, "out of memory");
322
323
mso = (struct hdb_so_method *) dlsym(dl, symbol);
324
if (mso == NULL) {
325
krb5_warnx(context, "error finding symbol %s in %s: %s\n",
326
symbol, path, dlerror());
327
dlclose(dl);
328
free(symbol);
329
free(prefix);
330
free(path);
331
return NULL;
332
}
333
free(path);
334
free(symbol);
335
336
if (mso->version != HDB_INTERFACE_VERSION) {
337
krb5_warnx(context,
338
"error wrong version in shared module %s "
339
"version: %d should have been %d\n",
340
prefix, mso->version, HDB_INTERFACE_VERSION);
341
dlclose(dl);
342
free(prefix);
343
return NULL;
344
}
345
346
if (mso->create == NULL) {
347
krb5_errx(context, 1,
348
"no entry point function in shared mod %s ",
349
prefix);
350
dlclose(dl);
351
free(prefix);
352
return NULL;
353
}
354
355
method.create = mso->create;
356
method.prefix = prefix;
357
358
return &method;
359
}
360
#endif /* HAVE_DLOPEN */
361
362
/*
363
* find the relevant method for `filename', returning a pointer to the
364
* rest in `rest'.
365
* return NULL if there's no such method.
366
*/
367
368
static const struct hdb_method *
369
find_method (const char *filename, const char **rest)
370
{
371
const struct hdb_method *h;
372
373
for (h = methods; h->prefix != NULL; ++h) {
374
if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) {
375
*rest = filename + strlen(h->prefix);
376
return h;
377
}
378
}
379
#if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_NDBM)
380
if (strncmp(filename, "/", 1) == 0
381
|| strncmp(filename, "./", 2) == 0
382
|| strncmp(filename, "../", 3) == 0)
383
{
384
*rest = filename;
385
return &dbmetod;
386
}
387
#endif
388
389
return NULL;
390
}
391
392
krb5_error_code
393
hdb_list_builtin(krb5_context context, char **list)
394
{
395
const struct hdb_method *h;
396
size_t len = 0;
397
char *buf = NULL;
398
399
for (h = methods; h->prefix != NULL; ++h) {
400
if (h->prefix[0] == '\0')
401
continue;
402
len += strlen(h->prefix) + 2;
403
}
404
405
len += 1;
406
buf = malloc(len);
407
if (buf == NULL) {
408
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
409
return ENOMEM;
410
}
411
buf[0] = '\0';
412
413
for (h = methods; h->prefix != NULL; ++h) {
414
if (h != methods)
415
strlcat(buf, ", ", len);
416
strlcat(buf, h->prefix, len);
417
}
418
*list = buf;
419
return 0;
420
}
421
422
krb5_error_code
423
_hdb_keytab2hdb_entry(krb5_context context,
424
const krb5_keytab_entry *ktentry,
425
hdb_entry_ex *entry)
426
{
427
entry->entry.kvno = ktentry->vno;
428
entry->entry.created_by.time = ktentry->timestamp;
429
430
entry->entry.keys.val = calloc(1, sizeof(entry->entry.keys.val[0]));
431
if (entry->entry.keys.val == NULL)
432
return ENOMEM;
433
entry->entry.keys.len = 1;
434
435
entry->entry.keys.val[0].mkvno = NULL;
436
entry->entry.keys.val[0].salt = NULL;
437
438
return krb5_copy_keyblock_contents(context,
439
&ktentry->keyblock,
440
&entry->entry.keys.val[0].key);
441
}
442
443
/**
444
* Create a handle for a Kerberos database
445
*
446
* Create a handle for a Kerberos database backend specified by a
447
* filename. Doesn't create a file if its doesn't exists, you have to
448
* use O_CREAT to tell the backend to create the file.
449
*/
450
451
krb5_error_code
452
hdb_create(krb5_context context, HDB **db, const char *filename)
453
{
454
const struct hdb_method *h;
455
const char *residual;
456
krb5_error_code ret;
457
struct krb5_plugin *list = NULL, *e;
458
459
if(filename == NULL)
460
filename = HDB_DEFAULT_DB;
461
krb5_add_et_list(context, initialize_hdb_error_table_r);
462
h = find_method (filename, &residual);
463
464
if (h == NULL) {
465
ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "hdb", &list);
466
if(ret == 0 && list != NULL) {
467
for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
468
h = _krb5_plugin_get_symbol(e);
469
if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0
470
&& h->interface_version == HDB_INTERFACE_VERSION) {
471
residual = filename + strlen(h->prefix);
472
break;
473
}
474
}
475
if (e == NULL) {
476
h = NULL;
477
_krb5_plugin_free(list);
478
}
479
}
480
}
481
482
#ifdef HAVE_DLOPEN
483
if (h == NULL)
484
h = find_dynamic_method (context, filename, &residual);
485
#endif
486
if (h == NULL)
487
krb5_errx(context, 1, "No database support for %s", filename);
488
return (*h->create)(context, db, residual);
489
}
490
491