dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / krb5 / kdb / kdb_cpw.c
blob523b57fe0bcbad4a15c613768a9fda6e3852931d
1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /*
7 * lib/kdb/kdb_cpw.c
9 * Copyright 1995 by the Massachusetts Institute of Technology.
10 * All Rights Reserved.
12 * Export of this software from the United States of America may
13 * require a specific license from the United States Government.
14 * It is the responsibility of any person or organization contemplating
15 * export to obtain such a license before exporting.
17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18 * distribute this software and its documentation for any purpose and
19 * without fee is hereby granted, provided that the above copyright
20 * notice appear in all copies and that both that copyright notice and
21 * this permission notice appear in supporting documentation, and that
22 * the name of M.I.T. not be used in advertising or publicity pertaining
23 * to distribution of the software without specific, written prior
24 * permission. Furthermore if you modify this software you must label
25 * your software as modified software and not distribute it in such a
26 * fashion that it might be confused with the original M.I.T. software.
27 * M.I.T. makes no representations about the suitability of
28 * this software for any purpose. It is provided "as is" without express
29 * or implied warranty.
34 * Copyright (C) 1998 by the FundsXpress, INC.
36 * All rights reserved.
38 * Export of this software from the United States of America may require
39 * a specific license from the United States Government. It is the
40 * responsibility of any person or organization contemplating export to
41 * obtain such a license before exporting.
43 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
44 * distribute this software and its documentation for any purpose and
45 * without fee is hereby granted, provided that the above copyright
46 * notice appear in all copies and that both that copyright notice and
47 * this permission notice appear in supporting documentation, and that
48 * the name of FundsXpress. not be used in advertising or publicity pertaining
49 * to distribution of the software without specific, written prior
50 * permission. FundsXpress makes no representations about the suitability of
51 * this software for any purpose. It is provided "as is" without express
52 * or implied warranty.
54 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
55 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
56 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
59 #include "k5-int.h"
60 #include "kdb.h"
61 #include <stdio.h>
62 #include <errno.h>
64 static int
65 get_key_data_kvno(context, count, data)
66 krb5_context context;
67 int count;
68 krb5_key_data * data;
70 int i, kvno;
71 /* Find last key version number */
72 for (kvno = i = 0; i < count; i++) {
73 if (kvno < data[i].key_data_kvno) {
74 kvno = data[i].key_data_kvno;
77 return(kvno);
80 static void
81 cleanup_key_data(context, count, data)
82 krb5_context context;
83 int count;
84 krb5_key_data * data;
86 int i, j;
88 /* If data is NULL, count is always 0 */
89 if (data == NULL) return;
91 for (i = 0; i < count; i++) {
92 for (j = 0; j < data[i].key_data_ver; j++) {
93 if (data[i].key_data_length[j]) {
94 krb5_db_free(context, data[i].key_data_contents[j]);
98 krb5_db_free(context, data);
101 static krb5_error_code
102 add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno)
103 krb5_context context;
104 krb5_keyblock * master_key;
105 krb5_key_salt_tuple * ks_tuple;
106 int ks_tuple_count;
107 krb5_db_entry * db_entry;
108 int kvno;
110 krb5_principal krbtgt_princ;
111 krb5_keyblock key;
112 krb5_db_entry krbtgt_entry;
113 krb5_boolean more;
114 int max_kvno, one, i, j, k;
115 krb5_error_code retval;
116 krb5_key_data tmp_key_data;
117 krb5_key_data *tptr;
119 memset( &tmp_key_data, 0, sizeof(tmp_key_data));
122 retval = krb5_build_principal_ext(context, &krbtgt_princ,
123 db_entry->princ->realm.length,
124 db_entry->princ->realm.data,
125 KRB5_TGS_NAME_SIZE,
126 KRB5_TGS_NAME,
127 db_entry->princ->realm.length,
128 db_entry->princ->realm.data,
130 if (retval)
131 return retval;
133 /* Get tgt from database */
134 retval = krb5_db_get_principal(context, krbtgt_princ, &krbtgt_entry,
135 &one, &more);
136 krb5_free_principal(context, krbtgt_princ); /* don't need it anymore */
137 if (retval)
138 return(retval);
139 if ((one > 1) || (more)) {
140 krb5_db_free_principal(context, &krbtgt_entry, one);
141 return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
143 if (!one)
144 return KRB5_KDB_NOENTRY;
146 /* Get max kvno */
147 for (max_kvno = j = 0; j < krbtgt_entry.n_key_data; j++) {
148 if (max_kvno < krbtgt_entry.key_data[j].key_data_kvno) {
149 max_kvno = krbtgt_entry.key_data[j].key_data_kvno;
153 for (i = 0; i < ks_tuple_count; i++) {
154 krb5_boolean similar;
156 similar = 0;
159 * We could use krb5_keysalt_iterate to replace this loop, or use
160 * krb5_keysalt_is_present for the loop below, but we want to avoid
161 * circular library dependencies.
163 for (j = 0; j < i; j++) {
164 if ((retval = krb5_c_enctype_compare(context,
165 ks_tuple[i].ks_enctype,
166 ks_tuple[j].ks_enctype,
167 &similar)))
168 return(retval);
170 if (similar)
171 break;
174 if (similar)
175 continue;
177 if ((retval = krb5_dbe_create_key_data(context, db_entry)))
178 goto add_key_rnd_err;
180 /* there used to be code here to extract the old key, and derive
181 a new key from it. Now that there's a unified prng, that isn't
182 necessary. */
184 /* make new key */
185 if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,
186 &key)))
187 goto add_key_rnd_err;
190 /* db library will free this. Since, its a so, it could actually be using different memory management
191 function. So, its better if the memory is allocated by the db's malloc. So, a temporary memory is used
192 here which will later be copied to the db_entry */
193 retval = krb5_dbekd_encrypt_key_data(context, master_key,
194 &key, NULL, kvno,
195 &tmp_key_data);
197 krb5_free_keyblock_contents(context, &key);
198 if( retval )
199 goto add_key_rnd_err;
201 tptr = &db_entry->key_data[db_entry->n_key_data-1];
203 tptr->key_data_ver = tmp_key_data.key_data_ver;
204 tptr->key_data_kvno = tmp_key_data.key_data_kvno;
206 for( k = 0; k < tmp_key_data.key_data_ver; k++ )
208 tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
209 tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
210 if( tmp_key_data.key_data_contents[k] )
212 tptr->key_data_contents[k] = krb5_db_alloc(context, NULL, tmp_key_data.key_data_length[k]);
213 if( tptr->key_data_contents[k] == NULL )
215 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
216 db_entry->key_data = NULL;
217 db_entry->n_key_data = 0;
218 retval = ENOMEM;
219 goto add_key_rnd_err;
221 memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
223 memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
224 free( tmp_key_data.key_data_contents[k] );
225 tmp_key_data.key_data_contents[k] = NULL;
231 add_key_rnd_err:
232 krb5_db_free_principal(context, &krbtgt_entry, one);
234 for( i = 0; i < tmp_key_data.key_data_ver; i++ )
236 if( tmp_key_data.key_data_contents[i] )
238 memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
239 free( tmp_key_data.key_data_contents[i] );
242 return(retval);
246 * Change random key for a krb5_db_entry
247 * Assumes the max kvno
249 * As a side effect all old keys are nuked if keepold is false.
251 krb5_error_code
252 krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry)
253 krb5_context context;
254 krb5_keyblock * master_key;
255 krb5_key_salt_tuple * ks_tuple;
256 int ks_tuple_count;
257 krb5_boolean keepold;
258 krb5_db_entry * db_entry;
260 int key_data_count;
261 int n_new_key_data;
262 krb5_key_data * key_data;
263 krb5_error_code retval;
264 int kvno;
265 int i;
267 /* First save the old keydata */
268 kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
269 key_data_count = db_entry->n_key_data;
270 key_data = db_entry->key_data;
271 db_entry->key_data = NULL;
272 db_entry->n_key_data = 0;
274 /* increment the kvno */
275 kvno++;
277 retval = add_key_rnd(context, master_key, ks_tuple,
278 ks_tuple_count, db_entry, kvno);
279 if (retval) {
280 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
281 db_entry->n_key_data = key_data_count;
282 db_entry->key_data = key_data;
283 } else if (keepold) {
284 n_new_key_data = db_entry->n_key_data;
285 for (i = 0; i < key_data_count; i++) {
286 retval = krb5_dbe_create_key_data(context, db_entry);
287 if (retval) {
288 cleanup_key_data(context, db_entry->n_key_data,
289 db_entry->key_data);
290 break;
292 db_entry->key_data[i+n_new_key_data] = key_data[i];
293 memset(&key_data[i], 0, sizeof(krb5_key_data));
295 krb5_db_free(context, key_data); /* we moved the cotents to new memory. But, the original block which contained the data */
296 } else {
297 cleanup_key_data(context, key_data_count, key_data);
299 return(retval);
303 * Add random key for a krb5_db_entry
304 * Assumes the max kvno
306 * As a side effect all old keys older than the max kvno are nuked.
308 krb5_error_code
309 krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry)
310 krb5_context context;
311 krb5_keyblock * master_key;
312 krb5_key_salt_tuple * ks_tuple;
313 int ks_tuple_count;
314 krb5_db_entry * db_entry;
316 int key_data_count;
317 krb5_key_data * key_data;
318 krb5_error_code retval;
319 int kvno;
320 int i;
322 /* First save the old keydata */
323 kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
324 key_data_count = db_entry->n_key_data;
325 key_data = db_entry->key_data;
326 db_entry->key_data = NULL;
327 db_entry->n_key_data = 0;
329 /* increment the kvno */
330 kvno++;
332 if ((retval = add_key_rnd(context, master_key, ks_tuple,
333 ks_tuple_count, db_entry, kvno))) {
334 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
335 db_entry->n_key_data = key_data_count;
336 db_entry->key_data = key_data;
337 } else {
338 /* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */
339 for (i = 0; i < key_data_count; i++) {
340 if (key_data[i].key_data_kvno == (kvno - 1)) {
341 if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
342 cleanup_key_data(context, db_entry->n_key_data,
343 db_entry->key_data);
344 break;
346 /* We should decrypt/re-encrypt the data to use the same mkvno*/
347 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
348 memset(&key_data[i], 0, sizeof(krb5_key_data));
351 cleanup_key_data(context, key_data_count, key_data);
353 return(retval);
357 * Add key_data for a krb5_db_entry
358 * If passwd is NULL the assumes that the caller wants a random password.
360 static krb5_error_code
361 add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd,
362 db_entry, kvno)
363 krb5_context context;
364 krb5_keyblock * master_key;
365 krb5_key_salt_tuple * ks_tuple;
366 int ks_tuple_count;
367 char * passwd;
368 krb5_db_entry * db_entry;
369 int kvno;
371 krb5_error_code retval;
372 krb5_keysalt key_salt;
373 krb5_keyblock key;
374 krb5_data pwd;
375 int i, j, k;
376 krb5_key_data tmp_key_data;
377 krb5_key_data *tptr;
379 memset( &tmp_key_data, 0, sizeof(tmp_key_data));
381 retval = 0;
383 for (i = 0; i < ks_tuple_count; i++) {
384 krb5_boolean similar;
386 similar = 0;
389 * We could use krb5_keysalt_iterate to replace this loop, or use
390 * krb5_keysalt_is_present for the loop below, but we want to avoid
391 * circular library dependencies.
393 for (j = 0; j < i; j++) {
394 if ((retval = krb5_c_enctype_compare(context,
395 ks_tuple[i].ks_enctype,
396 ks_tuple[j].ks_enctype,
397 &similar)))
398 return(retval);
400 if (similar &&
401 (ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype))
402 break;
405 if (j < i)
406 continue;
408 if ((retval = krb5_dbe_create_key_data(context, db_entry)))
409 return(retval);
411 /* Convert password string to key using appropriate salt */
412 switch (key_salt.type = ks_tuple[i].ks_salttype) {
413 case KRB5_KDB_SALTTYPE_ONLYREALM: {
414 krb5_data * saltdata;
415 if ((retval = krb5_copy_data(context, krb5_princ_realm(context,
416 db_entry->princ), &saltdata)))
417 return(retval);
419 key_salt.data = *saltdata;
420 krb5_xfree(saltdata);
422 break;
423 case KRB5_KDB_SALTTYPE_NOREALM:
424 if ((retval=krb5_principal2salt_norealm(context, db_entry->princ,
425 &key_salt.data)))
426 return(retval);
427 break;
428 case KRB5_KDB_SALTTYPE_NORMAL:
429 if ((retval = krb5_principal2salt(context, db_entry->princ,
430 &key_salt.data)))
431 return(retval);
432 break;
433 case KRB5_KDB_SALTTYPE_V4:
434 key_salt.data.length = 0;
435 key_salt.data.data = 0;
436 break;
437 case KRB5_KDB_SALTTYPE_AFS3: {
438 #if 0
439 krb5_data * saltdata;
440 if (retval = krb5_copy_data(context, krb5_princ_realm(context,
441 db_entry->princ), &saltdata))
442 return(retval);
444 key_salt.data = *saltdata;
445 key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/
446 krb5_xfree(saltdata);
447 #else
448 /* Why do we do this? Well, the afs_mit_string_to_key needs to
449 use strlen, and the realm is not NULL terminated.... */
450 unsigned int slen =
451 (*krb5_princ_realm(context,db_entry->princ)).length;
452 if(!(key_salt.data.data = (char *) malloc(slen+1)))
453 return ENOMEM;
454 key_salt.data.data[slen] = 0;
455 memcpy((char *)key_salt.data.data,
456 (char *)(*krb5_princ_realm(context,db_entry->princ)).data,
457 slen);
458 key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/
459 #endif
462 break;
463 default:
464 return(KRB5_KDB_BAD_SALTTYPE);
467 pwd.data = passwd;
468 pwd.length = strlen(passwd);
470 /* Solaris Kerberos */
471 memset(&key, 0, sizeof (krb5_keyblock));
473 /* AFS string to key will happen here */
474 if ((retval = krb5_c_string_to_key(context, ks_tuple[i].ks_enctype,
475 &pwd, &key_salt.data, &key))) {
476 free(key_salt.data.data);
477 return(retval);
480 if (key_salt.data.length == SALT_TYPE_AFS_LENGTH)
481 key_salt.data.length =
482 krb5_princ_realm(context, db_entry->princ)->length;
484 /* memory allocation to be done by db. So, use temporary block and later copy
485 it to the memory allocated by db */
486 retval = krb5_dbekd_encrypt_key_data(context, master_key, &key,
487 (const krb5_keysalt *)&key_salt,
488 kvno, &tmp_key_data);
489 free(key_salt.data.data);
491 /* Solaris Kerberos */
492 krb5_free_keyblock_contents(context, &key);
494 if( retval )
495 return retval;
497 tptr = &db_entry->key_data[db_entry->n_key_data-1];
499 tptr->key_data_ver = tmp_key_data.key_data_ver;
500 tptr->key_data_kvno = tmp_key_data.key_data_kvno;
502 for( k = 0; k < tmp_key_data.key_data_ver; k++ )
504 tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
505 tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
506 if( tmp_key_data.key_data_contents[k] )
508 tptr->key_data_contents[k] = krb5_db_alloc(context, NULL, tmp_key_data.key_data_length[k]);
509 if( tptr->key_data_contents[k] == NULL )
511 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
512 db_entry->key_data = NULL;
513 db_entry->n_key_data = 0;
514 retval = ENOMEM;
515 goto add_key_pwd_err;
517 memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
519 memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
520 free( tmp_key_data.key_data_contents[k] );
521 tmp_key_data.key_data_contents[k] = NULL;
525 add_key_pwd_err:
526 for( i = 0; i < tmp_key_data.key_data_ver; i++ )
528 if( tmp_key_data.key_data_contents[i] )
530 memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
531 free( tmp_key_data.key_data_contents[i] );
535 return(retval);
539 * Change password for a krb5_db_entry
540 * Assumes the max kvno
542 * As a side effect all old keys are nuked if keepold is false.
544 krb5_error_code
545 krb5_dbe_def_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
546 new_kvno, keepold, db_entry)
547 krb5_context context;
548 krb5_keyblock * master_key;
549 krb5_key_salt_tuple * ks_tuple;
550 int ks_tuple_count;
551 char * passwd;
552 int new_kvno;
553 krb5_boolean keepold;
554 krb5_db_entry * db_entry;
556 int key_data_count;
557 int n_new_key_data;
558 krb5_key_data * key_data;
559 krb5_error_code retval;
560 int old_kvno;
561 int i;
563 /* First save the old keydata */
564 old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
565 db_entry->key_data);
566 key_data_count = db_entry->n_key_data;
567 key_data = db_entry->key_data;
568 db_entry->key_data = NULL;
569 db_entry->n_key_data = 0;
571 /* increment the kvno. if the requested kvno is too small,
572 increment the old kvno */
573 if (new_kvno < old_kvno+1)
574 new_kvno = old_kvno+1;
576 retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
577 passwd, db_entry, new_kvno);
578 if (retval) {
579 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
580 db_entry->n_key_data = key_data_count;
581 db_entry->key_data = key_data;
582 } else if (keepold) {
583 n_new_key_data = db_entry->n_key_data;
584 for (i = 0; i < key_data_count; i++) {
585 retval = krb5_dbe_create_key_data(context, db_entry);
586 if (retval) {
587 cleanup_key_data(context, db_entry->n_key_data,
588 db_entry->key_data);
589 break;
591 db_entry->key_data[i+n_new_key_data] = key_data[i];
592 memset(&key_data[i], 0, sizeof(krb5_key_data));
594 krb5_db_free( context, key_data );
595 } else {
596 cleanup_key_data(context, key_data_count, key_data);
598 return(retval);
602 * Add password for a krb5_db_entry
603 * Assumes the max kvno
605 * As a side effect all old keys older than the max kvno are nuked.
607 krb5_error_code
608 krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
609 krb5_context context;
610 krb5_keyblock * master_key;
611 krb5_key_salt_tuple * ks_tuple;
612 int ks_tuple_count;
613 char * passwd;
614 krb5_db_entry * db_entry;
616 int key_data_count;
617 krb5_key_data * key_data;
618 krb5_error_code retval;
619 int old_kvno, new_kvno;
620 int i;
622 /* First save the old keydata */
623 old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
624 db_entry->key_data);
625 key_data_count = db_entry->n_key_data;
626 key_data = db_entry->key_data;
627 db_entry->key_data = NULL;
628 db_entry->n_key_data = 0;
630 /* increment the kvno */
631 new_kvno = old_kvno+1;
633 if ((retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
634 passwd, db_entry, new_kvno))) {
635 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
636 db_entry->n_key_data = key_data_count;
637 db_entry->key_data = key_data;
638 } else {
639 /* Copy keys with key_data_kvno == old_kvno */
640 for (i = 0; i < key_data_count; i++) {
641 if (key_data[i].key_data_kvno == old_kvno) {
642 if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
643 cleanup_key_data(context, db_entry->n_key_data,
644 db_entry->key_data);
645 break;
647 /* We should decrypt/re-encrypt the data to use the same mkvno*/
648 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
649 memset(&key_data[i], 0, sizeof(krb5_key_data));
652 cleanup_key_data(context, key_data_count, key_data);
654 return(retval);