2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
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.
65 get_key_data_kvno(context
, count
, data
)
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
;
81 cleanup_key_data(context
, count
, data
)
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
;
107 krb5_db_entry
* db_entry
;
110 krb5_principal krbtgt_princ
;
112 krb5_db_entry krbtgt_entry
;
114 int max_kvno
, one
, i
, j
, k
;
115 krb5_error_code retval
;
116 krb5_key_data tmp_key_data
;
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
,
127 db_entry
->princ
->realm
.length
,
128 db_entry
->princ
->realm
.data
,
133 /* Get tgt from database */
134 retval
= krb5_db_get_principal(context
, krbtgt_princ
, &krbtgt_entry
,
136 krb5_free_principal(context
, krbtgt_princ
); /* don't need it anymore */
139 if ((one
> 1) || (more
)) {
140 krb5_db_free_principal(context
, &krbtgt_entry
, one
);
141 return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE
;
144 return KRB5_KDB_NOENTRY
;
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
;
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
,
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
185 if ((retval
= krb5_c_make_random_key(context
, ks_tuple
[i
].ks_enctype
,
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
,
197 krb5_free_keyblock_contents(context
, &key
);
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;
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
;
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
] );
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.
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
;
257 krb5_boolean keepold
;
258 krb5_db_entry
* db_entry
;
262 krb5_key_data
* key_data
;
263 krb5_error_code retval
;
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 */
277 retval
= add_key_rnd(context
, master_key
, ks_tuple
,
278 ks_tuple_count
, db_entry
, kvno
);
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
);
288 cleanup_key_data(context
, db_entry
->n_key_data
,
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 */
297 cleanup_key_data(context
, key_data_count
, key_data
);
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.
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
;
314 krb5_db_entry
* db_entry
;
317 krb5_key_data
* key_data
;
318 krb5_error_code retval
;
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 */
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
;
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
,
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
);
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
,
363 krb5_context context
;
364 krb5_keyblock
* master_key
;
365 krb5_key_salt_tuple
* ks_tuple
;
368 krb5_db_entry
* db_entry
;
371 krb5_error_code retval
;
372 krb5_keysalt key_salt
;
376 krb5_key_data tmp_key_data
;
379 memset( &tmp_key_data
, 0, sizeof(tmp_key_data
));
383 for (i
= 0; i
< ks_tuple_count
; i
++) {
384 krb5_boolean similar
;
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
,
401 (ks_tuple
[j
].ks_salttype
== ks_tuple
[i
].ks_salttype
))
408 if ((retval
= krb5_dbe_create_key_data(context
, db_entry
)))
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
)))
419 key_salt
.data
= *saltdata
;
420 krb5_xfree(saltdata
);
423 case KRB5_KDB_SALTTYPE_NOREALM
:
424 if ((retval
=krb5_principal2salt_norealm(context
, db_entry
->princ
,
428 case KRB5_KDB_SALTTYPE_NORMAL
:
429 if ((retval
= krb5_principal2salt(context
, db_entry
->princ
,
433 case KRB5_KDB_SALTTYPE_V4
:
434 key_salt
.data
.length
= 0;
435 key_salt
.data
.data
= 0;
437 case KRB5_KDB_SALTTYPE_AFS3
: {
439 krb5_data
* saltdata
;
440 if (retval
= krb5_copy_data(context
, krb5_princ_realm(context
,
441 db_entry
->princ
), &saltdata
))
444 key_salt
.data
= *saltdata
;
445 key_salt
.data
.length
= SALT_TYPE_AFS_LENGTH
; /*length actually used below...*/
446 krb5_xfree(saltdata
);
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.... */
451 (*krb5_princ_realm(context
,db_entry
->princ
)).length
;
452 if(!(key_salt
.data
.data
= (char *) malloc(slen
+1)))
454 key_salt
.data
.data
[slen
] = 0;
455 memcpy((char *)key_salt
.data
.data
,
456 (char *)(*krb5_princ_realm(context
,db_entry
->princ
)).data
,
458 key_salt
.data
.length
= SALT_TYPE_AFS_LENGTH
; /*length actually used below...*/
464 return(KRB5_KDB_BAD_SALTTYPE
);
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
);
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
);
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;
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
;
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
] );
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.
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
;
553 krb5_boolean keepold
;
554 krb5_db_entry
* db_entry
;
558 krb5_key_data
* key_data
;
559 krb5_error_code retval
;
563 /* First save the old keydata */
564 old_kvno
= get_key_data_kvno(context
, db_entry
->n_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
);
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
);
587 cleanup_key_data(context
, db_entry
->n_key_data
,
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
);
596 cleanup_key_data(context
, key_data_count
, key_data
);
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.
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
;
614 krb5_db_entry
* db_entry
;
617 krb5_key_data
* key_data
;
618 krb5_error_code retval
;
619 int old_kvno
, new_kvno
;
622 /* First save the old keydata */
623 old_kvno
= get_key_data_kvno(context
, db_entry
->n_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
;
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
,
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
);