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