1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /*
7 * lib/kdb/kdb_cpw.c
8 *
9 * Copyright 1995 by the Massachusetts Institute of Technology.
10 * All Rights Reserved.
11 *
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.
16 *
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.
30 *
31 */
32
33 /*
34 * Copyright (C) 1998 by the FundsXpress, INC.
35 *
36 * All rights reserved.
37 *
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.
42 *
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.
53 *
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.
57 */
58
59 #include "k5-int.h"
60 #include "kdb.h"
61 #include <stdio.h>
62 #include <errno.h>
63
64 static int
get_key_data_kvno(context,count,data)65 get_key_data_kvno(context, count, data)
66 krb5_context context;
67 int count;
68 krb5_key_data * data;
69 {
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;
75 }
76 }
77 return(kvno);
78 }
79
80 static void
cleanup_key_data(context,count,data)81 cleanup_key_data(context, count, data)
82 krb5_context context;
83 int count;
84 krb5_key_data * data;
85 {
86 int i, j;
87
88 /* If data is NULL, count is always 0 */
89 if (data == NULL) return;
90
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]);
95 }
96 }
97 }
98 krb5_db_free(context, data);
99 }
100
101 static krb5_error_code
add_key_rnd(context,master_key,ks_tuple,ks_tuple_count,db_entry,kvno)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;
109 {
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;
118
119 memset( &tmp_key_data, 0, sizeof(tmp_key_data));
120
121
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,
129 0);
130 if (retval)
131 return retval;
132
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;
142 }
143 if (!one)
144 return KRB5_KDB_NOENTRY;
145
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;
150 }
151 }
152
153 for (i = 0; i < ks_tuple_count; i++) {
154 krb5_boolean similar;
155
156 similar = 0;
157
158 /*
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.
162 */
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);
169
170 if (similar)
171 break;
172 }
173
174 if (similar)
175 continue;
176
177 if ((retval = krb5_dbe_create_key_data(context, db_entry)))
178 goto add_key_rnd_err;
179
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. */
183
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;
188
189
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);
196
197 krb5_free_keyblock_contents(context, &key);
198 if( retval )
199 goto add_key_rnd_err;
200
201 tptr = &db_entry->key_data[db_entry->n_key_data-1];
202
203 tptr->key_data_ver = tmp_key_data.key_data_ver;
204 tptr->key_data_kvno = tmp_key_data.key_data_kvno;
205
206 for( k = 0; k < tmp_key_data.key_data_ver; k++ )
207 {
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] )
211 {
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 )
214 {
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;
220 }
221 memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
222
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;
226 }
227 }
228
229 }
230
231 add_key_rnd_err:
232 krb5_db_free_principal(context, &krbtgt_entry, one);
233
234 for( i = 0; i < tmp_key_data.key_data_ver; i++ )
235 {
236 if( tmp_key_data.key_data_contents[i] )
237 {
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] );
240 }
241 }
242 return(retval);
243 }
244
245 /*
246 * Change random key for a krb5_db_entry
247 * Assumes the max kvno
248 *
249 * As a side effect all old keys are nuked if keepold is false.
250 */
251 krb5_error_code
krb5_dbe_crk(context,master_key,ks_tuple,ks_tuple_count,keepold,db_entry)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;
259 {
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;
266
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;
273
274 /* increment the kvno */
275 kvno++;
276
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;
291 }
292 db_entry->key_data[i+n_new_key_data] = key_data[i];
293 memset(&key_data[i], 0, sizeof(krb5_key_data));
294 }
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);
298 }
299 return(retval);
300 }
301
302 /*
303 * Add random key for a krb5_db_entry
304 * Assumes the max kvno
305 *
306 * As a side effect all old keys older than the max kvno are nuked.
307 */
308 krb5_error_code
krb5_dbe_ark(context,master_key,ks_tuple,ks_tuple_count,db_entry)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;
315 {
316 int key_data_count;
317 krb5_key_data * key_data;
318 krb5_error_code retval;
319 int kvno;
320 int i;
321
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;
328
329 /* increment the kvno */
330 kvno++;
331
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;
345 }
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));
349 }
350 }
351 cleanup_key_data(context, key_data_count, key_data);
352 }
353 return(retval);
354 }
355
356 /*
357 * Add key_data for a krb5_db_entry
358 * If passwd is NULL the assumes that the caller wants a random password.
359 */
360 static krb5_error_code
add_key_pwd(context,master_key,ks_tuple,ks_tuple_count,passwd,db_entry,kvno)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;
370 {
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;
378
379 memset( &tmp_key_data, 0, sizeof(tmp_key_data));
380
381 retval = 0;
382
383 for (i = 0; i < ks_tuple_count; i++) {
384 krb5_boolean similar;
385
386 similar = 0;
387
388 /*
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.
392 */
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);
399
400 if (similar &&
401 (ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype))
402 break;
403 }
404
405 if (j < i)
406 continue;
407
408 if ((retval = krb5_dbe_create_key_data(context, db_entry)))
409 return(retval);
410
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);
418
419 key_salt.data = *saltdata;
420 krb5_xfree(saltdata);
421 }
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);
443
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
460
461 }
462 break;
463 default:
464 return(KRB5_KDB_BAD_SALTTYPE);
465 }
466
467 pwd.data = passwd;
468 pwd.length = strlen(passwd);
469
470 /* Solaris Kerberos */
471 memset(&key, 0, sizeof (krb5_keyblock));
472
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 if (key_salt.data.data)
477 free(key_salt.data.data);
478 return(retval);
479 }
480
481 if (key_salt.data.length == SALT_TYPE_AFS_LENGTH)
482 key_salt.data.length =
483 krb5_princ_realm(context, db_entry->princ)->length;
484
485 /* memory allocation to be done by db. So, use temporary block and later copy
486 it to the memory allocated by db */
487 retval = krb5_dbekd_encrypt_key_data(context, master_key, &key,
488 (const krb5_keysalt *)&key_salt,
489 kvno, &tmp_key_data);
490 if (key_salt.data.data)
491 free(key_salt.data.data);
492
493 /* Solaris Kerberos */
494 krb5_free_keyblock_contents(context, &key);
495
496 if( retval )
497 return retval;
498
499 tptr = &db_entry->key_data[db_entry->n_key_data-1];
500
501 tptr->key_data_ver = tmp_key_data.key_data_ver;
502 tptr->key_data_kvno = tmp_key_data.key_data_kvno;
503
504 for( k = 0; k < tmp_key_data.key_data_ver; k++ )
505 {
506 tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
507 tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
508 if( tmp_key_data.key_data_contents[k] )
509 {
510 tptr->key_data_contents[k] = krb5_db_alloc(context, NULL, tmp_key_data.key_data_length[k]);
511 if( tptr->key_data_contents[k] == NULL )
512 {
513 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
514 db_entry->key_data = NULL;
515 db_entry->n_key_data = 0;
516 retval = ENOMEM;
517 goto add_key_pwd_err;
518 }
519 memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
520
521 memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
522 free( tmp_key_data.key_data_contents[k] );
523 tmp_key_data.key_data_contents[k] = NULL;
524 }
525 }
526 }
527 add_key_pwd_err:
528 for( i = 0; i < tmp_key_data.key_data_ver; i++ )
529 {
530 if( tmp_key_data.key_data_contents[i] )
531 {
532 memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
533 free( tmp_key_data.key_data_contents[i] );
534 }
535 }
536
537 return(retval);
538 }
539
540 /*
541 * Change password for a krb5_db_entry
542 * Assumes the max kvno
543 *
544 * As a side effect all old keys are nuked if keepold is false.
545 */
546 krb5_error_code
krb5_dbe_def_cpw(context,master_key,ks_tuple,ks_tuple_count,passwd,new_kvno,keepold,db_entry)547 krb5_dbe_def_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
548 new_kvno, keepold, db_entry)
549 krb5_context context;
550 krb5_keyblock * master_key;
551 krb5_key_salt_tuple * ks_tuple;
552 int ks_tuple_count;
553 char * passwd;
554 int new_kvno;
555 krb5_boolean keepold;
556 krb5_db_entry * db_entry;
557 {
558 int key_data_count;
559 int n_new_key_data;
560 krb5_key_data * key_data;
561 krb5_error_code retval;
562 int old_kvno;
563 int i;
564
565 /* First save the old keydata */
566 old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
567 db_entry->key_data);
568 key_data_count = db_entry->n_key_data;
569 key_data = db_entry->key_data;
570 db_entry->key_data = NULL;
571 db_entry->n_key_data = 0;
572
573 /* increment the kvno. if the requested kvno is too small,
574 increment the old kvno */
575 if (new_kvno < old_kvno+1)
576 new_kvno = old_kvno+1;
577
578 retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
579 passwd, db_entry, new_kvno);
580 if (retval) {
581 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
582 db_entry->n_key_data = key_data_count;
583 db_entry->key_data = key_data;
584 } else if (keepold) {
585 n_new_key_data = db_entry->n_key_data;
586 for (i = 0; i < key_data_count; i++) {
587 retval = krb5_dbe_create_key_data(context, db_entry);
588 if (retval) {
589 cleanup_key_data(context, db_entry->n_key_data,
590 db_entry->key_data);
591 break;
592 }
593 db_entry->key_data[i+n_new_key_data] = key_data[i];
594 memset(&key_data[i], 0, sizeof(krb5_key_data));
595 }
596 krb5_db_free( context, key_data );
597 } else {
598 cleanup_key_data(context, key_data_count, key_data);
599 }
600 return(retval);
601 }
602
603 /*
604 * Add password for a krb5_db_entry
605 * Assumes the max kvno
606 *
607 * As a side effect all old keys older than the max kvno are nuked.
608 */
609 krb5_error_code
krb5_dbe_apw(context,master_key,ks_tuple,ks_tuple_count,passwd,db_entry)610 krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
611 krb5_context context;
612 krb5_keyblock * master_key;
613 krb5_key_salt_tuple * ks_tuple;
614 int ks_tuple_count;
615 char * passwd;
616 krb5_db_entry * db_entry;
617 {
618 int key_data_count;
619 krb5_key_data * key_data;
620 krb5_error_code retval;
621 int old_kvno, new_kvno;
622 int i;
623
624 /* First save the old keydata */
625 old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
626 db_entry->key_data);
627 key_data_count = db_entry->n_key_data;
628 key_data = db_entry->key_data;
629 db_entry->key_data = NULL;
630 db_entry->n_key_data = 0;
631
632 /* increment the kvno */
633 new_kvno = old_kvno+1;
634
635 if ((retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
636 passwd, db_entry, new_kvno))) {
637 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
638 db_entry->n_key_data = key_data_count;
639 db_entry->key_data = key_data;
640 } else {
641 /* Copy keys with key_data_kvno == old_kvno */
642 for (i = 0; i < key_data_count; i++) {
643 if (key_data[i].key_data_kvno == old_kvno) {
644 if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
645 cleanup_key_data(context, db_entry->n_key_data,
646 db_entry->key_data);
647 break;
648 }
649 /* We should decrypt/re-encrypt the data to use the same mkvno*/
650 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
651 memset(&key_data[i], 0, sizeof(krb5_key_data));
652 }
653 }
654 cleanup_key_data(context, key_data_count, key_data);
655 }
656 return(retval);
657 }
658
659
660