Path: blob/main/crypto/krb5/src/lib/kadm5/srv/server_kdb.c
39566 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/*2* Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved3*4* $Header$5*/67/*8* Copyright 2009 Sun Microsystems, Inc. All rights reserved.9* Use is subject to license terms.10*/1112#include "k5-int.h"13#include <kadm5/admin.h>14#include "server_internal.h"1516krb5_principal master_princ;17krb5_keyblock master_keyblock; /* local mkey */18krb5_db_entry master_db;1920krb5_principal hist_princ;2122/* much of this code is stolen from the kdc. there should be some23library code to deal with this. */2425krb5_error_code kdb_init_master(kadm5_server_handle_t handle,26char *r, int from_keyboard)27{28int ret = 0;29char *realm;30krb5_boolean from_kbd = FALSE;31krb5_kvno mkvno = IGNORE_VNO;3233if (from_keyboard)34from_kbd = TRUE;3536if (r == NULL) {37if ((ret = krb5_get_default_realm(handle->context, &realm)))38return ret;39} else {40realm = r;41}4243krb5_free_principal(handle->context, master_princ);44master_princ = NULL;45if ((ret = krb5_db_setup_mkey_name(handle->context,46handle->params.mkey_name,47realm, NULL, &master_princ)))48goto done;4950krb5_free_keyblock_contents(handle->context, &master_keyblock);51master_keyblock.enctype = handle->params.enctype;5253/*54* Fetch the local mkey, may not be the latest but that's okay because we55* really want the list of all mkeys and those can be retrieved with any56* valid mkey.57*/58ret = krb5_db_fetch_mkey(handle->context, master_princ,59master_keyblock.enctype, from_kbd,60FALSE /* only prompt once */,61handle->params.stash_file,62&mkvno /* get the kvno of the returned mkey */,63NULL /* I'm not sure about this,64but it's what the kdc does --marc */,65&master_keyblock);66if (ret)67goto done;6869ret = krb5_db_fetch_mkey_list(handle->context, master_princ,70&master_keyblock);71if (ret)72krb5_db_fini(handle->context);7374done:75if (r == NULL)76free(realm);7778return(ret);79}8081/* Fetch the currently active master key version number and keyblock. */82krb5_error_code83kdb_get_active_mkey(kadm5_server_handle_t handle, krb5_kvno *act_kvno_out,84krb5_keyblock **act_mkey_out)85{86krb5_error_code ret;87krb5_actkvno_node *active_mkey_list;8889ret = krb5_dbe_fetch_act_key_list(handle->context, master_princ,90&active_mkey_list);91if (ret)92return ret;93ret = krb5_dbe_find_act_mkey(handle->context, active_mkey_list,94act_kvno_out, act_mkey_out);95krb5_dbe_free_actkvno_list(handle->context, active_mkey_list);96return ret;97}9899/*100* Function: kdb_init_hist101*102* Purpose: Initializes the hist_princ variable.103*104* Arguments:105*106* handle (r) kadm5 api server handle107* r (r) realm of history principal to use, or NULL108*109* Effects: This function sets the value of the hist_princ global variable.110*/111krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r)112{113int ret = 0;114char *realm, *hist_name;115116if (r == NULL) {117if ((ret = krb5_get_default_realm(handle->context, &realm)))118return ret;119} else {120realm = r;121}122123if (asprintf(&hist_name, "%s@%s", KADM5_HIST_PRINCIPAL, realm) < 0) {124hist_name = NULL;125goto done;126}127128krb5_free_principal(handle->context, hist_princ);129hist_princ = NULL;130if ((ret = krb5_parse_name(handle->context, hist_name, &hist_princ)))131goto done;132133done:134free(hist_name);135if (r == NULL)136free(realm);137return ret;138}139140static krb5_error_code141create_hist(kadm5_server_handle_t handle)142{143kadm5_ret_t ret;144krb5_key_salt_tuple ks[1];145kadm5_principal_ent_rec ent;146long mask = KADM5_PRINCIPAL | KADM5_MAX_LIFE | KADM5_ATTRIBUTES;147148/* Create the history principal. */149memset(&ent, 0, sizeof(ent));150ent.principal = hist_princ;151ent.max_life = KRB5_KDB_DISALLOW_ALL_TIX;152ent.attributes = 0;153ks[0].ks_enctype = handle->params.enctype;154ks[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;155ret = kadm5_create_principal_3(handle, &ent, mask, 1, ks, NULL);156if (ret)157return ret;158159/* For better compatibility with pre-1.8 libkadm5 code, we want the160* initial history kvno to be 2, so re-randomize it. */161return kadm5_randkey_principal_3(handle, ent.principal, 0, 1, ks,162NULL, NULL);163}164165/*166* Fetch the current history key(s), creating the history principal if167* necessary. Database created since krb5 1.3 will have only one key, but168* databases created before that may have multiple keys (of the same kvno)169* and we need to try them all. History keys will be returned in a list170* terminated by an entry with enctype 0.171*/172krb5_error_code173kdb_get_hist_key(kadm5_server_handle_t handle, krb5_keyblock **keyblocks_out,174krb5_kvno *kvno_out)175{176krb5_error_code ret;177krb5_db_entry *kdb;178krb5_keyblock *mkey, *kblist = NULL;179krb5_int16 i;180181/* Fetch the history principal, creating it if necessary. */182ret = kdb_get_entry(handle, hist_princ, &kdb, NULL);183if (ret == KADM5_UNK_PRINC) {184ret = create_hist(handle);185if (ret)186return ret;187ret = kdb_get_entry(handle, hist_princ, &kdb, NULL);188}189if (ret)190return ret;191192if (kdb->n_key_data <= 0) {193ret = KRB5_KDB_NO_MATCHING_KEY;194k5_setmsg(handle->context, ret,195_("History entry contains no key data"));196goto done;197}198199ret = krb5_dbe_find_mkey(handle->context, kdb, &mkey);200if (ret)201goto done;202203kblist = k5calloc(kdb->n_key_data + 1, sizeof(*kblist), &ret);204if (kblist == NULL)205goto done;206for (i = 0; i < kdb->n_key_data; i++) {207ret = krb5_dbe_decrypt_key_data(handle->context, mkey,208&kdb->key_data[i], &kblist[i],209NULL);210if (ret)211goto done;212}213214*keyblocks_out = kblist;215kblist = NULL;216*kvno_out = kdb->key_data[0].key_data_kvno;217218done:219kdb_free_entry(handle, kdb, NULL);220kdb_free_keyblocks(handle, kblist);221return ret;222}223224/* Free all keyblocks in a list (terminated by a keyblock with enctype 0). */225void226kdb_free_keyblocks(kadm5_server_handle_t handle, krb5_keyblock *keyblocks)227{228krb5_keyblock *kb;229230if (keyblocks == NULL)231return;232for (kb = keyblocks; kb->enctype != 0; kb++)233krb5_free_keyblock_contents(handle->context, kb);234free(keyblocks);235}236237/*238* Function: kdb_get_entry239*240* Purpose: Gets an entry from the kerberos database and breaks241* it out into a krb5_db_entry and an osa_princ_ent_t.242*243* Arguments:244*245* handle (r) the server_handle246* principal (r) the principal to get247* kdb (w) krb5_db_entry to create248* adb (w) osa_princ_ent_rec to fill in249*250* when the caller is done with kdb and adb, kdb_free_entry must be251* called to release them. The adb record is filled in with the252* contents of the KRB5_TL_KADM_DATA record; if that record doesn't253* exist, an empty but valid adb record is returned.254*/255krb5_error_code256kdb_get_entry(kadm5_server_handle_t handle,257krb5_principal principal, krb5_db_entry **kdb_ptr,258osa_princ_ent_rec *adb)259{260krb5_error_code ret;261krb5_tl_data tl_data;262XDR xdrs;263krb5_db_entry *kdb;264265*kdb_ptr = NULL;266267ret = krb5_db_get_principal(handle->context, principal, 0, &kdb);268if (ret == KRB5_KDB_NOENTRY)269return(KADM5_UNK_PRINC);270if (ret)271return(ret);272273if (adb) {274memset(adb, 0, sizeof(*adb));275276tl_data.tl_data_type = KRB5_TL_KADM_DATA;277/*278* XXX Currently, lookup_tl_data always returns zero; it sets279* tl_data->tl_data_length to zero if the type isn't found.280* This should be fixed...281*/282if ((ret = krb5_dbe_lookup_tl_data(handle->context, kdb, &tl_data))283|| (tl_data.tl_data_length == 0)) {284/* there's no admin data. this can happen, if the admin285server is put into production after some principals286are created. In this case, return valid admin287data (which is all zeros with the hist_kvno filled288in), and when the entry is written, the admin289data will get stored correctly. */290291adb->admin_history_kvno = INITIAL_HIST_KVNO;292*kdb_ptr = kdb;293return(ret);294}295296xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents,297tl_data.tl_data_length, XDR_DECODE);298if (! xdr_osa_princ_ent_rec(&xdrs, adb)) {299xdr_destroy(&xdrs);300krb5_db_free_principal(handle->context, kdb);301return(KADM5_XDR_FAILURE);302}303xdr_destroy(&xdrs);304}305306*kdb_ptr = kdb;307return(0);308}309310/*311* Function: kdb_free_entry312*313* Purpose: frees the resources allocated by kdb_get_entry314*315* Arguments:316*317* handle (r) the server_handle318* kdb (w) krb5_db_entry to fill in319* adb (w) osa_princ_ent_rec to fill in320*321* when the caller is done with kdb and adb, kdb_free_entry must be322* called to release them.323*/324325krb5_error_code326kdb_free_entry(kadm5_server_handle_t handle,327krb5_db_entry *kdb, osa_princ_ent_rec *adb)328{329XDR xdrs;330331332if (kdb)333krb5_db_free_principal(handle->context, kdb);334335if (adb) {336xdrmem_create(&xdrs, NULL, 0, XDR_FREE);337xdr_osa_princ_ent_rec(&xdrs, adb);338xdr_destroy(&xdrs);339}340341return(0);342}343344/*345* Function: kdb_put_entry346*347* Purpose: Stores the osa_princ_ent_t and krb5_db_entry into to348* database.349*350* Arguments:351*352* handle (r) the server_handle353* kdb (r/w) the krb5_db_entry to store354* adb (r) the osa_princ_db_ent to store355*356* Effects:357*358* The last modifier field of the kdb is set to the caller at now.359* adb is encoded with xdr_osa_princ_ent_ret and stored in kbd as360* KRB5_TL_KADM_DATA. kdb is then written to the database.361*/362krb5_error_code363kdb_put_entry(kadm5_server_handle_t handle,364krb5_db_entry *kdb, osa_princ_ent_rec *adb)365{366krb5_error_code ret;367krb5_timestamp now;368XDR xdrs;369krb5_tl_data tl_data;370371ret = krb5_timeofday(handle->context, &now);372if (ret)373return(ret);374375ret = krb5_dbe_update_mod_princ_data(handle->context, kdb, now,376handle->current_caller);377if (ret)378return(ret);379380xdralloc_create(&xdrs, XDR_ENCODE);381if(! xdr_osa_princ_ent_rec(&xdrs, adb)) {382xdr_destroy(&xdrs);383return(KADM5_XDR_FAILURE);384}385tl_data.tl_data_type = KRB5_TL_KADM_DATA;386tl_data.tl_data_length = xdr_getpos(&xdrs);387tl_data.tl_data_contents = (krb5_octet *)xdralloc_getdata(&xdrs);388389ret = krb5_dbe_update_tl_data(handle->context, kdb, &tl_data);390391xdr_destroy(&xdrs);392393if (ret)394return(ret);395396/* we are always updating TL data */397kdb->mask |= KADM5_TL_DATA;398399ret = krb5_db_put_principal(handle->context, kdb);400if (ret)401return(ret);402403return(0);404}405406krb5_error_code407kdb_delete_entry(kadm5_server_handle_t handle, krb5_principal name)408{409krb5_error_code ret;410411ret = krb5_db_delete_principal(handle->context, name);412return (ret == KRB5_KDB_NOENTRY) ? KADM5_UNK_PRINC : ret;413}414415typedef struct _iter_data {416void (*func)(void *, krb5_principal);417void *data;418} iter_data;419420static krb5_error_code421kdb_iter_func(krb5_pointer data, krb5_db_entry *kdb)422{423iter_data *id = (iter_data *) data;424425(*(id->func))(id->data, kdb->princ);426427return(0);428}429430krb5_error_code431kdb_iter_entry(kadm5_server_handle_t handle, char *match_entry,432void (*iter_fct)(void *, krb5_principal), void *data)433{434iter_data id;435krb5_error_code ret;436437id.func = iter_fct;438id.data = data;439440ret = krb5_db_iterate(handle->context, match_entry, kdb_iter_func, &id, 0);441if (ret)442return(ret);443444return(0);445}446447448