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
kdb_init_master(kadm5_server_handle_t handle,char * r,int from_keyboard)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
kdb_get_active_mkey(kadm5_server_handle_t handle,krb5_kvno * act_kvno_out,krb5_keyblock ** act_mkey_out)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 */
kdb_init_hist(kadm5_server_handle_t handle,char * r)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
create_hist(kadm5_server_handle_t handle)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
kdb_get_hist_key(kadm5_server_handle_t handle,krb5_keyblock ** keyblocks_out,krb5_kvno * kvno_out)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
kdb_free_keyblocks(kadm5_server_handle_t handle,krb5_keyblock * keyblocks)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
kdb_get_entry(kadm5_server_handle_t handle,krb5_principal principal,krb5_db_entry ** kdb_ptr,osa_princ_ent_rec * adb)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
kdb_free_entry(kadm5_server_handle_t handle,krb5_db_entry * kdb,osa_princ_ent_rec * adb)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
kdb_put_entry(kadm5_server_handle_t handle,krb5_db_entry * kdb,osa_princ_ent_rec * adb)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
kdb_delete_entry(kadm5_server_handle_t handle,krb5_principal name)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
kdb_iter_func(krb5_pointer data,krb5_db_entry * kdb)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
kdb_iter_entry(kadm5_server_handle_t handle,char * match_entry,void (* iter_fct)(void *,krb5_principal),void * data)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