xref: /illumos-gate/usr/src/lib/krb5/kdb/kdb_cpw.c (revision 54925bf6)
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 
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     /* 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 
102 static krb5_error_code
103 add_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 
195 add_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  */
207 krb5_error_code
208 krb5_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  */
263 krb5_error_code
264 krb5_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  */
315 static krb5_error_code
316 add_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  */
457 krb5_error_code
458 krb5_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  */
520 krb5_error_code
521 krb5_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