Home | History | Annotate | Download | only in kdb
      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
     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
     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
    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
    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
    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
    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
    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
    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