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