Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/lib/kadm5/srv/server_kdb.c
39566 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/*
3
* Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
4
*
5
* $Header$
6
*/
7
8
/*
9
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
10
* Use is subject to license terms.
11
*/
12
13
#include "k5-int.h"
14
#include <kadm5/admin.h>
15
#include "server_internal.h"
16
17
krb5_principal master_princ;
18
krb5_keyblock master_keyblock; /* local mkey */
19
krb5_db_entry master_db;
20
21
krb5_principal hist_princ;
22
23
/* much of this code is stolen from the kdc. there should be some
24
library code to deal with this. */
25
26
krb5_error_code kdb_init_master(kadm5_server_handle_t handle,
27
char *r, int from_keyboard)
28
{
29
int ret = 0;
30
char *realm;
31
krb5_boolean from_kbd = FALSE;
32
krb5_kvno mkvno = IGNORE_VNO;
33
34
if (from_keyboard)
35
from_kbd = TRUE;
36
37
if (r == NULL) {
38
if ((ret = krb5_get_default_realm(handle->context, &realm)))
39
return ret;
40
} else {
41
realm = r;
42
}
43
44
krb5_free_principal(handle->context, master_princ);
45
master_princ = NULL;
46
if ((ret = krb5_db_setup_mkey_name(handle->context,
47
handle->params.mkey_name,
48
realm, NULL, &master_princ)))
49
goto done;
50
51
krb5_free_keyblock_contents(handle->context, &master_keyblock);
52
master_keyblock.enctype = handle->params.enctype;
53
54
/*
55
* Fetch the local mkey, may not be the latest but that's okay because we
56
* really want the list of all mkeys and those can be retrieved with any
57
* valid mkey.
58
*/
59
ret = krb5_db_fetch_mkey(handle->context, master_princ,
60
master_keyblock.enctype, from_kbd,
61
FALSE /* only prompt once */,
62
handle->params.stash_file,
63
&mkvno /* get the kvno of the returned mkey */,
64
NULL /* I'm not sure about this,
65
but it's what the kdc does --marc */,
66
&master_keyblock);
67
if (ret)
68
goto done;
69
70
ret = krb5_db_fetch_mkey_list(handle->context, master_princ,
71
&master_keyblock);
72
if (ret)
73
krb5_db_fini(handle->context);
74
75
done:
76
if (r == NULL)
77
free(realm);
78
79
return(ret);
80
}
81
82
/* Fetch the currently active master key version number and keyblock. */
83
krb5_error_code
84
kdb_get_active_mkey(kadm5_server_handle_t handle, krb5_kvno *act_kvno_out,
85
krb5_keyblock **act_mkey_out)
86
{
87
krb5_error_code ret;
88
krb5_actkvno_node *active_mkey_list;
89
90
ret = krb5_dbe_fetch_act_key_list(handle->context, master_princ,
91
&active_mkey_list);
92
if (ret)
93
return ret;
94
ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list,
95
act_kvno_out, act_mkey_out);
96
krb5_dbe_free_actkvno_list(handle->context, active_mkey_list);
97
return ret;
98
}
99
100
/*
101
* Function: kdb_init_hist
102
*
103
* Purpose: Initializes the hist_princ variable.
104
*
105
* Arguments:
106
*
107
* handle (r) kadm5 api server handle
108
* r (r) realm of history principal to use, or NULL
109
*
110
* Effects: This function sets the value of the hist_princ global variable.
111
*/
112
krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r)
113
{
114
int ret = 0;
115
char *realm, *hist_name;
116
117
if (r == NULL) {
118
if ((ret = krb5_get_default_realm(handle->context, &realm)))
119
return ret;
120
} else {
121
realm = r;
122
}
123
124
if (asprintf(&hist_name, "%s@%s", KADM5_HIST_PRINCIPAL, realm) < 0) {
125
hist_name = NULL;
126
goto done;
127
}
128
129
krb5_free_principal(handle->context, hist_princ);
130
hist_princ = NULL;
131
if ((ret = krb5_parse_name(handle->context, hist_name, &hist_princ)))
132
goto done;
133
134
done:
135
free(hist_name);
136
if (r == NULL)
137
free(realm);
138
return ret;
139
}
140
141
static krb5_error_code
142
create_hist(kadm5_server_handle_t handle)
143
{
144
kadm5_ret_t ret;
145
krb5_key_salt_tuple ks[1];
146
kadm5_principal_ent_rec ent;
147
long mask = KADM5_PRINCIPAL | KADM5_MAX_LIFE | KADM5_ATTRIBUTES;
148
149
/* Create the history principal. */
150
memset(&ent, 0, sizeof(ent));
151
ent.principal = hist_princ;
152
ent.max_life = KRB5_KDB_DISALLOW_ALL_TIX;
153
ent.attributes = 0;
154
ks[0].ks_enctype = handle->params.enctype;
155
ks[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
156
ret = kadm5_create_principal_3(handle, &ent, mask, 1, ks, NULL);
157
if (ret)
158
return ret;
159
160
/* For better compatibility with pre-1.8 libkadm5 code, we want the
161
* initial history kvno to be 2, so re-randomize it. */
162
return kadm5_randkey_principal_3(handle, ent.principal, 0, 1, ks,
163
NULL, NULL);
164
}
165
166
/*
167
* Fetch the current history key(s), creating the history principal if
168
* necessary. Database created since krb5 1.3 will have only one key, but
169
* databases created before that may have multiple keys (of the same kvno)
170
* and we need to try them all. History keys will be returned in a list
171
* terminated by an entry with enctype 0.
172
*/
173
krb5_error_code
174
kdb_get_hist_key(kadm5_server_handle_t handle, krb5_keyblock **keyblocks_out,
175
krb5_kvno *kvno_out)
176
{
177
krb5_error_code ret;
178
krb5_db_entry *kdb;
179
krb5_keyblock *mkey, *kblist = NULL;
180
krb5_int16 i;
181
182
/* Fetch the history principal, creating it if necessary. */
183
ret = kdb_get_entry(handle, hist_princ, &kdb, NULL);
184
if (ret == KADM5_UNK_PRINC) {
185
ret = create_hist(handle);
186
if (ret)
187
return ret;
188
ret = kdb_get_entry(handle, hist_princ, &kdb, NULL);
189
}
190
if (ret)
191
return ret;
192
193
if (kdb->n_key_data <= 0) {
194
ret = KRB5_KDB_NO_MATCHING_KEY;
195
k5_setmsg(handle->context, ret,
196
_("History entry contains no key data"));
197
goto done;
198
}
199
200
ret = krb5_dbe_find_mkey(handle->context, kdb, &mkey);
201
if (ret)
202
goto done;
203
204
kblist = k5calloc(kdb->n_key_data + 1, sizeof(*kblist), &ret);
205
if (kblist == NULL)
206
goto done;
207
for (i = 0; i < kdb->n_key_data; i++) {
208
ret = krb5_dbe_decrypt_key_data(handle->context, mkey,
209
&kdb->key_data[i], &kblist[i],
210
NULL);
211
if (ret)
212
goto done;
213
}
214
215
*keyblocks_out = kblist;
216
kblist = NULL;
217
*kvno_out = kdb->key_data[0].key_data_kvno;
218
219
done:
220
kdb_free_entry(handle, kdb, NULL);
221
kdb_free_keyblocks(handle, kblist);
222
return ret;
223
}
224
225
/* Free all keyblocks in a list (terminated by a keyblock with enctype 0). */
226
void
227
kdb_free_keyblocks(kadm5_server_handle_t handle, krb5_keyblock *keyblocks)
228
{
229
krb5_keyblock *kb;
230
231
if (keyblocks == NULL)
232
return;
233
for (kb = keyblocks; kb->enctype != 0; kb++)
234
krb5_free_keyblock_contents(handle->context, kb);
235
free(keyblocks);
236
}
237
238
/*
239
* Function: kdb_get_entry
240
*
241
* Purpose: Gets an entry from the kerberos database and breaks
242
* it out into a krb5_db_entry and an osa_princ_ent_t.
243
*
244
* Arguments:
245
*
246
* handle (r) the server_handle
247
* principal (r) the principal to get
248
* kdb (w) krb5_db_entry to create
249
* adb (w) osa_princ_ent_rec to fill in
250
*
251
* when the caller is done with kdb and adb, kdb_free_entry must be
252
* called to release them. The adb record is filled in with the
253
* contents of the KRB5_TL_KADM_DATA record; if that record doesn't
254
* exist, an empty but valid adb record is returned.
255
*/
256
krb5_error_code
257
kdb_get_entry(kadm5_server_handle_t handle,
258
krb5_principal principal, krb5_db_entry **kdb_ptr,
259
osa_princ_ent_rec *adb)
260
{
261
krb5_error_code ret;
262
krb5_tl_data tl_data;
263
XDR xdrs;
264
krb5_db_entry *kdb;
265
266
*kdb_ptr = NULL;
267
268
ret = krb5_db_get_principal(handle->context, principal, 0, &kdb);
269
if (ret == KRB5_KDB_NOENTRY)
270
return(KADM5_UNK_PRINC);
271
if (ret)
272
return(ret);
273
274
if (adb) {
275
memset(adb, 0, sizeof(*adb));
276
277
tl_data.tl_data_type = KRB5_TL_KADM_DATA;
278
/*
279
* XXX Currently, lookup_tl_data always returns zero; it sets
280
* tl_data->tl_data_length to zero if the type isn't found.
281
* This should be fixed...
282
*/
283
if ((ret = krb5_dbe_lookup_tl_data(handle->context, kdb, &tl_data))
284
|| (tl_data.tl_data_length == 0)) {
285
/* there's no admin data. this can happen, if the admin
286
server is put into production after some principals
287
are created. In this case, return valid admin
288
data (which is all zeros with the hist_kvno filled
289
in), and when the entry is written, the admin
290
data will get stored correctly. */
291
292
adb->admin_history_kvno = INITIAL_HIST_KVNO;
293
*kdb_ptr = kdb;
294
return(ret);
295
}
296
297
xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents,
298
tl_data.tl_data_length, XDR_DECODE);
299
if (! xdr_osa_princ_ent_rec(&xdrs, adb)) {
300
xdr_destroy(&xdrs);
301
krb5_db_free_principal(handle->context, kdb);
302
return(KADM5_XDR_FAILURE);
303
}
304
xdr_destroy(&xdrs);
305
}
306
307
*kdb_ptr = kdb;
308
return(0);
309
}
310
311
/*
312
* Function: kdb_free_entry
313
*
314
* Purpose: frees the resources allocated by kdb_get_entry
315
*
316
* Arguments:
317
*
318
* handle (r) the server_handle
319
* kdb (w) krb5_db_entry to fill in
320
* adb (w) osa_princ_ent_rec to fill in
321
*
322
* when the caller is done with kdb and adb, kdb_free_entry must be
323
* called to release them.
324
*/
325
326
krb5_error_code
327
kdb_free_entry(kadm5_server_handle_t handle,
328
krb5_db_entry *kdb, osa_princ_ent_rec *adb)
329
{
330
XDR xdrs;
331
332
333
if (kdb)
334
krb5_db_free_principal(handle->context, kdb);
335
336
if (adb) {
337
xdrmem_create(&xdrs, NULL, 0, XDR_FREE);
338
xdr_osa_princ_ent_rec(&xdrs, adb);
339
xdr_destroy(&xdrs);
340
}
341
342
return(0);
343
}
344
345
/*
346
* Function: kdb_put_entry
347
*
348
* Purpose: Stores the osa_princ_ent_t and krb5_db_entry into to
349
* database.
350
*
351
* Arguments:
352
*
353
* handle (r) the server_handle
354
* kdb (r/w) the krb5_db_entry to store
355
* adb (r) the osa_princ_db_ent to store
356
*
357
* Effects:
358
*
359
* The last modifier field of the kdb is set to the caller at now.
360
* adb is encoded with xdr_osa_princ_ent_ret and stored in kbd as
361
* KRB5_TL_KADM_DATA. kdb is then written to the database.
362
*/
363
krb5_error_code
364
kdb_put_entry(kadm5_server_handle_t handle,
365
krb5_db_entry *kdb, osa_princ_ent_rec *adb)
366
{
367
krb5_error_code ret;
368
krb5_timestamp now;
369
XDR xdrs;
370
krb5_tl_data tl_data;
371
372
ret = krb5_timeofday(handle->context, &now);
373
if (ret)
374
return(ret);
375
376
ret = krb5_dbe_update_mod_princ_data(handle->context, kdb, now,
377
handle->current_caller);
378
if (ret)
379
return(ret);
380
381
xdralloc_create(&xdrs, XDR_ENCODE);
382
if(! xdr_osa_princ_ent_rec(&xdrs, adb)) {
383
xdr_destroy(&xdrs);
384
return(KADM5_XDR_FAILURE);
385
}
386
tl_data.tl_data_type = KRB5_TL_KADM_DATA;
387
tl_data.tl_data_length = xdr_getpos(&xdrs);
388
tl_data.tl_data_contents = (krb5_octet *)xdralloc_getdata(&xdrs);
389
390
ret = krb5_dbe_update_tl_data(handle->context, kdb, &tl_data);
391
392
xdr_destroy(&xdrs);
393
394
if (ret)
395
return(ret);
396
397
/* we are always updating TL data */
398
kdb->mask |= KADM5_TL_DATA;
399
400
ret = krb5_db_put_principal(handle->context, kdb);
401
if (ret)
402
return(ret);
403
404
return(0);
405
}
406
407
krb5_error_code
408
kdb_delete_entry(kadm5_server_handle_t handle, krb5_principal name)
409
{
410
krb5_error_code ret;
411
412
ret = krb5_db_delete_principal(handle->context, name);
413
return (ret == KRB5_KDB_NOENTRY) ? KADM5_UNK_PRINC : ret;
414
}
415
416
typedef struct _iter_data {
417
void (*func)(void *, krb5_principal);
418
void *data;
419
} iter_data;
420
421
static krb5_error_code
422
kdb_iter_func(krb5_pointer data, krb5_db_entry *kdb)
423
{
424
iter_data *id = (iter_data *) data;
425
426
(*(id->func))(id->data, kdb->princ);
427
428
return(0);
429
}
430
431
krb5_error_code
432
kdb_iter_entry(kadm5_server_handle_t handle, char *match_entry,
433
void (*iter_fct)(void *, krb5_principal), void *data)
434
{
435
iter_data id;
436
krb5_error_code ret;
437
438
id.func = iter_fct;
439
id.data = data;
440
441
ret = krb5_db_iterate(handle->context, match_entry, kdb_iter_func, &id, 0);
442
if (ret)
443
return(ret);
444
445
return(0);
446
}
447
448