1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/kdb/kdb_cpw.c */
4 * Copyright 1995, 2009 by the Massachusetts Institute of Technology.
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
27 * Copyright (C) 1998 by the FundsXpress, INC.
29 * All rights reserved.
31 * Export of this software from the United States of America may require
32 * a specific license from the United States Government. It is the
33 * responsibility of any person or organization contemplating export to
34 * obtain such a license before exporting.
36 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
37 * distribute this software and its documentation for any purpose and
38 * without fee is hereby granted, provided that the above copyright
39 * notice appear in all copies and that both that copyright notice and
40 * this permission notice appear in supporting documentation, and that
41 * the name of FundsXpress. not be used in advertising or publicity pertaining
42 * to distribution of the software without specific, written prior
43 * permission. FundsXpress makes no representations about the suitability of
44 * this software for any purpose. It is provided "as is" without express
45 * or implied warranty.
47 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
48 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
49 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
58 krb5_db_get_key_data_kvno(context, count, data)
64 /* Find last key version number */
65 for (kvno = i = 0; i < count; i++) {
66 if (kvno < data[i].key_data_kvno) {
67 kvno = data[i].key_data_kvno;
74 cleanup_key_data(context, count, data)
81 /* If data is NULL, count is always 0 */
82 if (data == NULL) return;
84 for (i = 0; i < count; i++) {
85 for (j = 0; j < data[i].key_data_ver; j++) {
86 if (data[i].key_data_length[j]) {
87 krb5_db_free(context, data[i].key_data_contents[j]);
91 krb5_db_free(context, data);
94 static krb5_error_code
95 add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno)
97 krb5_keyblock * master_key;
98 krb5_key_salt_tuple * ks_tuple;
100 krb5_db_entry * db_entry;
103 krb5_principal krbtgt_princ;
105 krb5_db_entry *krbtgt_entry;
106 int max_kvno, i, j, k;
107 krb5_error_code retval;
108 krb5_key_data tmp_key_data;
111 memset( &tmp_key_data, 0, sizeof(tmp_key_data));
114 retval = krb5_build_principal_ext(context, &krbtgt_princ,
115 db_entry->princ->realm.length,
116 db_entry->princ->realm.data,
119 db_entry->princ->realm.length,
120 db_entry->princ->realm.data,
125 /* Get tgt from database */
126 retval = krb5_db_get_principal(context, krbtgt_princ, 0, &krbtgt_entry);
127 krb5_free_principal(context, krbtgt_princ); /* don't need it anymore */
132 for (max_kvno = j = 0; j < krbtgt_entry->n_key_data; j++) {
133 if (max_kvno < krbtgt_entry->key_data[j].key_data_kvno) {
134 max_kvno = krbtgt_entry->key_data[j].key_data_kvno;
138 for (i = 0; i < ks_tuple_count; i++) {
139 krb5_boolean similar;
144 * We could use krb5_keysalt_iterate to replace this loop, or use
145 * krb5_keysalt_is_present for the loop below, but we want to avoid
146 * circular library dependencies.
148 for (j = 0; j < i; j++) {
149 if ((retval = krb5_c_enctype_compare(context,
150 ks_tuple[i].ks_enctype,
151 ks_tuple[j].ks_enctype,
162 if ((retval = krb5_dbe_create_key_data(context, db_entry)))
163 goto add_key_rnd_err;
165 /* there used to be code here to extract the old key, and derive
166 a new key from it. Now that there's a unified prng, that isn't
170 if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,
172 goto add_key_rnd_err;
175 /* db library will free this. Since, its a so, it could actually be using different memory management
176 function. So, its better if the memory is allocated by the db's malloc. So, a temporary memory is used
177 here which will later be copied to the db_entry */
178 retval = krb5_dbe_encrypt_key_data(context, master_key, &key, NULL,
179 kvno, &tmp_key_data);
181 krb5_free_keyblock_contents(context, &key);
183 goto add_key_rnd_err;
185 tptr = &db_entry->key_data[db_entry->n_key_data-1];
187 tptr->key_data_ver = tmp_key_data.key_data_ver;
188 tptr->key_data_kvno = tmp_key_data.key_data_kvno;
190 for( k = 0; k < tmp_key_data.key_data_ver; k++ )
192 tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
193 tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
194 if( tmp_key_data.key_data_contents[k] )
196 tptr->key_data_contents[k] = krb5_db_alloc(context, NULL, tmp_key_data.key_data_length[k]);
197 if( tptr->key_data_contents[k] == NULL )
199 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
200 db_entry->key_data = NULL;
201 db_entry->n_key_data = 0;
203 goto add_key_rnd_err;
205 memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
207 memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
208 free( tmp_key_data.key_data_contents[k] );
209 tmp_key_data.key_data_contents[k] = NULL;
216 krb5_db_free_principal(context, krbtgt_entry);
218 for( i = 0; i < tmp_key_data.key_data_ver; i++ )
220 if( tmp_key_data.key_data_contents[i] )
222 memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
223 free( tmp_key_data.key_data_contents[i] );
230 * Change random key for a krb5_db_entry
231 * Assumes the max kvno
233 * As a side effect all old keys are nuked if keepold is false.
236 krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry)
237 krb5_context context;
238 krb5_keyblock * master_key;
239 krb5_key_salt_tuple * ks_tuple;
241 krb5_boolean keepold;
242 krb5_db_entry * db_entry;
246 krb5_key_data * key_data;
247 krb5_error_code retval;
251 /* First save the old keydata */
252 kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
254 key_data_count = db_entry->n_key_data;
255 key_data = db_entry->key_data;
256 db_entry->key_data = NULL;
257 db_entry->n_key_data = 0;
259 /* increment the kvno */
262 retval = add_key_rnd(context, master_key, ks_tuple,
263 ks_tuple_count, db_entry, kvno);
265 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
266 db_entry->n_key_data = key_data_count;
267 db_entry->key_data = key_data;
268 } else if (keepold) {
269 n_new_key_data = db_entry->n_key_data;
270 for (i = 0; i < key_data_count; i++) {
271 retval = krb5_dbe_create_key_data(context, db_entry);
273 cleanup_key_data(context, db_entry->n_key_data,
277 db_entry->key_data[i+n_new_key_data] = key_data[i];
278 memset(&key_data[i], 0, sizeof(krb5_key_data));
280 krb5_db_free(context, key_data); /* we moved the cotents to new memory. But, the original block which contained the data */
282 cleanup_key_data(context, key_data_count, key_data);
288 * Add random key for a krb5_db_entry
289 * Assumes the max kvno
291 * As a side effect all old keys older than the max kvno are nuked.
294 krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry)
295 krb5_context context;
296 krb5_keyblock * master_key;
297 krb5_key_salt_tuple * ks_tuple;
299 krb5_db_entry * db_entry;
302 krb5_key_data * key_data;
303 krb5_error_code retval;
307 /* First save the old keydata */
308 kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
310 key_data_count = db_entry->n_key_data;
311 key_data = db_entry->key_data;
312 db_entry->key_data = NULL;
313 db_entry->n_key_data = 0;
315 /* increment the kvno */
318 if ((retval = add_key_rnd(context, master_key, ks_tuple,
319 ks_tuple_count, db_entry, kvno))) {
320 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
321 db_entry->n_key_data = key_data_count;
322 db_entry->key_data = key_data;
324 /* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */
325 for (i = 0; i < key_data_count; i++) {
326 if (key_data[i].key_data_kvno == (kvno - 1)) {
327 if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
328 cleanup_key_data(context, db_entry->n_key_data,
332 /* We should decrypt/re-encrypt the data to use the same mkvno*/
333 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
334 memset(&key_data[i], 0, sizeof(krb5_key_data));
337 cleanup_key_data(context, key_data_count, key_data);
342 /* Construct a random explicit salt. */
343 static krb5_error_code
344 make_random_salt(krb5_context context, krb5_keysalt *salt_out)
346 krb5_error_code retval;
347 unsigned char rndbuf[8];
348 krb5_data salt, rnd = make_data(rndbuf, sizeof(rndbuf));
352 * Salts are limited by RFC 4120 to 7-bit ASCII. For ease of examination
353 * and to avoid certain folding issues for older enctypes, we use printable
354 * characters with four fixed bits and four random bits, encoding 64
355 * psuedo-random bits into 16 bytes.
357 retval = krb5_c_random_make_octets(context, &rnd);
360 retval = alloc_data(&salt, sizeof(rndbuf) * 2);
363 for (i = 0; i < sizeof(rndbuf); i++) {
364 salt.data[i * 2] = 0x40 | (rndbuf[i] >> 4);
365 salt.data[i * 2 + 1] = 0x40 | (rndbuf[i] & 0xf);
368 salt_out->type = KRB5_KDB_SALTTYPE_SPECIAL;
369 salt_out->data = salt;
374 * Add key_data for a krb5_db_entry
375 * If passwd is NULL the assumes that the caller wants a random password.
377 static krb5_error_code
378 add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd,
380 krb5_context context;
381 krb5_keyblock * master_key;
382 krb5_key_salt_tuple * ks_tuple;
385 krb5_db_entry * db_entry;
388 krb5_error_code retval;
389 krb5_keysalt key_salt;
393 krb5_key_data tmp_key_data;
396 memset( &tmp_key_data, 0, sizeof(tmp_key_data));
400 for (i = 0; i < ks_tuple_count; i++) {
401 krb5_boolean similar;
406 * We could use krb5_keysalt_iterate to replace this loop, or use
407 * krb5_keysalt_is_present for the loop below, but we want to avoid
408 * circular library dependencies.
410 for (j = 0; j < i; j++) {
411 if ((retval = krb5_c_enctype_compare(context,
412 ks_tuple[i].ks_enctype,
413 ks_tuple[j].ks_enctype,
418 (ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype))
425 if ((retval = krb5_dbe_create_key_data(context, db_entry)))
428 /* Convert password string to key using appropriate salt */
429 switch (key_salt.type = ks_tuple[i].ks_salttype) {
430 case KRB5_KDB_SALTTYPE_ONLYREALM: {
431 krb5_data * saltdata;
432 if ((retval = krb5_copy_data(context, krb5_princ_realm(context,
433 db_entry->princ), &saltdata)))
436 key_salt.data = *saltdata;
440 case KRB5_KDB_SALTTYPE_NOREALM:
441 if ((retval=krb5_principal2salt_norealm(context, db_entry->princ,
445 case KRB5_KDB_SALTTYPE_NORMAL:
446 if ((retval = krb5_principal2salt(context, db_entry->princ,
450 case KRB5_KDB_SALTTYPE_V4:
451 key_salt.data.length = 0;
452 key_salt.data.data = 0;
454 case KRB5_KDB_SALTTYPE_AFS3:
455 /* The afs_mit_string_to_key needs to use strlen, and the
456 realm field is not (necessarily) NULL terminated. */
457 retval = krb5int_copy_data_contents_add0(context,
458 krb5_princ_realm(context,
463 key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/
465 case KRB5_KDB_SALTTYPE_SPECIAL:
466 retval = make_random_salt(context, &key_salt);
471 return(KRB5_KDB_BAD_SALTTYPE);
475 pwd.length = strlen(passwd);
477 /* AFS string to key will happen here */
478 if ((retval = krb5_c_string_to_key(context, ks_tuple[i].ks_enctype,
479 &pwd, &key_salt.data, &key))) {
480 if (key_salt.data.data)
481 free(key_salt.data.data);
485 if (key_salt.data.length == SALT_TYPE_AFS_LENGTH)
486 key_salt.data.length =
487 krb5_princ_realm(context, db_entry->princ)->length;
489 /* memory allocation to be done by db. So, use temporary block and later copy
490 it to the memory allocated by db */
491 retval = krb5_dbe_encrypt_key_data(context, master_key, &key,
492 (const krb5_keysalt *)&key_salt,
493 kvno, &tmp_key_data);
494 if (key_salt.data.data)
495 free(key_salt.data.data);
501 tptr = &db_entry->key_data[db_entry->n_key_data-1];
503 tptr->key_data_ver = tmp_key_data.key_data_ver;
504 tptr->key_data_kvno = tmp_key_data.key_data_kvno;
506 for( k = 0; k < tmp_key_data.key_data_ver; k++ )
508 tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
509 tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
510 if( tmp_key_data.key_data_contents[k] )
512 tptr->key_data_contents[k] = krb5_db_alloc(context, NULL, tmp_key_data.key_data_length[k]);
513 if( tptr->key_data_contents[k] == NULL )
515 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
516 db_entry->key_data = NULL;
517 db_entry->n_key_data = 0;
519 goto add_key_pwd_err;
521 memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
523 memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
524 free( tmp_key_data.key_data_contents[k] );
525 tmp_key_data.key_data_contents[k] = NULL;
530 for( i = 0; i < tmp_key_data.key_data_ver; i++ )
532 if( tmp_key_data.key_data_contents[i] )
534 memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
535 free( tmp_key_data.key_data_contents[i] );
543 * Change password for a krb5_db_entry
544 * Assumes the max kvno
546 * As a side effect all old keys are nuked if keepold is false.
549 krb5_dbe_def_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
550 new_kvno, keepold, db_entry)
551 krb5_context context;
552 krb5_keyblock * master_key;
553 krb5_key_salt_tuple * ks_tuple;
557 krb5_boolean keepold;
558 krb5_db_entry * db_entry;
562 krb5_key_data * key_data;
563 krb5_error_code retval;
567 /* First save the old keydata */
568 old_kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
570 key_data_count = db_entry->n_key_data;
571 key_data = db_entry->key_data;
572 db_entry->key_data = NULL;
573 db_entry->n_key_data = 0;
575 /* increment the kvno. if the requested kvno is too small,
576 increment the old kvno */
577 if (new_kvno < old_kvno+1)
578 new_kvno = old_kvno+1;
580 retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
581 passwd, db_entry, new_kvno);
583 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
584 db_entry->n_key_data = key_data_count;
585 db_entry->key_data = key_data;
586 } else if (keepold) {
587 n_new_key_data = db_entry->n_key_data;
588 for (i = 0; i < key_data_count; i++) {
589 retval = krb5_dbe_create_key_data(context, db_entry);
591 cleanup_key_data(context, db_entry->n_key_data,
595 db_entry->key_data[i+n_new_key_data] = key_data[i];
596 memset(&key_data[i], 0, sizeof(krb5_key_data));
598 krb5_db_free( context, key_data );
600 cleanup_key_data(context, key_data_count, key_data);
606 * Add password for a krb5_db_entry
607 * Assumes the max kvno
609 * As a side effect all old keys older than the max kvno are nuked.
612 krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
613 krb5_context context;
614 krb5_keyblock * master_key;
615 krb5_key_salt_tuple * ks_tuple;
618 krb5_db_entry * db_entry;
621 krb5_key_data * key_data;
622 krb5_error_code retval;
623 int old_kvno, new_kvno;
626 /* First save the old keydata */
627 old_kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
629 key_data_count = db_entry->n_key_data;
630 key_data = db_entry->key_data;
631 db_entry->key_data = NULL;
632 db_entry->n_key_data = 0;
634 /* increment the kvno */
635 new_kvno = old_kvno+1;
637 if ((retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
638 passwd, db_entry, new_kvno))) {
639 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
640 db_entry->n_key_data = key_data_count;
641 db_entry->key_data = key_data;
643 /* Copy keys with key_data_kvno == old_kvno */
644 for (i = 0; i < key_data_count; i++) {
645 if (key_data[i].key_data_kvno == old_kvno) {
646 if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
647 cleanup_key_data(context, db_entry->n_key_data,
651 /* We should decrypt/re-encrypt the data to use the same mkvno*/
652 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
653 memset(&key_data[i], 0, sizeof(krb5_key_data));
656 cleanup_key_data(context, key_data_count, key_data);