xref: /freebsd/crypto/krb5/src/lib/kadm5/srv/server_kdb.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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