Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/lib/hdb/hdb.c
105420 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
Key *k;
172
int i;
173
174
if (ent->free_entry)
175
(*ent->free_entry)(context, ent);
176
177
for(i = 0; i < ent->entry.keys.len; i++) {
178
k = &ent->entry.keys.val[i];
179
180
memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
181
}
182
free_hdb_entry(&ent->entry);
183
}
184
185
krb5_error_code
186
hdb_foreach(krb5_context context,
187
HDB *db,
188
unsigned flags,
189
hdb_foreach_func_t func,
190
void *data)
191
{
192
krb5_error_code ret;
193
hdb_entry_ex entry;
194
ret = db->hdb_firstkey(context, db, flags, &entry);
195
if (ret == 0)
196
krb5_clear_error_message(context);
197
while(ret == 0){
198
ret = (*func)(context, db, &entry, data);
199
hdb_free_entry(context, &entry);
200
if(ret == 0)
201
ret = db->hdb_nextkey(context, db, flags, &entry);
202
}
203
if(ret == HDB_ERR_NOENTRY)
204
ret = 0;
205
return ret;
206
}
207
208
krb5_error_code
209
hdb_check_db_format(krb5_context context, HDB *db)
210
{
211
krb5_data tag;
212
krb5_data version;
213
krb5_error_code ret, ret2;
214
unsigned ver;
215
int foo;
216
217
ret = db->hdb_lock(context, db, HDB_RLOCK);
218
if (ret)
219
return ret;
220
221
tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
222
tag.length = strlen(tag.data);
223
ret = (*db->hdb__get)(context, db, tag, &version);
224
ret2 = db->hdb_unlock(context, db);
225
if(ret)
226
return ret;
227
if (ret2)
228
return ret2;
229
foo = sscanf(version.data, "%u", &ver);
230
krb5_data_free (&version);
231
if (foo != 1)
232
return HDB_ERR_BADVERSION;
233
if(ver != HDB_DB_FORMAT)
234
return HDB_ERR_BADVERSION;
235
return 0;
236
}
237
238
krb5_error_code
239
hdb_init_db(krb5_context context, HDB *db)
240
{
241
krb5_error_code ret, ret2;
242
krb5_data tag;
243
krb5_data version;
244
char ver[32];
245
246
ret = hdb_check_db_format(context, db);
247
if(ret != HDB_ERR_NOENTRY)
248
return ret;
249
250
ret = db->hdb_lock(context, db, HDB_WLOCK);
251
if (ret)
252
return ret;
253
254
tag.data = (void *)(intptr_t)HDB_DB_FORMAT_ENTRY;
255
tag.length = strlen(tag.data);
256
snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT);
257
version.data = ver;
258
version.length = strlen(version.data) + 1; /* zero terminated */
259
ret = (*db->hdb__put)(context, db, 0, tag, version);
260
ret2 = db->hdb_unlock(context, db);
261
if (ret) {
262
if (ret2)
263
krb5_clear_error_message(context);
264
return ret;
265
}
266
return ret2;
267
}
268
269
#ifdef HAVE_DLOPEN
270
271
/*
272
* Load a dynamic backend from /usr/heimdal/lib/hdb_NAME.so,
273
* looking for the hdb_NAME_create symbol.
274
*/
275
276
static const struct hdb_method *
277
find_dynamic_method (krb5_context context,
278
const char *filename,
279
const char **rest)
280
{
281
static struct hdb_method method;
282
struct hdb_so_method *mso;
283
char *prefix, *path, *symbol;
284
const char *p;
285
void *dl;
286
size_t len;
287
288
p = strchr(filename, ':');
289
290
/* if no prefix, don't know what module to load, just ignore it */
291
if (p == NULL)
292
return NULL;
293
294
len = p - filename;
295
*rest = filename + len + 1;
296
297
prefix = malloc(len + 1);
298
if (prefix == NULL)
299
krb5_errx(context, 1, "out of memory");
300
strlcpy(prefix, filename, len + 1);
301
302
if (asprintf(&path, LIBDIR "/hdb_%s.so", prefix) == -1)
303
krb5_errx(context, 1, "out of memory");
304
305
#ifndef RTLD_NOW
306
#define RTLD_NOW 0
307
#endif
308
#ifndef RTLD_GLOBAL
309
#define RTLD_GLOBAL 0
310
#endif
311
312
dl = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
313
if (dl == NULL) {
314
krb5_warnx(context, "error trying to load dynamic module %s: %s\n",
315
path, dlerror());
316
free(prefix);
317
free(path);
318
return NULL;
319
}
320
321
if (asprintf(&symbol, "hdb_%s_interface", prefix) == -1)
322
krb5_errx(context, 1, "out of memory");
323
324
mso = (struct hdb_so_method *) dlsym(dl, symbol);
325
if (mso == NULL) {
326
krb5_warnx(context, "error finding symbol %s in %s: %s\n",
327
symbol, path, dlerror());
328
dlclose(dl);
329
free(symbol);
330
free(prefix);
331
free(path);
332
return NULL;
333
}
334
free(path);
335
free(symbol);
336
337
if (mso->version != HDB_INTERFACE_VERSION) {
338
krb5_warnx(context,
339
"error wrong version in shared module %s "
340
"version: %d should have been %d\n",
341
prefix, mso->version, HDB_INTERFACE_VERSION);
342
dlclose(dl);
343
free(prefix);
344
return NULL;
345
}
346
347
if (mso->create == NULL) {
348
krb5_errx(context, 1,
349
"no entry point function in shared mod %s ",
350
prefix);
351
dlclose(dl);
352
free(prefix);
353
return NULL;
354
}
355
356
method.create = mso->create;
357
method.prefix = prefix;
358
359
return &method;
360
}
361
#endif /* HAVE_DLOPEN */
362
363
/*
364
* find the relevant method for `filename', returning a pointer to the
365
* rest in `rest'.
366
* return NULL if there's no such method.
367
*/
368
369
static const struct hdb_method *
370
find_method (const char *filename, const char **rest)
371
{
372
const struct hdb_method *h;
373
374
for (h = methods; h->prefix != NULL; ++h) {
375
if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) {
376
*rest = filename + strlen(h->prefix);
377
return h;
378
}
379
}
380
#if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_NDBM)
381
if (strncmp(filename, "/", 1) == 0
382
|| strncmp(filename, "./", 2) == 0
383
|| strncmp(filename, "../", 3) == 0)
384
{
385
*rest = filename;
386
return &dbmetod;
387
}
388
#endif
389
390
return NULL;
391
}
392
393
krb5_error_code
394
hdb_list_builtin(krb5_context context, char **list)
395
{
396
const struct hdb_method *h;
397
size_t len = 0;
398
char *buf = NULL;
399
400
for (h = methods; h->prefix != NULL; ++h) {
401
if (h->prefix[0] == '\0')
402
continue;
403
len += strlen(h->prefix) + 2;
404
}
405
406
len += 1;
407
buf = malloc(len);
408
if (buf == NULL) {
409
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
410
return ENOMEM;
411
}
412
buf[0] = '\0';
413
414
for (h = methods; h->prefix != NULL; ++h) {
415
if (h != methods)
416
strlcat(buf, ", ", len);
417
strlcat(buf, h->prefix, len);
418
}
419
*list = buf;
420
return 0;
421
}
422
423
krb5_error_code
424
_hdb_keytab2hdb_entry(krb5_context context,
425
const krb5_keytab_entry *ktentry,
426
hdb_entry_ex *entry)
427
{
428
entry->entry.kvno = ktentry->vno;
429
entry->entry.created_by.time = ktentry->timestamp;
430
431
entry->entry.keys.val = calloc(1, sizeof(entry->entry.keys.val[0]));
432
if (entry->entry.keys.val == NULL)
433
return ENOMEM;
434
entry->entry.keys.len = 1;
435
436
entry->entry.keys.val[0].mkvno = NULL;
437
entry->entry.keys.val[0].salt = NULL;
438
439
return krb5_copy_keyblock_contents(context,
440
&ktentry->keyblock,
441
&entry->entry.keys.val[0].key);
442
}
443
444
/**
445
* Create a handle for a Kerberos database
446
*
447
* Create a handle for a Kerberos database backend specified by a
448
* filename. Doesn't create a file if its doesn't exists, you have to
449
* use O_CREAT to tell the backend to create the file.
450
*/
451
452
krb5_error_code
453
hdb_create(krb5_context context, HDB **db, const char *filename)
454
{
455
const struct hdb_method *h;
456
const char *residual;
457
krb5_error_code ret;
458
struct krb5_plugin *list = NULL, *e;
459
460
if(filename == NULL)
461
filename = HDB_DEFAULT_DB;
462
krb5_add_et_list(context, initialize_hdb_error_table_r);
463
h = find_method (filename, &residual);
464
465
if (h == NULL) {
466
ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "hdb", &list);
467
if(ret == 0 && list != NULL) {
468
for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
469
h = _krb5_plugin_get_symbol(e);
470
if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0
471
&& h->interface_version == HDB_INTERFACE_VERSION) {
472
residual = filename + strlen(h->prefix);
473
break;
474
}
475
}
476
if (e == NULL) {
477
h = NULL;
478
_krb5_plugin_free(list);
479
}
480
}
481
}
482
483
#ifdef HAVE_DLOPEN
484
if (h == NULL)
485
h = find_dynamic_method (context, filename, &residual);
486
#endif
487
if (h == NULL)
488
krb5_errx(context, 1, "No database support for %s", filename);
489
return (*h->create)(context, db, residual);
490
}
491
492