kdb_cpw.c revision 4960:a4746a82a247
1/*
2 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5#pragma ident	"%Z%%M%	%I%	%E% SMI"
6
7/*
8 * lib/kdb/kdb_cpw.c
9 *
10 * Copyright 1995 by the Massachusetts Institute of Technology.
11 * All Rights Reserved.
12 *
13 * Export of this software from the United States of America may
14 *   require a specific license from the United States Government.
15 *   It is the responsibility of any person or organization contemplating
16 *   export to obtain such a license before exporting.
17 *
18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19 * distribute this software and its documentation for any purpose and
20 * without fee is hereby granted, provided that the above copyright
21 * notice appear in all copies and that both that copyright notice and
22 * this permission notice appear in supporting documentation, and that
23 * the name of M.I.T. not be used in advertising or publicity pertaining
24 * to distribution of the software without specific, written prior
25 * permission.  Furthermore if you modify this software you must label
26 * your software as modified software and not distribute it in such a
27 * fashion that it might be confused with the original M.I.T. software.
28 * M.I.T. makes no representations about the suitability of
29 * this software for any purpose.  It is provided "as is" without express
30 * or implied warranty.
31 *
32 */
33
34/*
35 * Copyright (C) 1998 by the FundsXpress, INC.
36 *
37 * All rights reserved.
38 *
39 * Export of this software from the United States of America may require
40 * a specific license from the United States Government.  It is the
41 * responsibility of any person or organization contemplating export to
42 * obtain such a license before exporting.
43 *
44 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
45 * distribute this software and its documentation for any purpose and
46 * without fee is hereby granted, provided that the above copyright
47 * notice appear in all copies and that both that copyright notice and
48 * this permission notice appear in supporting documentation, and that
49 * the name of FundsXpress. not be used in advertising or publicity pertaining
50 * to distribution of the software without specific, written prior
51 * permission.  FundsXpress makes no representations about the suitability of
52 * this software for any purpose.  It is provided "as is" without express
53 * or implied warranty.
54 *
55 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
56 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
57 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
58 */
59
60#include "k5-int.h"
61#include <kdb.h>
62#include <stdio.h>
63#include <errno.h>
64
65static int
66get_key_data_kvno(context, count, data)
67    krb5_context	  context;
68    int			  count;
69    krb5_key_data	* data;
70{
71    int i, kvno;
72    /* Find last key version number */
73    for (kvno = i = 0; i < count; i++) {
74	if (kvno < data[i].key_data_kvno) {
75	    kvno = data[i].key_data_kvno;
76	}
77    }
78    return(kvno);
79}
80
81static void
82cleanup_key_data(context, count, data)
83    krb5_context	  context;
84    int			  count;
85    krb5_key_data	* data;
86{
87    int i, j;
88
89    /* If data is NULL, count is always 0 */
90    if (data == NULL) return;
91
92    for (i = 0; i < count; i++) {
93	for (j = 0; j < data[i].key_data_ver; j++) {
94	    if (data[i].key_data_length[j]) {
95	    	free(data[i].key_data_contents[j]);
96	    }
97	}
98    }
99    free(data);
100}
101
102static krb5_error_code
103add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno)
104    krb5_context	  context;
105    krb5_keyblock       * master_key;
106    krb5_key_salt_tuple	* ks_tuple;
107    int			  ks_tuple_count;
108    krb5_db_entry	* db_entry;
109    int			  kvno;
110{
111    krb5_principal	  krbtgt_princ;
112    krb5_keyblock	  key;
113    krb5_db_entry	  krbtgt_entry;
114    krb5_boolean	  more;
115    int			  max_kvno, one, i, j;
116    krb5_error_code	  retval;
117
118    retval = krb5_build_principal_ext(context, &krbtgt_princ,
119				      db_entry->princ->realm.length,
120				      db_entry->princ->realm.data,
121				      KRB5_TGS_NAME_SIZE,
122				      KRB5_TGS_NAME,
123				      db_entry->princ->realm.length,
124				      db_entry->princ->realm.data,
125				      0);
126    if (retval)
127	return retval;
128
129    /* Get tgt from database */
130    retval = krb5_db_get_principal(context, krbtgt_princ, &krbtgt_entry,
131				   &one, &more);
132    krb5_free_principal(context, krbtgt_princ); /* don't need it anymore */
133    if (retval)
134	return(retval);
135    if ((one > 1) || (more)) {
136	krb5_db_free_principal(context, &krbtgt_entry, one);
137	return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
138    }
139    if (!one)
140	return KRB5_KDB_NOENTRY;
141
142    /* Get max kvno */
143    for (max_kvno = j = 0; j < krbtgt_entry.n_key_data; j++) {
144	 if (max_kvno < krbtgt_entry.key_data[j].key_data_kvno) {
145	     max_kvno = krbtgt_entry.key_data[j].key_data_kvno;
146	}
147    }
148
149    for (i = 0; i < ks_tuple_count; i++) {
150	krb5_boolean similar;
151
152	similar = 0;
153
154	/*
155	 * We could use krb5_keysalt_iterate to replace this loop, or use
156	 * krb5_keysalt_is_present for the loop below, but we want to avoid
157	 * circular library dependencies.
158	 */
159	for (j = 0; j < i; j++) {
160	    if ((retval = krb5_c_enctype_compare(context,
161						 ks_tuple[i].ks_enctype,
162						 ks_tuple[j].ks_enctype,
163						 &similar)))
164		return(retval);
165
166	    if (similar)
167		break;
168	}
169
170	if (similar)
171	    continue;
172
173        if ((retval = krb5_dbe_create_key_data(context, db_entry)))
174	    goto add_key_rnd_err;
175
176	/* there used to be code here to extract the old key, and derive
177	   a new key from it.  Now that there's a unified prng, that isn't
178	   necessary. */
179
180	/* make new key */
181	if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,
182					     &key)))
183	    goto add_key_rnd_err;
184
185    	retval = krb5_dbekd_encrypt_key_data(context, master_key,
186					     &key, NULL, kvno,
187					     &db_entry->key_data[db_entry->n_key_data-1]);
188
189	krb5_free_keyblock_contents(context, &key);
190
191	if (retval)
192	    goto add_key_rnd_err;
193    }
194
195add_key_rnd_err:
196    krb5_db_free_principal(context, &krbtgt_entry, one);
197
198    return(retval);
199}
200
201/*
202 * Change random key for a krb5_db_entry
203 * Assumes the max kvno
204 *
205 * As a side effect all old keys are nuked if keepold is false.
206 */
207krb5_error_code
208krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry)
209    krb5_context	  context;
210    krb5_keyblock       * master_key;
211    krb5_key_salt_tuple	* ks_tuple;
212    int			  ks_tuple_count;
213    krb5_boolean	  keepold;
214    krb5_db_entry	* db_entry;
215{
216    int 		  key_data_count;
217    int			  n_new_key_data;
218    krb5_key_data 	* key_data;
219    krb5_error_code	  retval;
220    int			  kvno;
221    int			  i;
222
223    /* First save the old keydata */
224    kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
225    key_data_count = db_entry->n_key_data;
226    key_data = db_entry->key_data;
227    db_entry->key_data = NULL;
228    db_entry->n_key_data = 0;
229
230    /* increment the kvno */
231    kvno++;
232
233    retval = add_key_rnd(context, master_key, ks_tuple,
234			 ks_tuple_count, db_entry, kvno);
235    if (retval) {
236	cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
237	db_entry->n_key_data = key_data_count;
238	db_entry->key_data = key_data;
239    } else if (keepold) {
240	n_new_key_data = db_entry->n_key_data;
241	for (i = 0; i < key_data_count; i++) {
242	    retval = krb5_dbe_create_key_data(context, db_entry);
243	    if (retval) {
244		cleanup_key_data(context, db_entry->n_key_data,
245				 db_entry->key_data);
246		break;
247	    }
248	    db_entry->key_data[i+n_new_key_data] = key_data[i];
249	    memset(&key_data[i], 0, sizeof(krb5_key_data));
250	}
251    } else {
252	cleanup_key_data(context, key_data_count, key_data);
253    }
254    return(retval);
255}
256
257/*
258 * Add random key for a krb5_db_entry
259 * Assumes the max kvno
260 *
261 * As a side effect all old keys older than the max kvno are nuked.
262 */
263krb5_error_code
264krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry)
265    krb5_context	  context;
266    krb5_keyblock       * master_key;
267    krb5_key_salt_tuple	* ks_tuple;
268    int			  ks_tuple_count;
269    krb5_db_entry	* db_entry;
270{
271    int 		  key_data_count;
272    krb5_key_data 	* key_data;
273    krb5_error_code	  retval;
274    int			  kvno;
275    int			  i;
276
277    /* First save the old keydata */
278    kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
279    key_data_count = db_entry->n_key_data;
280    key_data = db_entry->key_data;
281    db_entry->key_data = NULL;
282    db_entry->n_key_data = 0;
283
284    /* increment the kvno */
285    kvno++;
286
287    if ((retval = add_key_rnd(context, master_key, ks_tuple,
288			     ks_tuple_count, db_entry, kvno))) {
289	cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
290	db_entry->n_key_data = key_data_count;
291	db_entry->key_data = key_data;
292    } else {
293	/* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */
294	for (i = 0; i < key_data_count; i++) {
295	    if (key_data[i].key_data_kvno == (kvno - 1)) {
296		if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
297		    cleanup_key_data(context, db_entry->n_key_data,
298				     db_entry->key_data);
299		    break;
300		}
301		/* We should decrypt/re-encrypt the data to use the same mkvno*/
302		db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
303		memset(&key_data[i], 0, sizeof(krb5_key_data));
304	    }
305	}
306	cleanup_key_data(context, key_data_count, key_data);
307    }
308    return(retval);
309}
310
311/*
312 * Add key_data for a krb5_db_entry
313 * If passwd is NULL the assumes that the caller wants a random password.
314 */
315static krb5_error_code
316add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd,
317	    db_entry, kvno)
318    krb5_context	  context;
319    krb5_keyblock       * master_key;
320    krb5_key_salt_tuple	* ks_tuple;
321    int			  ks_tuple_count;
322    char 		* passwd;
323    krb5_db_entry	* db_entry;
324    int			  kvno;
325{
326    krb5_error_code	  retval;
327    krb5_keysalt	  key_salt;
328    krb5_keyblock	  key;
329    krb5_data	  	  pwd;
330    int			  i, j;
331
332    retval = 0;
333
334    for (i = 0; i < ks_tuple_count; i++) {
335	krb5_boolean similar;
336
337	similar = 0;
338
339	/*
340	 * We could use krb5_keysalt_iterate to replace this loop, or use
341	 * krb5_keysalt_is_present for the loop below, but we want to avoid
342	 * circular library dependencies.
343	 */
344	for (j = 0; j < i; j++) {
345	    if ((retval = krb5_c_enctype_compare(context,
346						 ks_tuple[i].ks_enctype,
347						 ks_tuple[j].ks_enctype,
348						 &similar)))
349		return(retval);
350
351	    if (similar &&
352		(ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype))
353		break;
354	}
355
356	if (j < i)
357	    continue;
358
359	if ((retval = krb5_dbe_create_key_data(context, db_entry)))
360	    return(retval);
361
362	/* Convert password string to key using appropriate salt */
363	switch (key_salt.type = ks_tuple[i].ks_salttype) {
364    	case KRB5_KDB_SALTTYPE_ONLYREALM: {
365            krb5_data * saltdata;
366            if ((retval = krb5_copy_data(context, krb5_princ_realm(context,
367					      db_entry->princ), &saltdata)))
368	 	return(retval);
369
370	    key_salt.data = *saltdata;
371	    krb5_xfree(saltdata);
372	}
373		break;
374    	case KRB5_KDB_SALTTYPE_NOREALM:
375            if ((retval=krb5_principal2salt_norealm(context, db_entry->princ,
376						    &key_salt.data)))
377		return(retval);
378            break;
379	case KRB5_KDB_SALTTYPE_NORMAL:
380            if ((retval = krb5_principal2salt(context, db_entry->princ,
381					      &key_salt.data)))
382		return(retval);
383            break;
384    	case KRB5_KDB_SALTTYPE_V4:
385            key_salt.data.length = 0;
386            key_salt.data.data = 0;
387            break;
388    	case KRB5_KDB_SALTTYPE_AFS3: {
389#if 0
390            krb5_data * saltdata;
391            if (retval = krb5_copy_data(context, krb5_princ_realm(context,
392					db_entry->princ), &saltdata))
393	 	return(retval);
394
395	    key_salt.data = *saltdata;
396	    key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/
397	    krb5_xfree(saltdata);
398#else
399	    /* Why do we do this? Well, the afs_mit_string_to_key needs to
400	       use strlen, and the realm is not NULL terminated.... */
401	    unsigned int slen =
402		(*krb5_princ_realm(context,db_entry->princ)).length;
403	    if(!(key_salt.data.data = (char *) malloc(slen+1)))
404	        return ENOMEM;
405	    key_salt.data.data[slen] = 0;
406	    memcpy((char *)key_salt.data.data,
407		   (char *)(*krb5_princ_realm(context,db_entry->princ)).data,
408		   slen);
409	    key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/
410#endif
411
412	}
413		break;
414	default:
415	    return(KRB5_KDB_BAD_SALTTYPE);
416	}
417
418    	pwd.data = passwd;
419    	pwd.length = strlen(passwd);
420
421	memset(&key, 0, sizeof (krb5_keyblock));
422
423	if ((retval = krb5_c_string_to_key(context, ks_tuple[i].ks_enctype,
424					   &pwd, &key_salt.data, &key))) {
425	     if (key_salt.data.data)
426		  free(key_salt.data.data);
427	     return(retval);
428	}
429
430	if (key_salt.data.length == SALT_TYPE_AFS_LENGTH)
431	    key_salt.data.length =
432	      krb5_princ_realm(context, db_entry->princ)->length;
433
434	if ((retval = krb5_dbekd_encrypt_key_data(context, master_key, &key,
435		     (const krb5_keysalt *)&key_salt,
436		     kvno, &db_entry->key_data[db_entry->n_key_data-1]))) {
437	    if (key_salt.data.data)
438		 free(key_salt.data.data);
439
440	    krb5_free_keyblock_contents(context, &key);
441	    return(retval);
442	}
443	if (key_salt.data.data)
444	     free(key_salt.data.data);
445
446	krb5_free_keyblock_contents(context, &key);
447    }
448    return(retval);
449}
450
451/*
452 * Change password for a krb5_db_entry
453 * Assumes the max kvno
454 *
455 * As a side effect all old keys are nuked if keepold is false.
456 */
457krb5_error_code
458krb5_dbe_def_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
459	     new_kvno, keepold, db_entry)
460    krb5_context	  context;
461    krb5_keyblock       * master_key;
462    krb5_key_salt_tuple	* ks_tuple;
463    int			  ks_tuple_count;
464    char 		* passwd;
465    int			  new_kvno;
466    krb5_boolean	  keepold;
467    krb5_db_entry	* db_entry;
468{
469    int 		  key_data_count;
470    int			  n_new_key_data;
471    krb5_key_data 	* key_data;
472    krb5_error_code	  retval;
473    int			  old_kvno;
474    int			  i;
475
476    /* First save the old keydata */
477    old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
478				 db_entry->key_data);
479    key_data_count = db_entry->n_key_data;
480    key_data = db_entry->key_data;
481    db_entry->key_data = NULL;
482    db_entry->n_key_data = 0;
483
484    /* increment the kvno.  if the requested kvno is too small,
485       increment the old kvno */
486    if (new_kvno < old_kvno+1)
487       new_kvno = old_kvno+1;
488
489    retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
490			 passwd, db_entry, new_kvno);
491    if (retval) {
492	cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
493	db_entry->n_key_data = key_data_count;
494	db_entry->key_data = key_data;
495    } else if (keepold) {
496	n_new_key_data = db_entry->n_key_data;
497	for (i = 0; i < key_data_count; i++) {
498	    retval = krb5_dbe_create_key_data(context, db_entry);
499	    if (retval) {
500		cleanup_key_data(context, db_entry->n_key_data,
501				 db_entry->key_data);
502		break;
503	    }
504	    db_entry->key_data[i+n_new_key_data] = key_data[i];
505	    memset(&key_data[i], 0, sizeof(krb5_key_data));
506	}
507	krb5_db_free( context, key_data );
508    } else {
509	cleanup_key_data(context, key_data_count, key_data);
510    }
511    return(retval);
512}
513
514/*
515 * Add password for a krb5_db_entry
516 * Assumes the max kvno
517 *
518 * As a side effect all old keys older than the max kvno are nuked.
519 */
520krb5_error_code
521krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
522    krb5_context	  context;
523    krb5_keyblock       * master_key;
524    krb5_key_salt_tuple	* ks_tuple;
525    int			  ks_tuple_count;
526    char 		* passwd;
527    krb5_db_entry	* db_entry;
528{
529    int 		  key_data_count;
530    krb5_key_data 	* key_data;
531    krb5_error_code	  retval;
532    int			  old_kvno, new_kvno;
533    int			  i;
534
535    /* First save the old keydata */
536    old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
537				 db_entry->key_data);
538    key_data_count = db_entry->n_key_data;
539    key_data = db_entry->key_data;
540    db_entry->key_data = NULL;
541    db_entry->n_key_data = 0;
542
543    /* increment the kvno */
544    new_kvno = old_kvno+1;
545
546    if ((retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
547			     passwd, db_entry, new_kvno))) {
548	cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
549	db_entry->n_key_data = key_data_count;
550	db_entry->key_data = key_data;
551    } else {
552	/* Copy keys with key_data_kvno == old_kvno */
553	for (i = 0; i < key_data_count; i++) {
554	    if (key_data[i].key_data_kvno == old_kvno) {
555		if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
556		    cleanup_key_data(context, db_entry->n_key_data,
557				     db_entry->key_data);
558		    break;
559		}
560		/* We should decrypt/re-encrypt the data to use the same mkvno*/
561		db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
562		memset(&key_data[i], 0, sizeof(krb5_key_data));
563	    }
564	}
565	cleanup_key_data(context, key_data_count, key_data);
566    }
567    return(retval);
568}
569