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
64static int
65get_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
80static void
81cleanup_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
101static krb5_error_code
102add_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
231add_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 */
251krb5_error_code
252krb5_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 */
308krb5_error_code
309krb5_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 */
360static krb5_error_code
361add_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 */
546krb5_error_code
547krb5_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 */
609krb5_error_code
610krb5_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