4 * Copyright 1995 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.
29 * Copyright (C) 1998 by the FundsXpress, INC.
31 * All rights reserved.
33 * Export of this software from the United States of America may require
34 * a specific license from the United States Government. It is the
35 * responsibility of any person or organization contemplating export to
36 * obtain such a license before exporting.
38 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
39 * distribute this software and its documentation for any purpose and
40 * without fee is hereby granted, provided that the above copyright
41 * notice appear in all copies and that both that copyright notice and
42 * this permission notice appear in supporting documentation, and that
43 * the name of FundsXpress. not be used in advertising or publicity pertaining
44 * to distribution of the software without specific, written prior
45 * permission. FundsXpress makes no representations about the suitability of
46 * this software for any purpose. It is provided "as is" without express
47 * or implied warranty.
49 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
50 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
51 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
60 get_key_data_kvno(context, count, data)
66 /* Find last key version number */
67 for (kvno = i = 0; i < count; i++) {
68 if (kvno < data[i].key_data_kvno) {
69 kvno = data[i].key_data_kvno;
76 cleanup_key_data(context, count, data)
83 for (i = 0; i < count; i++) {
84 for (j = 0; j < data[i].key_data_ver; j++) {
85 if (data[i].key_data_length[j]) {
86 free(data[i].key_data_contents[j]);
93 static krb5_error_code
94 add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno)
96 krb5_keyblock * master_key;
97 krb5_key_salt_tuple * ks_tuple;
99 krb5_db_entry * db_entry;
102 krb5_principal krbtgt_princ;
104 krb5_db_entry krbtgt_entry;
105 krb5_key_data * krbtgt_kdata;
107 int max_kvno, one, i, j;
108 krb5_error_code retval;
110 retval = krb5_build_principal_ext(context, &krbtgt_princ,
111 db_entry->princ->realm.length,
112 db_entry->princ->realm.data,
115 db_entry->princ->realm.length,
116 db_entry->princ->realm.data,
121 /* Get tgt from database */
122 retval = krb5_db_get_principal(context, krbtgt_princ, &krbtgt_entry,
124 krb5_free_principal(context, krbtgt_princ); /* don't need it anymore */
127 if ((one > 1) || (more)) {
128 krb5_db_free_principal(context, &krbtgt_entry, one);
129 return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
132 return KRB5_KDB_NOENTRY;
135 for (max_kvno = j = 0; j < krbtgt_entry.n_key_data; j++) {
136 if (max_kvno < krbtgt_entry.key_data[j].key_data_kvno) {
137 max_kvno = krbtgt_entry.key_data[j].key_data_kvno;
141 for (i = 0; i < ks_tuple_count; i++) {
142 krb5_boolean similar;
147 * We could use krb5_keysalt_iterate to replace this loop, or use
148 * krb5_keysalt_is_present for the loop below, but we want to avoid
149 * circular library dependencies.
151 for (j = 0; j < i; j++) {
152 if ((retval = krb5_c_enctype_compare(context,
153 ks_tuple[i].ks_enctype,
154 ks_tuple[j].ks_enctype,
165 if (retval = krb5_dbe_create_key_data(context, db_entry))
166 goto add_key_rnd_err;
168 /* there used to be code here to extract the old key, and derive
169 a new key from it. Now that there's a unified prng, that isn't
173 if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,
175 goto add_key_rnd_err;
177 retval = krb5_dbekd_encrypt_key_data(context, master_key,
179 &db_entry->key_data[db_entry->n_key_data-1]);
181 krb5_free_keyblock_contents(context, &key);
184 goto add_key_rnd_err;
188 krb5_db_free_principal(context, &krbtgt_entry, one);
194 * Change random key for a krb5_db_entry
195 * Assumes the max kvno
197 * As a side effect all old keys are nuked if keepold is false.
200 krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry)
201 krb5_context context;
202 krb5_keyblock * master_key;
203 krb5_key_salt_tuple * ks_tuple;
205 krb5_boolean keepold;
206 krb5_db_entry * db_entry;
210 krb5_key_data * key_data;
211 krb5_error_code retval;
215 /* First save the old keydata */
216 kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
217 key_data_count = db_entry->n_key_data;
218 key_data = db_entry->key_data;
219 db_entry->key_data = NULL;
220 db_entry->n_key_data = 0;
222 /* increment the kvno */
225 retval = add_key_rnd(context, master_key, ks_tuple,
226 ks_tuple_count, db_entry, kvno);
228 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
229 db_entry->n_key_data = key_data_count;
230 db_entry->key_data = key_data;
231 } else if (keepold) {
232 n_new_key_data = db_entry->n_key_data;
233 for (i = 0; i < key_data_count; i++) {
234 retval = krb5_dbe_create_key_data(context, db_entry);
236 cleanup_key_data(context, db_entry->n_key_data,
240 db_entry->key_data[i+n_new_key_data] = key_data[i];
241 memset(&key_data[i], 0, sizeof(krb5_key_data));
244 cleanup_key_data(context, key_data_count, key_data);
250 * Add random key for a krb5_db_entry
251 * Assumes the max kvno
253 * As a side effect all old keys older than the max kvno are nuked.
256 krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry)
257 krb5_context context;
258 krb5_keyblock * master_key;
259 krb5_key_salt_tuple * ks_tuple;
261 krb5_db_entry * db_entry;
264 krb5_key_data * key_data;
265 krb5_error_code retval;
269 /* First save the old keydata */
270 kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
271 key_data_count = db_entry->n_key_data;
272 key_data = db_entry->key_data;
273 db_entry->key_data = NULL;
274 db_entry->n_key_data = 0;
276 /* increment the kvno */
279 if (retval = add_key_rnd(context, master_key, ks_tuple,
280 ks_tuple_count, db_entry, kvno)) {
281 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
282 db_entry->n_key_data = key_data_count;
283 db_entry->key_data = key_data;
285 /* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */
286 for (i = 0; i < key_data_count; i++) {
287 if (key_data[i].key_data_kvno == (kvno - 1)) {
288 if (retval = krb5_dbe_create_key_data(context, db_entry)) {
289 cleanup_key_data(context, db_entry->n_key_data,
293 /* We should decrypt/re-encrypt the data to use the same mkvno*/
294 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
295 memset(&key_data[i], 0, sizeof(krb5_key_data));
298 cleanup_key_data(context, key_data_count, key_data);
304 * Add key_data for a krb5_db_entry
305 * If passwd is NULL the assumes that the caller wants a random password.
307 static krb5_error_code
308 add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd,
310 krb5_context context;
311 krb5_keyblock * master_key;
312 krb5_key_salt_tuple * ks_tuple;
315 krb5_db_entry * db_entry;
318 krb5_error_code retval;
319 krb5_keysalt key_salt;
327 for (i = 0; i < ks_tuple_count; i++) {
328 krb5_boolean similar;
333 * We could use krb5_keysalt_iterate to replace this loop, or use
334 * krb5_keysalt_is_present for the loop below, but we want to avoid
335 * circular library dependencies.
337 for (j = 0; j < i; j++) {
338 if ((retval = krb5_c_enctype_compare(context,
339 ks_tuple[i].ks_enctype,
340 ks_tuple[j].ks_enctype,
345 (ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype))
352 if (retval = krb5_dbe_create_key_data(context, db_entry))
355 /* Convert password string to key using appropriate salt */
356 switch (key_salt.type = ks_tuple[i].ks_salttype) {
357 case KRB5_KDB_SALTTYPE_ONLYREALM: {
358 krb5_data * saltdata;
359 if (retval = krb5_copy_data(context, krb5_princ_realm(context,
360 db_entry->princ), &saltdata))
363 key_salt.data = *saltdata;
364 krb5_xfree(saltdata);
367 case KRB5_KDB_SALTTYPE_NOREALM:
368 if (retval=krb5_principal2salt_norealm(context, db_entry->princ,
372 case KRB5_KDB_SALTTYPE_NORMAL:
373 if (retval = krb5_principal2salt(context, db_entry->princ,
377 case KRB5_KDB_SALTTYPE_V4:
378 key_salt.data.length = 0;
379 key_salt.data.data = 0;
381 case KRB5_KDB_SALTTYPE_AFS3: {
383 krb5_data * saltdata;
384 if (retval = krb5_copy_data(context, krb5_princ_realm(context,
385 db_entry->princ), &saltdata))
388 key_salt.data = *saltdata;
389 key_salt.data.length = -1; /*length actually used below...*/
390 krb5_xfree(saltdata);
392 /* Why do we do this? Well, the afs_mit_string_to_key needs to
393 use strlen, and the realm is not NULL terminated.... */
394 int slen = (*krb5_princ_realm(context,db_entry->princ)).length;
395 if(!(key_salt.data.data = (char *) malloc(slen+1)))
397 key_salt.data.data[slen] = 0;
398 memcpy((char *)key_salt.data.data,
399 (char *)(*krb5_princ_realm(context,db_entry->princ)).data,
401 key_salt.data.length = -1; /*length actually used below...*/
407 return(KRB5_KDB_BAD_SALTTYPE);
411 pwd.length = strlen(passwd);
413 if ((retval = krb5_c_string_to_key(context, ks_tuple[i].ks_enctype,
414 &pwd, &key_salt.data, &key))) {
415 if (key_salt.data.data)
416 free(key_salt.data.data);
420 if (key_salt.data.length == -1)
421 key_salt.data.length =
422 krb5_princ_realm(context, db_entry->princ)->length;
424 if (retval = krb5_dbekd_encrypt_key_data(context, master_key, &key,
425 (const krb5_keysalt *)&key_salt,
426 kvno, &db_entry->key_data[db_entry->n_key_data-1])) {
427 if (key_salt.data.data)
428 free(key_salt.data.data);
429 krb5_xfree(key.contents);
432 if (key_salt.data.data)
433 free(key_salt.data.data);
434 krb5_xfree(key.contents);
440 * Change password for a krb5_db_entry
441 * Assumes the max kvno
443 * As a side effect all old keys are nuked if keepold is false.
446 krb5_dbe_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
447 new_kvno, keepold, db_entry)
448 krb5_context context;
449 krb5_keyblock * master_key;
450 krb5_key_salt_tuple * ks_tuple;
454 krb5_boolean keepold;
455 krb5_db_entry * db_entry;
459 krb5_key_data * key_data;
460 krb5_error_code retval;
464 /* First save the old keydata */
465 old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
467 key_data_count = db_entry->n_key_data;
468 key_data = db_entry->key_data;
469 db_entry->key_data = NULL;
470 db_entry->n_key_data = 0;
472 /* increment the kvno. if the requested kvno is too small,
473 increment the old kvno */
474 if (new_kvno < old_kvno+1)
475 new_kvno = old_kvno+1;
477 retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
478 passwd, db_entry, new_kvno);
480 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
481 db_entry->n_key_data = key_data_count;
482 db_entry->key_data = key_data;
483 } else if (keepold) {
484 n_new_key_data = db_entry->n_key_data;
485 for (i = 0; i < key_data_count; i++) {
486 retval = krb5_dbe_create_key_data(context, db_entry);
488 cleanup_key_data(context, db_entry->n_key_data,
492 db_entry->key_data[i+n_new_key_data] = key_data[i];
493 memset(&key_data[i], 0, sizeof(krb5_key_data));
496 cleanup_key_data(context, key_data_count, key_data);
502 * Add password for a krb5_db_entry
503 * Assumes the max kvno
505 * As a side effect all old keys older than the max kvno are nuked.
508 krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
509 krb5_context context;
510 krb5_keyblock * master_key;
511 krb5_key_salt_tuple * ks_tuple;
514 krb5_db_entry * db_entry;
517 krb5_key_data * key_data;
518 krb5_error_code retval;
519 int old_kvno, new_kvno;
522 /* First save the old keydata */
523 old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
525 key_data_count = db_entry->n_key_data;
526 key_data = db_entry->key_data;
527 db_entry->key_data = NULL;
528 db_entry->n_key_data = 0;
530 /* increment the kvno */
531 new_kvno = old_kvno+1;
533 if (retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
534 passwd, db_entry, new_kvno)) {
535 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
536 db_entry->n_key_data = key_data_count;
537 db_entry->key_data = key_data;
539 /* Copy keys with key_data_kvno == old_kvno */
540 for (i = 0; i < key_data_count; i++) {
541 if (key_data[i].key_data_kvno == old_kvno) {
542 if (retval = krb5_dbe_create_key_data(context, db_entry)) {
543 cleanup_key_data(context, db_entry->n_key_data,
547 /* We should decrypt/re-encrypt the data to use the same mkvno*/
548 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
549 memset(&key_data[i], 0, sizeof(krb5_key_data));
552 cleanup_key_data(context, key_data_count, key_data);