Path: blob/main/crypto/krb5/src/lib/kdb/kdb_cpw.c
106845 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* lib/kdb/kdb_cpw.c */2/*3* Copyright 1995, 2009, 2014 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 (C) 1998 by the FundsXpress, INC.27*28* All rights reserved.29*30* Export of this software from the United States of America may require31* a specific license from the United States Government. It is the32* responsibility of any person or organization contemplating export to33* obtain such a license before exporting.34*35* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and36* distribute this software and its documentation for any purpose and37* without fee is hereby granted, provided that the above copyright38* notice appear in all copies and that both that copyright notice and39* this permission notice appear in supporting documentation, and that40* the name of FundsXpress. not be used in advertising or publicity pertaining41* to distribution of the software without specific, written prior42* permission. FundsXpress makes no representations about the suitability of43* this software for any purpose. It is provided "as is" without express44* or implied warranty.45*46* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR47* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED48* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.49*/5051#include "k5-int.h"52#include "kdb.h"53#include <stdio.h>54#include <errno.h>5556int57krb5_db_get_key_data_kvno(krb5_context context, int count, krb5_key_data *data)58{59int i, kvno;60/* Find last key version number */61for (kvno = i = 0; i < count; i++) {62if (kvno < data[i].key_data_kvno) {63kvno = data[i].key_data_kvno;64}65}66return(kvno);67}6869static void70cleanup_key_data(krb5_context context, int count, krb5_key_data *data)71{72int i;7374/* If data is NULL, count is always 0 */75if (data == NULL) return;7677for (i = 0; i < count; i++)78krb5_dbe_free_key_data_contents(context, &data[i]);79free(data);80}8182/* Transfer key data from old_kd to new_kd, making sure that new_kd is83* encrypted with mkey. May steal from old_kd and zero it out. */84static krb5_error_code85preserve_one_old_key(krb5_context context, krb5_keyblock *mkey,86krb5_db_entry *dbent, krb5_key_data *old_kd,87krb5_key_data *new_kd)88{89krb5_error_code ret;90krb5_keyblock kb;91krb5_keysalt salt;9293memset(new_kd, 0, sizeof(*new_kd));9495ret = krb5_dbe_decrypt_key_data(context, mkey, old_kd, &kb, NULL);96if (ret == 0) {97/* old_kd is already encrypted in mkey, so just move it. */98*new_kd = *old_kd;99memset(old_kd, 0, sizeof(*old_kd));100krb5_free_keyblock_contents(context, &kb);101return 0;102}103104/* Decrypt and re-encrypt old_kd using mkey. */105ret = krb5_dbe_decrypt_key_data(context, NULL, old_kd, &kb, &salt);106if (ret)107return ret;108ret = krb5_dbe_encrypt_key_data(context, mkey, &kb, &salt,109old_kd->key_data_kvno, new_kd);110krb5_free_keyblock_contents(context, &kb);111krb5_free_data_contents(context, &salt.data);112return ret;113}114115/*116* Add key_data to dbent, making sure that each entry is encrypted in mkey. If117* keepold is greater than 1, preserve only the first (keepold-1) key versions,118* so that the total number of key versions including the new key set is119* keepold. May steal some elements from key_data and zero them out.120*/121static krb5_error_code122preserve_old_keys(krb5_context context, krb5_keyblock *mkey,123krb5_db_entry *dbent, unsigned int keepold, int n_key_data,124krb5_key_data *key_data)125{126krb5_error_code ret;127krb5_kvno last_kvno = 0, kvno_changes = 0;128int i;129130for (i = 0; i < n_key_data; i++) {131if (keepold > 1) {132if (i > 0 && key_data[i].key_data_kvno != last_kvno)133kvno_changes++;134if (kvno_changes >= keepold - 1)135break;136last_kvno = key_data[i].key_data_kvno;137}138139ret = krb5_dbe_create_key_data(context, dbent);140if (ret)141return ret;142ret = preserve_one_old_key(context, mkey, dbent, &key_data[i],143&dbent->key_data[dbent->n_key_data - 1]);144if (ret)145return ret;146}147return 0;148}149150static krb5_error_code151add_key_rnd(krb5_context context, krb5_keyblock *master_key,152krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,153krb5_db_entry *db_entry, int kvno)154{155krb5_keyblock key;156int i, j;157krb5_error_code retval;158krb5_key_data *kd_slot;159160for (i = 0; i < ks_tuple_count; i++) {161krb5_boolean similar;162163similar = 0;164165/*166* We could use krb5_keysalt_iterate to replace this loop, or use167* krb5_keysalt_is_present for the loop below, but we want to avoid168* circular library dependencies.169*/170for (j = 0; j < i; j++) {171if ((retval = krb5_c_enctype_compare(context,172ks_tuple[i].ks_enctype,173ks_tuple[j].ks_enctype,174&similar)))175return(retval);176177if (similar)178break;179}180181if (similar)182continue;183184if ((retval = krb5_dbe_create_key_data(context, db_entry)))185return retval;186kd_slot = &db_entry->key_data[db_entry->n_key_data - 1];187188/* there used to be code here to extract the old key, and derive189a new key from it. Now that there's a unified prng, that isn't190necessary. */191192/* make new key */193if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,194&key)))195return retval;196197retval = krb5_dbe_encrypt_key_data(context, master_key, &key, NULL,198kvno, kd_slot);199200krb5_free_keyblock_contents(context, &key);201if( retval )202return retval;203}204205return 0;206}207208/* Construct a random explicit salt. */209static krb5_error_code210make_random_salt(krb5_context context, krb5_keysalt *salt_out)211{212krb5_error_code retval;213unsigned char rndbuf[8];214krb5_data salt, rnd = make_data(rndbuf, sizeof(rndbuf));215unsigned int i;216217/*218* Salts are limited by RFC 4120 to 7-bit ASCII. For ease of examination219* and to avoid certain folding issues for older enctypes, we use printable220* characters with four fixed bits and four random bits, encoding 64221* psuedo-random bits into 16 bytes.222*/223retval = krb5_c_random_make_octets(context, &rnd);224if (retval)225return retval;226retval = alloc_data(&salt, sizeof(rndbuf) * 2);227if (retval)228return retval;229for (i = 0; i < sizeof(rndbuf); i++) {230salt.data[i * 2] = 0x40 | (rndbuf[i] >> 4);231salt.data[i * 2 + 1] = 0x40 | (rndbuf[i] & 0xf);232}233234salt_out->type = KRB5_KDB_SALTTYPE_SPECIAL;235salt_out->data = salt;236return 0;237}238239/*240* Add key_data for a krb5_db_entry241* If passwd is NULL the assumes that the caller wants a random password.242*/243static krb5_error_code244add_key_pwd(krb5_context context, krb5_keyblock *master_key,245krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,246const char *passwd, krb5_db_entry *db_entry, int kvno)247{248krb5_error_code retval;249krb5_keysalt key_salt;250krb5_keyblock key;251krb5_data pwd;252int i, j;253krb5_key_data *kd_slot;254255for (i = 0; i < ks_tuple_count; i++) {256krb5_boolean similar;257258similar = 0;259260/*261* We could use krb5_keysalt_iterate to replace this loop, or use262* krb5_keysalt_is_present for the loop below, but we want to avoid263* circular library dependencies.264*/265for (j = 0; j < i; j++) {266if ((retval = krb5_c_enctype_compare(context,267ks_tuple[i].ks_enctype,268ks_tuple[j].ks_enctype,269&similar)))270return(retval);271272if (similar)273break;274}275276if (j < i)277continue;278279if ((retval = krb5_dbe_create_key_data(context, db_entry)))280return(retval);281kd_slot = &db_entry->key_data[db_entry->n_key_data - 1];282283/* Convert password string to key using appropriate salt */284switch (key_salt.type = ks_tuple[i].ks_salttype) {285case KRB5_KDB_SALTTYPE_ONLYREALM: {286krb5_data * saltdata;287if ((retval = krb5_copy_data(context, krb5_princ_realm(context,288db_entry->princ), &saltdata)))289return(retval);290291key_salt.data = *saltdata;292free(saltdata);293}294break;295case KRB5_KDB_SALTTYPE_NOREALM:296if ((retval=krb5_principal2salt_norealm(context, db_entry->princ,297&key_salt.data)))298return(retval);299break;300case KRB5_KDB_SALTTYPE_NORMAL:301if ((retval = krb5_principal2salt(context, db_entry->princ,302&key_salt.data)))303return(retval);304break;305case KRB5_KDB_SALTTYPE_SPECIAL:306retval = make_random_salt(context, &key_salt);307if (retval)308return retval;309break;310default:311return(KRB5_KDB_BAD_SALTTYPE);312}313314pwd = string2data((char *)passwd);315316retval = krb5_c_string_to_key_with_params(context,317ks_tuple[i].ks_enctype,318&pwd, &key_salt.data,319NULL, &key);320if (retval) {321free(key_salt.data.data);322return retval;323}324325retval = krb5_dbe_encrypt_key_data(context, master_key, &key,326(const krb5_keysalt *)&key_salt,327kvno, kd_slot);328if (key_salt.data.data)329free(key_salt.data.data);330free(key.contents);331332if( retval )333return retval;334}335336return 0;337}338339static krb5_error_code340rekey(krb5_context context, krb5_keyblock *mkey, krb5_key_salt_tuple *ks_tuple,341int ks_tuple_count, const char *password, int new_kvno,342unsigned int keepold, krb5_db_entry *db_entry)343{344krb5_error_code ret;345krb5_key_data *key_data;346int n_key_data, old_kvno;347348/* Save aside the old key data. */349n_key_data = db_entry->n_key_data;350key_data = db_entry->key_data;351db_entry->n_key_data = 0;352db_entry->key_data = NULL;353354/* Make sure the new kvno is greater than the old largest kvno. */355old_kvno = krb5_db_get_key_data_kvno(context, n_key_data, key_data);356if (new_kvno < old_kvno + 1)357new_kvno = old_kvno + 1;358/* Wrap from 65535 to 1; we can only store 16-bit kvno values in key_data,359* and we assign special meaning to kvno 0. */360if (new_kvno == (1 << 16))361new_kvno = 1;362363/* Add new keys to the front of the list. */364if (password != NULL) {365ret = add_key_pwd(context, mkey, ks_tuple, ks_tuple_count, password,366db_entry, new_kvno);367} else {368ret = add_key_rnd(context, mkey, ks_tuple, ks_tuple_count, db_entry,369new_kvno);370}371if (ret) {372cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);373db_entry->n_key_data = n_key_data;374db_entry->key_data = key_data;375return ret;376}377378/* Possibly add some or all of the old keys to the back of the list. May379* steal from and zero out some of the old key data entries. */380if (keepold > 0) {381ret = preserve_old_keys(context, mkey, db_entry, keepold, n_key_data,382key_data);383}384385/* Free any old key data entries not stolen and zeroed out above. */386cleanup_key_data(context, n_key_data, key_data);387return ret;388}389390/*391* Change random key for a krb5_db_entry392* Assumes the max kvno393*394* As a side effect all old keys are nuked if keepold is false.395*/396krb5_error_code397krb5_dbe_crk(krb5_context context, krb5_keyblock *mkey,398krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,399unsigned int keepold, krb5_db_entry *dbent)400{401return rekey(context, mkey, ks_tuple, ks_tuple_count, NULL, 0, keepold,402dbent);403}404405/*406* Add random key for a krb5_db_entry407* Assumes the max kvno408*409* As a side effect all old keys older than the max kvno are nuked.410*/411krb5_error_code412krb5_dbe_ark(krb5_context context, krb5_keyblock *mkey,413krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,414krb5_db_entry *dbent)415{416return rekey(context, mkey, ks_tuple, ks_tuple_count, NULL, 0, 2, dbent);417}418419/*420* Change password for a krb5_db_entry421* Assumes the max kvno422*423* As a side effect all old keys are nuked if keepold is false.424*/425krb5_error_code426krb5_dbe_def_cpw(krb5_context context, krb5_keyblock *mkey,427krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,428char *password, int new_kvno, unsigned int keepold,429krb5_db_entry *dbent)430{431return rekey(context, mkey, ks_tuple, ks_tuple_count, password, new_kvno,432keepold, dbent);433}434435/*436* Add password for a krb5_db_entry437* Assumes the max kvno438*439* As a side effect all old keys older than the max kvno are nuked.440*/441krb5_error_code442krb5_dbe_apw(krb5_context context, krb5_keyblock *mkey,443krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, char *password,444krb5_db_entry *dbent)445{446return rekey(context, mkey, ks_tuple, ks_tuple_count, password, 0, 2,447dbent);448}449450451