Path: blob/main/crypto/krb5/src/lib/kdb/kdb_default.c
39566 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* lib/kdb/kdb_default.c */2/*3* Copyright 1995, 2009 by the Massachusetts Institute of Technology.4* All Rights Reserved.5*6* Export of this software from the United States of America may7* require a specific license from the United States Government.8* It is the responsibility of any person or organization contemplating9* export to obtain such a license before exporting.10*11* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and12* distribute this software and its documentation for any purpose and13* without fee is hereby granted, provided that the above copyright14* notice appear in all copies and that both that copyright notice and15* this permission notice appear in supporting documentation, and that16* the name of M.I.T. not be used in advertising or publicity pertaining17* to distribution of the software without specific, written prior18* permission. Furthermore if you modify this software you must label19* your software as modified software and not distribute it in such a20* fashion that it might be confused with the original M.I.T. software.21* M.I.T. makes no representations about the suitability of22* this software for any purpose. It is provided "as is" without express23* or implied warranty.24*/25/*26* Copyright 2009 Sun Microsystems, Inc. All rights reserved.27* Use is subject to license terms.28*/2930#include "k5-int.h"31#include "kdb.h"32#include <string.h>33#include <stdio.h>34#include <errno.h>35#include <arpa/inet.h>363738/*39* Set *kd_out to the key data entry matching kvno, enctype, and salttype. If40* any of those three parameters are -1, ignore them. If kvno is 0, match only41* the highest kvno. Begin searching at the index *start and set *start to the42* index after the match. Do not return keys of non-permitted enctypes; return43* KRB5_KDB_NO_PERMITTED_KEY if the whole list was searched and only44* non-permitted matches were found.45*/46krb5_error_code47krb5_dbe_def_search_enctype(krb5_context context, krb5_db_entry *ent,48krb5_int32 *start, krb5_int32 enctype,49krb5_int32 salttype, krb5_int32 kvno,50krb5_key_data **kd_out)51{52krb5_key_data *kd;53krb5_int32 db_salttype;54krb5_boolean saw_non_permitted = FALSE;55int i;5657*kd_out = NULL;5859if (enctype != -1 && !krb5_is_permitted_enctype(context, enctype))60return KRB5_KDB_NO_PERMITTED_KEY;61if (ent->n_key_data == 0)62return KRB5_KDB_NO_MATCHING_KEY;6364/* Match the highest kvno if kvno is 0. Key data is sorted in descending65* order of kvno. */66if (kvno == 0)67kvno = ent->key_data[0].key_data_kvno;6869for (i = *start; i < ent->n_key_data; i++) {70kd = &ent->key_data[i];71db_salttype = (kd->key_data_ver > 1) ? kd->key_data_type[1] :72KRB5_KDB_SALTTYPE_NORMAL;7374/* Match this entry against the arguments. Stop searching if we have75* passed the entries for the requested kvno. */76if (enctype != -1 && kd->key_data_type[0] != enctype)77continue;78if (salttype >= 0 && db_salttype != salttype)79continue;80if (kvno >= 0 && kd->key_data_kvno < kvno)81break;82if (kvno >= 0 && kd->key_data_kvno != kvno)83continue;8485/* Filter out non-permitted enctypes. */86if (!krb5_is_permitted_enctype(context, kd->key_data_type[0])) {87saw_non_permitted = TRUE;88continue;89}9091*start = i + 1;92*kd_out = kd;93return 0;94}9596/* If we scanned the whole set of keys and matched only non-permitted97* enctypes, indicate that. */98return (*start == 0 && saw_non_permitted) ? KRB5_KDB_NO_PERMITTED_KEY :99KRB5_KDB_NO_MATCHING_KEY;100}101102/*103* kdb default functions. Ideally, some other file should have this functions. For now, TBD.104*/105#ifndef min106#define min(a,b) (((a) < (b)) ? (a) : (b))107#endif108109krb5_error_code110krb5_def_store_mkey_list(krb5_context context,111char *keyfile,112krb5_principal mname,113krb5_keylist_node *keylist,114char *master_pwd)115{116krb5_error_code retval = 0;117char defkeyfile[MAXPATHLEN+1];118char *tmp_ktname = NULL, *tmp_ktpath;119krb5_data *realm = krb5_princ_realm(context, mname);120krb5_keytab kt = NULL;121krb5_keytab_entry new_entry;122struct stat stb;123int statrc;124125if (!keyfile) {126(void) snprintf(defkeyfile, sizeof(defkeyfile), "%s%s",127DEFAULT_KEYFILE_STUB, realm->data);128keyfile = defkeyfile;129}130131if ((statrc = stat(keyfile, &stb)) >= 0) {132/* if keyfile exists it better be a regular file */133if (!S_ISREG(stb.st_mode)) {134retval = EINVAL;135k5_setmsg(context, retval,136_("keyfile (%s) is not a regular file: %s"),137keyfile, error_message(retval));138goto out;139}140}141142/*143* We assume the stash file is in a directory writable only by root.144* As such, don't worry about collisions, just do an atomic rename.145*/146retval = asprintf(&tmp_ktname, "FILE:%s_tmp", keyfile);147if (retval < 0) {148k5_setmsg(context, retval,149_("Could not create temp keytab file name."));150goto out;151}152153/*154* Set tmp_ktpath to point to the keyfile path (skip FILE:). Subtracting155* 1 to account for NULL terminator in sizeof calculation of a string156* constant. Used further down.157*/158tmp_ktpath = tmp_ktname + (sizeof("FILE:") - 1);159160/*161* This time-of-check-to-time-of-access race is fine; we care only162* about an administrator running the command twice, not an attacker163* trying to beat us to creating the file. Per the above comment, we164* assume the stash file is in a directory writable only by root.165*/166statrc = stat(tmp_ktpath, &stb);167if (statrc == -1 && errno != ENOENT) {168/* ENOENT is the expected case */169retval = errno;170goto out;171} else if (statrc == 0) {172retval = EEXIST;173k5_setmsg(context, retval,174_("Temporary stash file already exists: %s."), tmp_ktpath);175goto out;176}177178/* create new stash keytab using temp file name */179retval = krb5_kt_resolve(context, tmp_ktname, &kt);180if (retval != 0)181goto out;182183while (keylist && !retval) {184memset(&new_entry, 0, sizeof(new_entry));185new_entry.principal = mname;186new_entry.key = keylist->keyblock;187new_entry.vno = keylist->kvno;188189retval = krb5_kt_add_entry(context, kt, &new_entry);190keylist = keylist->next;191}192krb5_kt_close(context, kt);193194if (retval != 0) {195/* Clean up by deleting the tmp keyfile if it exists. */196(void)unlink(tmp_ktpath);197} else {198/* Atomically rename temp keyfile to original filename. */199if (rename(tmp_ktpath, keyfile) < 0) {200retval = errno;201k5_setmsg(context, retval,202_("rename of temporary keyfile (%s) to (%s) failed: %s"),203tmp_ktpath, keyfile, error_message(errno));204}205}206207out:208if (tmp_ktname != NULL)209free(tmp_ktname);210211return retval;212}213214static krb5_error_code215krb5_db_def_fetch_mkey_stash(krb5_context context,216const char *keyfile,217krb5_keyblock *key,218krb5_kvno *kvno)219{220krb5_error_code retval = 0;221krb5_ui_2 enctype;222krb5_ui_4 keylength;223FILE *kf = NULL;224225if (!(kf = fopen(keyfile, "rb")))226return KRB5_KDB_CANTREAD_STORED;227set_cloexec_file(kf);228229if (fread((krb5_pointer) &enctype, 2, 1, kf) != 1) {230retval = KRB5_KDB_CANTREAD_STORED;231goto errout;232}233234#if BIG_ENDIAN_MASTER_KEY235enctype = ntohs((uint16_t) enctype);236#endif237238if (key->enctype == ENCTYPE_UNKNOWN)239key->enctype = enctype;240else if (enctype != key->enctype) {241retval = KRB5_KDB_BADSTORED_MKEY;242goto errout;243}244245if (fread((krb5_pointer) &keylength,246sizeof(keylength), 1, kf) != 1) {247retval = KRB5_KDB_CANTREAD_STORED;248goto errout;249}250251#if BIG_ENDIAN_MASTER_KEY252key->length = ntohl((uint32_t) keylength);253#else254key->length = keylength;255#endif256257if (!key->length || key->length > 1024) {258retval = KRB5_KDB_BADSTORED_MKEY;259goto errout;260}261262if (!(key->contents = (krb5_octet *)malloc(key->length))) {263retval = ENOMEM;264goto errout;265}266267if (fread((krb5_pointer) key->contents, sizeof(key->contents[0]),268key->length, kf) != key->length) {269retval = KRB5_KDB_CANTREAD_STORED;270zap(key->contents, key->length);271free(key->contents);272key->contents = 0;273} else274retval = 0;275276/*277* Note, the old stash format did not store the kvno and at this point it278* can be assumed to be 1 as is the case for the mkey princ. If the kvno is279* passed in and isn't ignore_vno just leave it alone as this could cause280* verifcation trouble if the mkey princ is using a kvno other than 1.281*/282if (kvno && *kvno == IGNORE_VNO)283*kvno = 1;284285errout:286(void) fclose(kf);287return retval;288}289290static krb5_error_code291krb5_db_def_fetch_mkey_keytab(krb5_context context,292const char *keyfile,293krb5_principal mname,294krb5_keyblock *key,295krb5_kvno *kvno)296{297krb5_error_code retval = 0;298krb5_keytab kt = NULL;299krb5_keytab_entry kt_ent;300krb5_enctype enctype = IGNORE_ENCTYPE;301302if ((retval = krb5_kt_resolve(context, keyfile, &kt)) != 0)303goto errout;304305/* override default */306if (key->enctype != ENCTYPE_UNKNOWN)307enctype = key->enctype;308309if ((retval = krb5_kt_get_entry(context, kt, mname,310kvno ? *kvno : IGNORE_VNO,311enctype,312&kt_ent)) == 0) {313314if (key->enctype == ENCTYPE_UNKNOWN)315key->enctype = kt_ent.key.enctype;316317if (((int) kt_ent.key.length) < 0) {318retval = KRB5_KDB_BADSTORED_MKEY;319krb5_kt_free_entry(context, &kt_ent);320goto errout;321}322323key->length = kt_ent.key.length;324325/*326* If a kvno pointer was passed in and it dereferences the327* IGNORE_VNO value then it should be assigned the value of the kvno328* found in the keytab otherwise the KNVO specified should be the329* same as the one returned from the keytab.330*/331if (kvno != NULL && *kvno == IGNORE_VNO)332*kvno = kt_ent.vno;333334/*335* kt_ent will be free'd so need to allocate and copy key contents for336* output to caller.337*/338key->contents = k5memdup(kt_ent.key.contents, kt_ent.key.length,339&retval);340if (key->contents == NULL) {341krb5_kt_free_entry(context, &kt_ent);342goto errout;343}344krb5_kt_free_entry(context, &kt_ent);345}346347errout:348if (kt)349krb5_kt_close(context, kt);350351return retval;352}353354krb5_error_code355krb5_db_def_fetch_mkey(krb5_context context,356krb5_principal mname,357krb5_keyblock *key,358krb5_kvno *kvno,359char *db_args)360{361krb5_error_code retval;362char keyfile[MAXPATHLEN+1];363krb5_data *realm = krb5_princ_realm(context, mname);364365key->magic = KV5M_KEYBLOCK;366367if (db_args != NULL) {368(void) strncpy(keyfile, db_args, sizeof(keyfile));369} else {370(void) snprintf(keyfile, sizeof(keyfile), "%s%s",371DEFAULT_KEYFILE_STUB, realm->data);372}373/* null terminate no matter what */374keyfile[sizeof(keyfile) - 1] = '\0';375376/* Try the keytab and old stash file formats. */377retval = krb5_db_def_fetch_mkey_keytab(context, keyfile, mname, key, kvno);378if (retval == KRB5_KEYTAB_BADVNO)379retval = krb5_db_def_fetch_mkey_stash(context, keyfile, key, kvno);380381/*382* Use a generic error code for failure to retrieve the master383* key, but set a message indicating the actual error.384*/385if (retval != 0) {386k5_setmsg(context, KRB5_KDB_CANTREAD_STORED,387_("Can not fetch master key (error: %s)."),388error_message(retval));389return KRB5_KDB_CANTREAD_STORED;390} else391return 0;392}393394krb5_error_code395krb5_def_fetch_mkey_list(krb5_context context,396krb5_principal mprinc,397const krb5_keyblock *mkey,398krb5_keylist_node **mkeys_list)399{400krb5_error_code retval;401krb5_db_entry *master_entry;402krb5_boolean found_key = FALSE;403krb5_keyblock cur_mkey;404krb5_keylist_node *mkey_list_head = NULL, **mkey_list_node;405krb5_key_data *key_data;406krb5_mkey_aux_node *mkey_aux_data_list = NULL, *aux_data_entry;407int i;408409if (mkeys_list == NULL)410return (EINVAL);411412memset(&cur_mkey, 0, sizeof(cur_mkey));413414retval = krb5_db_get_principal(context, mprinc, 0, &master_entry);415if (retval == KRB5_KDB_NOENTRY)416return (KRB5_KDB_NOMASTERKEY);417if (retval)418return (retval);419420if (master_entry->n_key_data == 0) {421retval = KRB5_KDB_NOMASTERKEY;422goto clean_n_exit;423}424425/*426* Check if the input mkey is the latest key and if it isn't then find the427* latest mkey.428*/429430if (mkey->enctype == master_entry->key_data[0].key_data_type[0]) {431if (krb5_dbe_decrypt_key_data(context, mkey,432&master_entry->key_data[0],433&cur_mkey, NULL) == 0) {434found_key = TRUE;435}436}437438if (!found_key) {439if ((retval = krb5_dbe_lookup_mkey_aux(context, master_entry,440&mkey_aux_data_list)))441goto clean_n_exit;442443for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL;444aux_data_entry = aux_data_entry->next) {445446if (krb5_dbe_decrypt_key_data(context, mkey,447&aux_data_entry->latest_mkey,448&cur_mkey, NULL) == 0) {449found_key = TRUE;450break;451}452}453if (found_key != TRUE) {454k5_setmsg(context, KRB5_KDB_BADMASTERKEY,455_("Unable to decrypt latest master key with the "456"provided master key\n"));457retval = KRB5_KDB_BADMASTERKEY;458goto clean_n_exit;459}460}461462/*463* Extract all the mkeys from master_entry using the most current mkey and464* create a mkey list for the mkeys field in kdc_realm_t.465*/466467mkey_list_head = (krb5_keylist_node *) malloc(sizeof(krb5_keylist_node));468if (mkey_list_head == NULL) {469retval = ENOMEM;470goto clean_n_exit;471}472473memset(mkey_list_head, 0, sizeof(krb5_keylist_node));474475/* Set mkey_list_head to the current mkey as an optimization. */476/* mkvno may not be latest so ... */477mkey_list_head->kvno = master_entry->key_data[0].key_data_kvno;478/* this is the latest clear mkey (avoids a redundant decrypt) */479mkey_list_head->keyblock = cur_mkey;480481/* loop through any other master keys creating a list of krb5_keylist_nodes */482mkey_list_node = &mkey_list_head->next;483for (i = 1; i < master_entry->n_key_data; i++) {484if (*mkey_list_node == NULL) {485/* *mkey_list_node points to next field of previous node */486*mkey_list_node = (krb5_keylist_node *) malloc(sizeof(krb5_keylist_node));487if (*mkey_list_node == NULL) {488retval = ENOMEM;489goto clean_n_exit;490}491memset(*mkey_list_node, 0, sizeof(krb5_keylist_node));492}493key_data = &master_entry->key_data[i];494retval = krb5_dbe_decrypt_key_data(context, &cur_mkey, key_data,495&((*mkey_list_node)->keyblock),496NULL);497if (retval)498goto clean_n_exit;499500(*mkey_list_node)->kvno = key_data->key_data_kvno;501mkey_list_node = &((*mkey_list_node)->next);502}503504*mkeys_list = mkey_list_head;505506clean_n_exit:507krb5_db_free_principal(context, master_entry);508krb5_dbe_free_mkey_aux_list(context, mkey_aux_data_list);509if (retval != 0)510krb5_dbe_free_key_list(context, mkey_list_head);511return retval;512}513514krb5_error_code515krb5_db_def_rename_principal(krb5_context kcontext,516krb5_const_principal source,517krb5_const_principal target)518{519krb5_db_entry *kdb = NULL;520krb5_principal oldprinc;521krb5_error_code ret;522523if (source == NULL || target == NULL)524return EINVAL;525526ret = krb5_db_get_principal(kcontext, source, 0, &kdb);527if (ret)528goto cleanup;529530/* Store salt values explicitly so that they don't depend on the principal531* name. */532ret = krb5_dbe_specialize_salt(kcontext, kdb);533if (ret)534goto cleanup;535536/* Temporarily alias kdb->princ to target and put the principal entry. */537oldprinc = kdb->princ;538kdb->princ = (krb5_principal)target;539ret = krb5_db_put_principal(kcontext, kdb);540kdb->princ = oldprinc;541if (ret)542goto cleanup;543544ret = krb5_db_delete_principal(kcontext, (krb5_principal)source);545546547cleanup:548krb5_db_free_principal(kcontext, kdb);549return ret;550}551552553