xref: /illumos-gate/usr/src/lib/krb5/kdb/kdb_cpw.c (revision 7c478bd9)
1 /*
2  * Copyright 2004 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 "krb5/adm.h"
62 #include <stdio.h>
63 #include <errno.h>
64 
65 static int
66 get_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 
81 static void
82 cleanup_key_data(context, count, data)
83     krb5_context	  context;
84     int			  count;
85     krb5_key_data	* data;
86 {
87     int i, j;
88 
89     for (i = 0; i < count; i++) {
90 	for (j = 0; j < data[i].key_data_ver; j++) {
91 	    if (data[i].key_data_length[j]) {
92 	    	free(data[i].key_data_contents[j]);
93 	    }
94 	}
95     }
96     free(data);
97 }
98 
99 static krb5_error_code
100 add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno)
101     krb5_context	  context;
102     krb5_keyblock       * master_key;
103     krb5_key_salt_tuple	* ks_tuple;
104     int			  ks_tuple_count;
105     krb5_db_entry	* db_entry;
106     int			  kvno;
107 {
108     krb5_principal	  krbtgt_princ;
109     krb5_keyblock	  key;
110     krb5_db_entry	  krbtgt_entry;
111     krb5_key_data	* krbtgt_kdata;
112     krb5_boolean	  more;
113     int			  max_kvno, one, i, j;
114     krb5_error_code	  retval;
115 
116     retval = krb5_build_principal_ext(context, &krbtgt_princ,
117 				      db_entry->princ->realm.length,
118 				      db_entry->princ->realm.data,
119 				      KRB5_TGS_NAME_SIZE,
120 				      KRB5_TGS_NAME,
121 				      db_entry->princ->realm.length,
122 				      db_entry->princ->realm.data,
123 				      0);
124     if (retval)
125 	return retval;
126 
127     /* Get tgt from database */
128     retval = krb5_db_get_principal(context, krbtgt_princ, &krbtgt_entry,
129 				   &one, &more);
130     krb5_free_principal(context, krbtgt_princ); /* don't need it anymore */
131     if (retval)
132 	return(retval);
133     if ((one > 1) || (more)) {
134 	krb5_db_free_principal(context, &krbtgt_entry, one);
135 	return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
136     }
137     if (!one)
138 	return KRB5_KDB_NOENTRY;
139 
140     /* Get max kvno */
141     for (max_kvno = j = 0; j < krbtgt_entry.n_key_data; j++) {
142 	 if (max_kvno < krbtgt_entry.key_data[j].key_data_kvno) {
143 	     max_kvno = krbtgt_entry.key_data[j].key_data_kvno;
144 	}
145     }
146 
147     for (i = 0; i < ks_tuple_count; i++) {
148 	krb5_boolean similar;
149 
150 	similar = 0;
151 
152 	/*
153 	 * We could use krb5_keysalt_iterate to replace this loop, or use
154 	 * krb5_keysalt_is_present for the loop below, but we want to avoid
155 	 * circular library dependencies.
156 	 */
157 	for (j = 0; j < i; j++) {
158 	    if ((retval = krb5_c_enctype_compare(context,
159 						 ks_tuple[i].ks_enctype,
160 						 ks_tuple[j].ks_enctype,
161 						 &similar)))
162 		return(retval);
163 
164 	    if (similar)
165 		break;
166 	}
167 
168 	if (similar)
169 	    continue;
170 
171         if (retval = krb5_dbe_create_key_data(context, db_entry))
172 	    goto add_key_rnd_err;
173 
174 	/* there used to be code here to extract the old key, and derive
175 	   a new key from it.  Now that there's a unified prng, that isn't
176 	   necessary. */
177 
178 	/* make new key */
179 	if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,
180 					     &key)))
181 	    goto add_key_rnd_err;
182 
183     	retval = krb5_dbekd_encrypt_key_data(context, master_key,
184 					     &key, NULL, kvno,
185 					     &db_entry->key_data[db_entry->n_key_data-1]);
186 
187 	krb5_free_keyblock_contents(context, &key);
188 
189 	if (retval)
190 	    goto add_key_rnd_err;
191     }
192 
193 add_key_rnd_err:
194     krb5_db_free_principal(context, &krbtgt_entry, one);
195 
196     return(retval);
197 }
198 
199 /*
200  * Change random key for a krb5_db_entry
201  * Assumes the max kvno
202  *
203  * As a side effect all old keys are nuked if keepold is false.
204  */
205 krb5_error_code
206 krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry)
207     krb5_context	  context;
208     krb5_keyblock       * master_key;
209     krb5_key_salt_tuple	* ks_tuple;
210     int			  ks_tuple_count;
211     krb5_boolean	  keepold;
212     krb5_db_entry	* db_entry;
213 {
214     int 		  key_data_count;
215     int			  n_new_key_data;
216     krb5_key_data 	* key_data;
217     krb5_error_code	  retval;
218     int			  kvno;
219     int			  i;
220 
221     /* First save the old keydata */
222     kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
223     key_data_count = db_entry->n_key_data;
224     key_data = db_entry->key_data;
225     db_entry->key_data = NULL;
226     db_entry->n_key_data = 0;
227 
228     /* increment the kvno */
229     kvno++;
230 
231     retval = add_key_rnd(context, master_key, ks_tuple,
232 			 ks_tuple_count, db_entry, kvno);
233     if (retval) {
234 	cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
235 	db_entry->n_key_data = key_data_count;
236 	db_entry->key_data = key_data;
237     } else if (keepold) {
238 	n_new_key_data = db_entry->n_key_data;
239 	for (i = 0; i < key_data_count; i++) {
240 	    retval = krb5_dbe_create_key_data(context, db_entry);
241 	    if (retval) {
242 		cleanup_key_data(context, db_entry->n_key_data,
243 				 db_entry->key_data);
244 		break;
245 	    }
246 	    db_entry->key_data[i+n_new_key_data] = key_data[i];
247 	    memset(&key_data[i], 0, sizeof(krb5_key_data));
248 	}
249     } else {
250 	cleanup_key_data(context, key_data_count, key_data);
251     }
252     return(retval);
253 }
254 
255 /*
256  * Add random key for a krb5_db_entry
257  * Assumes the max kvno
258  *
259  * As a side effect all old keys older than the max kvno are nuked.
260  */
261 krb5_error_code
262 krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry)
263     krb5_context	  context;
264     krb5_keyblock       * master_key;
265     krb5_key_salt_tuple	* ks_tuple;
266     int			  ks_tuple_count;
267     krb5_db_entry	* db_entry;
268 {
269     int 		  key_data_count;
270     krb5_key_data 	* key_data;
271     krb5_error_code	  retval;
272     int			  kvno;
273     int			  i;
274 
275     /* First save the old keydata */
276     kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
277     key_data_count = db_entry->n_key_data;
278     key_data = db_entry->key_data;
279     db_entry->key_data = NULL;
280     db_entry->n_key_data = 0;
281 
282     /* increment the kvno */
283     kvno++;
284 
285     if (retval = add_key_rnd(context, master_key, ks_tuple,
286 			     ks_tuple_count, db_entry, kvno)) {
287 	cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
288 	db_entry->n_key_data = key_data_count;
289 	db_entry->key_data = key_data;
290     } else {
291 	/* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */
292 	for (i = 0; i < key_data_count; i++) {
293 	    if (key_data[i].key_data_kvno == (kvno - 1)) {
294 		if (retval = krb5_dbe_create_key_data(context, db_entry)) {
295 		    cleanup_key_data(context, db_entry->n_key_data,
296 				     db_entry->key_data);
297 		    break;
298 		}
299 		/* We should decrypt/re-encrypt the data to use the same mkvno*/
300 		db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
301 		memset(&key_data[i], 0, sizeof(krb5_key_data));
302 	    }
303 	}
304 	cleanup_key_data(context, key_data_count, key_data);
305     }
306     return(retval);
307 }
308 
309 /*
310  * Add key_data for a krb5_db_entry
311  * If passwd is NULL the assumes that the caller wants a random password.
312  */
313 static krb5_error_code
314 add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd,
315 	    db_entry, kvno)
316     krb5_context	  context;
317     krb5_keyblock       * master_key;
318     krb5_key_salt_tuple	* ks_tuple;
319     int			  ks_tuple_count;
320     char 		* passwd;
321     krb5_db_entry	* db_entry;
322     int			  kvno;
323 {
324     krb5_error_code	  retval;
325     krb5_keysalt	  key_salt;
326     krb5_keyblock	  key;
327     krb5_data	  	  pwd;
328     krb5_boolean	  found;
329     int			  i, j;
330 
331     retval = 0;
332     for (i = 0; i < ks_tuple_count; i++) {
333 	krb5_boolean similar;
334 
335 	similar = 0;
336 
337 	/*
338 	 * We could use krb5_keysalt_iterate to replace this loop, or use
339 	 * krb5_keysalt_is_present for the loop below, but we want to avoid
340 	 * circular library dependencies.
341 	 */
342 	for (j = 0; j < i; j++) {
343 	    if ((retval = krb5_c_enctype_compare(context,
344 						 ks_tuple[i].ks_enctype,
345 						 ks_tuple[j].ks_enctype,
346 						 &similar)))
347 		return(retval);
348 
349 	    if (similar &&
350 		(ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype))
351 		break;
352 	}
353 
354 	if (j < i)
355 	    continue;
356 
357 	if (retval = krb5_dbe_create_key_data(context, db_entry))
358 	    return(retval);
359 
360 	/* Convert password string to key using appropriate salt */
361 	switch (key_salt.type = ks_tuple[i].ks_salttype) {
362     	case KRB5_KDB_SALTTYPE_ONLYREALM: {
363             krb5_data * saltdata;
364             if (retval = krb5_copy_data(context, krb5_princ_realm(context,
365 					db_entry->princ), &saltdata))
366 	 	return(retval);
367 
368 	    key_salt.data = *saltdata;
369 	    krb5_xfree(saltdata);
370 	}
371 		break;
372     	case KRB5_KDB_SALTTYPE_NOREALM:
373             if (retval=krb5_principal2salt_norealm(context, db_entry->princ,
374                                                          &key_salt.data))
375 		return(retval);
376             break;
377 	case KRB5_KDB_SALTTYPE_NORMAL:
378             if (retval = krb5_principal2salt(context, db_entry->princ,
379 					         &key_salt.data))
380 		return(retval);
381             break;
382     	case KRB5_KDB_SALTTYPE_V4:
383             key_salt.data.length = 0;
384             key_salt.data.data = 0;
385             break;
386     	case KRB5_KDB_SALTTYPE_AFS3: {
387 #if 0
388             krb5_data * saltdata;
389             if (retval = krb5_copy_data(context, krb5_princ_realm(context,
390 					db_entry->princ), &saltdata))
391 	 	return(retval);
392 
393 	    key_salt.data = *saltdata;
394 	    key_salt.data.length = -1; /*length actually used below...*/
395 	    krb5_xfree(saltdata);
396 #else
397 	    /* Why do we do this? Well, the afs_mit_string_to_key needs to
398 	       use strlen, and the realm is not NULL terminated.... */
399 	    int slen = (*krb5_princ_realm(context,db_entry->princ)).length;
400 	    if(!(key_salt.data.data = (char *) malloc(slen+1)))
401 	        return ENOMEM;
402 	    key_salt.data.data[slen] = 0;
403 	    memcpy((char *)key_salt.data.data,
404 		   (char *)(*krb5_princ_realm(context,db_entry->princ)).data,
405 		   slen);
406 	    key_salt.data.length = -1; /*length actually used below...*/
407 #endif
408 
409 	}
410 		break;
411 	default:
412 	    return(KRB5_KDB_BAD_SALTTYPE);
413 	}
414 
415     	pwd.data = passwd;
416     	pwd.length = strlen(passwd);
417 
418 	memset(&key, 0, sizeof (krb5_keyblock));
419 
420 	if ((retval = krb5_c_string_to_key(context, ks_tuple[i].ks_enctype,
421 					   &pwd, &key_salt.data, &key))) {
422 	     if (key_salt.data.data)
423 		  free(key_salt.data.data);
424 	     return(retval);
425 	}
426 
427 	if (key_salt.data.length == -1)
428 	    key_salt.data.length =
429 	      krb5_princ_realm(context, db_entry->princ)->length;
430 
431 	if (retval = krb5_dbekd_encrypt_key_data(context, master_key, &key,
432 		     (const krb5_keysalt *)&key_salt,
433 		     kvno, &db_entry->key_data[db_entry->n_key_data-1])) {
434 	    if (key_salt.data.data)
435 		 free(key_salt.data.data);
436 
437 	    krb5_free_keyblock_contents(context, &key);
438 	    return(retval);
439 	}
440 	if (key_salt.data.data)
441 	     free(key_salt.data.data);
442 
443 	krb5_free_keyblock_contents(context, &key);
444     }
445     return(retval);
446 }
447 
448 /*
449  * Change password for a krb5_db_entry
450  * Assumes the max kvno
451  *
452  * As a side effect all old keys are nuked if keepold is false.
453  */
454 krb5_error_code
455 krb5_dbe_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
456 	     new_kvno, keepold, db_entry)
457     krb5_context	  context;
458     krb5_keyblock       * master_key;
459     krb5_key_salt_tuple	* ks_tuple;
460     int			  ks_tuple_count;
461     char 		* passwd;
462     int			  new_kvno;
463     krb5_boolean	  keepold;
464     krb5_db_entry	* db_entry;
465 {
466     int 		  key_data_count;
467     int			  n_new_key_data;
468     krb5_key_data 	* key_data;
469     krb5_error_code	  retval;
470     int			  old_kvno;
471     int			  i;
472 
473     /* First save the old keydata */
474     old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
475 				 db_entry->key_data);
476     key_data_count = db_entry->n_key_data;
477     key_data = db_entry->key_data;
478     db_entry->key_data = NULL;
479     db_entry->n_key_data = 0;
480 
481     /* increment the kvno.  if the requested kvno is too small,
482        increment the old kvno */
483     if (new_kvno < old_kvno+1)
484        new_kvno = old_kvno+1;
485 
486     retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
487 			 passwd, db_entry, new_kvno);
488     if (retval) {
489 	cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
490 	db_entry->n_key_data = key_data_count;
491 	db_entry->key_data = key_data;
492     } else if (keepold) {
493 	n_new_key_data = db_entry->n_key_data;
494 	for (i = 0; i < key_data_count; i++) {
495 	    retval = krb5_dbe_create_key_data(context, db_entry);
496 	    if (retval) {
497 		cleanup_key_data(context, db_entry->n_key_data,
498 				 db_entry->key_data);
499 		break;
500 	    }
501 	    db_entry->key_data[i+n_new_key_data] = key_data[i];
502 	    memset(&key_data[i], 0, sizeof(krb5_key_data));
503 	}
504     } else {
505 	cleanup_key_data(context, key_data_count, key_data);
506     }
507     return(retval);
508 }
509 
510 /*
511  * Add password for a krb5_db_entry
512  * Assumes the max kvno
513  *
514  * As a side effect all old keys older than the max kvno are nuked.
515  */
516 krb5_error_code
517 krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
518     krb5_context	  context;
519     krb5_keyblock       * master_key;
520     krb5_key_salt_tuple	* ks_tuple;
521     int			  ks_tuple_count;
522     char 		* passwd;
523     krb5_db_entry	* db_entry;
524 {
525     int 		  key_data_count;
526     krb5_key_data 	* key_data;
527     krb5_error_code	  retval;
528     int			  old_kvno, new_kvno;
529     int			  i;
530 
531     /* First save the old keydata */
532     old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
533 				 db_entry->key_data);
534     key_data_count = db_entry->n_key_data;
535     key_data = db_entry->key_data;
536     db_entry->key_data = NULL;
537     db_entry->n_key_data = 0;
538 
539     /* increment the kvno */
540     new_kvno = old_kvno+1;
541 
542     if (retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
543 			     passwd, db_entry, new_kvno)) {
544 	cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
545 	db_entry->n_key_data = key_data_count;
546 	db_entry->key_data = key_data;
547     } else {
548 	/* Copy keys with key_data_kvno == old_kvno */
549 	for (i = 0; i < key_data_count; i++) {
550 	    if (key_data[i].key_data_kvno == old_kvno) {
551 		if (retval = krb5_dbe_create_key_data(context, db_entry)) {
552 		    cleanup_key_data(context, db_entry->n_key_data,
553 				     db_entry->key_data);
554 		    break;
555 		}
556 		/* We should decrypt/re-encrypt the data to use the same mkvno*/
557 		db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
558 		memset(&key_data[i], 0, sizeof(krb5_key_data));
559 	    }
560 	}
561 	cleanup_key_data(context, key_data_count, key_data);
562     }
563     return(retval);
564 }
565