1/* $NetBSD: set_keys.c,v 1.4 2023/06/19 21:41:44 christos Exp $ */ 2 3/* 4 * Copyright (c) 1997 - 2001, 2003 Kungliga Tekniska H��gskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "kadm5_locl.h" 37 38__RCSID("$NetBSD: set_keys.c,v 1.4 2023/06/19 21:41:44 christos Exp $"); 39 40/* 41 * Set the keys of `ent' to the string-to-key of `password' 42 */ 43 44kadm5_ret_t 45_kadm5_set_keys(kadm5_server_context *context, 46 hdb_entry *ent, 47 int n_ks_tuple, 48 krb5_key_salt_tuple *ks_tuple, 49 const char *password) 50{ 51 Key *keys; 52 size_t num_keys; 53 kadm5_ret_t ret; 54 55 ret = hdb_generate_key_set_password_with_ks_tuple(context->context, 56 ent->principal, 57 password, 58 ks_tuple, n_ks_tuple, 59 &keys, &num_keys); 60 if (ret) 61 return ret; 62 63 _kadm5_free_keys (context->context, ent->keys.len, ent->keys.val); 64 ent->keys.val = keys; 65 ent->keys.len = num_keys; 66 67 hdb_entry_set_pw_change_time(context->context, ent, 0); 68 69 if (krb5_config_get_bool_default(context->context, NULL, FALSE, 70 "kadmin", "save-password", NULL)) 71 { 72 ret = hdb_entry_set_password(context->context, context->db, 73 ent, password); 74 if (ret) 75 return ret; 76 } 77 78 return 0; 79} 80 81static void 82setup_Key(Key *k, Salt *s, krb5_key_data *kd, size_t kd_offset) 83{ 84 memset(k, 0, sizeof (*k)); /* sets mkvno and salt */ 85 k->key.keytype = kd[kd_offset].key_data_type[0]; 86 k->key.keyvalue.length = kd[kd_offset].key_data_length[0]; 87 k->key.keyvalue.data = kd[kd_offset].key_data_contents[0]; 88 89 if(kd[kd_offset].key_data_ver == 2) { 90 memset(s, 0, sizeof (*s)); 91 s->type = kd[kd_offset].key_data_type[1]; 92 s->salt.length = kd[kd_offset].key_data_length[1]; 93 s->salt.data = kd[kd_offset].key_data_contents[1]; 94 k->salt = s; 95 } 96} 97 98/* 99 * Set the keys of `ent' to (`n_key_data', `key_data') 100 */ 101 102kadm5_ret_t 103_kadm5_set_keys2(kadm5_server_context *context, 104 hdb_entry *ent, 105 int16_t n_key_data, 106 krb5_key_data *key_data) 107{ 108 krb5_error_code ret; 109 size_t i, k; 110 HDB_extension ext; 111 HDB_extension *extp = NULL; 112 HDB_Ext_KeySet *hist_keys = &ext.data.u.hist_keys; 113 Key key; 114 Salt salt; 115 Keys keys; 116 hdb_keyset hkset; 117 krb5_kvno kvno = -1; 118 int one_key_set = 1; 119 int replace_hist_keys = 0; 120 121 if (n_key_data == 0) { 122 /* Clear all keys! */ 123 ret = hdb_clear_extension(context->context, ent, 124 choice_HDB_extension_data_hist_keys); 125 if (ret) 126 return ret; 127 free_Keys(&ent->keys); 128 return 0; 129 } 130 131 memset(&keys, 0, sizeof (keys)); 132 memset(&hkset, 0, sizeof (hkset)); /* set set_time */ 133 memset(&ext, 0, sizeof (ext)); 134 ext.mandatory = FALSE; 135 ext.data.element = choice_HDB_extension_data_hist_keys; 136 memset(hist_keys, 0, sizeof (*hist_keys)); 137 138 for (i = 0; i < n_key_data; i++) { 139 if (kvno != -1 && kvno != key_data[i].key_data_kvno) { 140 one_key_set = 0; 141 break; 142 } 143 kvno = key_data[i].key_data_kvno; 144 } 145 if (one_key_set) { 146 /* 147 * If we're updating KADM5_KEY_DATA with a single keyset then we 148 * assume we must be setting the principal's kvno as well! 149 * 150 * Just have to be careful about old clients that might have 151 * sent 0 as the kvno... This may seem ugly, but it's the price 152 * of backwards compatibility with pre-multi-kvno kadmin clients 153 * (besides, who's to say that updating KADM5_KEY_DATA requires 154 * updating the entry's kvno?) 155 * 156 * Note that we do nothing special for the case where multiple 157 * keysets are given but the entry's kvno is not set and not in 158 * the given set of keysets. If this happens we'll just update 159 * the key history only and leave the current keyset alone. 160 */ 161 if (kvno == 0) { 162 /* Force kvno to 1 if it was 0; (ank would do this anyways) */ 163 if (ent->kvno == 0) 164 ent->kvno = 1; 165 /* Below we need key_data[*].kvno to be reasonable */ 166 for (i = 0; i < n_key_data; i++) 167 key_data[i].key_data_kvno = ent->kvno; 168 } else { 169 /* 170 * Or force the entry's kvno to match the one from the new, 171 * singular keyset 172 */ 173 ent->kvno = kvno; 174 } 175 } 176 177 for (i = 0; i < n_key_data; i++) { 178 if (key_data[i].key_data_kvno == ent->kvno) { 179 /* A current key; add to current key set */ 180 setup_Key(&key, &salt, key_data, i); 181 ret = add_Keys(&keys, &key); 182 if (ret) 183 goto out; 184 continue; 185 } 186 187 /* 188 * This kvno is historical. Build an hdb_keyset for keys of 189 * this enctype and add them to the new key history. 190 */ 191 for (k = 0; k < hist_keys->len; k++) { 192 if (hist_keys->val[k].kvno == key_data[i].key_data_kvno) 193 break; 194 } 195 if (hist_keys->len > k && 196 hist_keys->val[k].kvno == key_data[i].key_data_kvno) 197 /* We've added all keys of this kvno already (see below) */ 198 continue; 199 200 memset(&hkset, 0, sizeof (hkset)); /* set set_time */ 201 hkset.kvno = key_data[i].key_data_kvno; 202 for (k = 0; k < n_key_data; k++) { 203 /* Find all keys of this kvno and add them to the new keyset */ 204 if (key_data[k].key_data_kvno != hkset.kvno) 205 continue; 206 207 setup_Key(&key, &salt, key_data, k); 208 ret = add_Keys(&hkset.keys, &key); 209 if (ret) { 210 free_hdb_keyset(&hkset); 211 goto out; 212 } 213 } 214 ret = add_HDB_Ext_KeySet(hist_keys, &hkset); 215 free_hdb_keyset(&hkset); 216 if (ret) 217 goto out; 218 replace_hist_keys = 1; 219 } 220 221 if (replace_hist_keys) 222 /* No key history given -> leave it alone */ 223 extp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys); 224 if (extp != NULL) { 225 HDB_Ext_KeySet *old_hist_keys; 226 227 /* 228 * Try to keep the very useful set_time values from the old hist 229 * keys. kadm5 loses this info, so this heuristic is the best we 230 * can do. 231 */ 232 old_hist_keys = &extp->data.u.hist_keys; 233 for (i = 0; i < old_hist_keys->len; i++) { 234 if (old_hist_keys->val[i].set_time == NULL) 235 continue; 236 for (k = 0; k < hist_keys->len; k++) { 237 if (hist_keys->val[k].kvno != old_hist_keys->val[k].kvno) 238 continue; 239 hist_keys->val[k].set_time = old_hist_keys->val[k].set_time; 240 old_hist_keys->val[k].set_time = NULL; 241 } 242 } 243 } 244 245 if (replace_hist_keys) { 246 /* If hist keys not given in key_data then don't blow away hist_keys */ 247 ret = hdb_replace_extension(context->context, ent, &ext); 248 if (ret) 249 goto out; 250 } 251 252 /* 253 * A structure copy is more efficient here than this would be: 254 * 255 * copy_Keys(&keys, &ent->keys); 256 * free_Keys(&keys); 257 * 258 * Of course, the above hdb_replace_extension() is not at all efficient... 259 */ 260 free_HDB_extension(&ext); 261 free_Keys(&ent->keys); 262 free_hdb_keyset(&hkset); 263 ent->keys = keys; 264 hdb_entry_set_pw_change_time(context->context, ent, 0); 265 hdb_entry_clear_password(context->context, ent); 266 267 return 0; 268 269out: 270 free_Keys(&keys); 271 free_HDB_extension(&ext); 272 return ret; 273} 274 275/* 276 * Set the keys of `ent' to `n_keys, keys' 277 */ 278 279kadm5_ret_t 280_kadm5_set_keys3(kadm5_server_context *context, 281 hdb_entry *ent, 282 int n_keys, 283 krb5_keyblock *keyblocks) 284{ 285 krb5_error_code ret; 286 int i; 287 unsigned len; 288 Key *keys; 289 290 len = n_keys; 291 keys = malloc (len * sizeof(*keys)); 292 if (keys == NULL && len != 0) 293 return ENOMEM; 294 295 _kadm5_init_keys (keys, len); 296 297 for(i = 0; i < n_keys; i++) { 298 keys[i].mkvno = NULL; 299 ret = krb5_copy_keyblock_contents (context->context, 300 &keyblocks[i], 301 &keys[i].key); 302 if(ret) 303 goto out; 304 keys[i].salt = NULL; 305 } 306 _kadm5_free_keys (context->context, ent->keys.len, ent->keys.val); 307 ent->keys.len = len; 308 ent->keys.val = keys; 309 310 hdb_entry_set_pw_change_time(context->context, ent, 0); 311 hdb_entry_clear_password(context->context, ent); 312 313 return 0; 314 out: 315 _kadm5_free_keys (context->context, len, keys); 316 return ret; 317} 318 319/* 320 * 321 */ 322 323static int 324is_des_key_p(int keytype) 325{ 326 return keytype == ETYPE_DES_CBC_CRC || 327 keytype == ETYPE_DES_CBC_MD4 || 328 keytype == ETYPE_DES_CBC_MD5; 329} 330 331 332/* 333 * Set the keys of `ent' to random keys and return them in `n_keys' 334 * and `new_keys'. 335 */ 336 337kadm5_ret_t 338_kadm5_set_keys_randomly (kadm5_server_context *context, 339 hdb_entry *ent, 340 int n_ks_tuple, 341 krb5_key_salt_tuple *ks_tuple, 342 krb5_keyblock **new_keys, 343 int *n_keys) 344{ 345 krb5_keyblock *kblock = NULL; 346 kadm5_ret_t ret = 0; 347 int des_keyblock; 348 size_t i, num_keys; 349 Key *keys; 350 351 ret = hdb_generate_key_set(context->context, ent->principal, 352 ks_tuple, n_ks_tuple, &keys, &num_keys, 1); 353 if (ret) 354 return ret; 355 356 kblock = malloc(num_keys * sizeof(kblock[0])); 357 if (kblock == NULL) { 358 ret = ENOMEM; 359 _kadm5_free_keys (context->context, num_keys, keys); 360 return ret; 361 } 362 memset(kblock, 0, num_keys * sizeof(kblock[0])); 363 364 des_keyblock = -1; 365 for (i = 0; i < num_keys; i++) { 366 367 /* 368 * To make sure all des keys are the the same we generate only 369 * the first one and then copy key to all other des keys. 370 */ 371 372 if (des_keyblock != -1 && is_des_key_p(keys[i].key.keytype)) { 373 ret = krb5_copy_keyblock_contents (context->context, 374 &kblock[des_keyblock], 375 &kblock[i]); 376 if (ret) 377 goto out; 378 kblock[i].keytype = keys[i].key.keytype; 379 } else { 380 ret = krb5_generate_random_keyblock (context->context, 381 keys[i].key.keytype, 382 &kblock[i]); 383 if (ret) 384 goto out; 385 386 if (is_des_key_p(keys[i].key.keytype)) 387 des_keyblock = i; 388 } 389 390 ret = krb5_copy_keyblock_contents (context->context, 391 &kblock[i], 392 &keys[i].key); 393 if (ret) 394 goto out; 395 } 396 397out: 398 if(ret) { 399 for (i = 0; i < num_keys; ++i) 400 krb5_free_keyblock_contents (context->context, &kblock[i]); 401 free(kblock); 402 _kadm5_free_keys (context->context, num_keys, keys); 403 return ret; 404 } 405 406 _kadm5_free_keys (context->context, ent->keys.len, ent->keys.val); 407 ent->keys.val = keys; 408 ent->keys.len = num_keys; 409 if (n_keys && new_keys) { 410 *new_keys = kblock; 411 *n_keys = num_keys; 412 } else { 413 free(kblock); 414 } 415 416 hdb_entry_set_pw_change_time(context->context, ent, 0); 417 hdb_entry_clear_password(context->context, ent); 418 419 return 0; 420} 421