Home | History | Annotate | Download | only in srv
      1 /*
      2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 
      7 /*
      8  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
      9  *
     10  *	Openvision retains the copyright to derivative works of
     11  *	this source code.  Do *NOT* create a derivative of this
     12  *	source code before consulting with your legal department.
     13  *	Do *NOT* integrate *ANY* of this source code into another
     14  *	product before consulting with your legal department.
     15  *
     16  *	For further information, read the top-level Openvision
     17  *	copyright which is contained in the top-level MIT Kerberos
     18  *	copyright.
     19  *
     20  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
     21  *
     22  */
     23 
     24 
     25 /*
     26  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
     27  *
     28  * $Header$
     29  */
     30 
     31 #if !defined(lint) && !defined(__CODECENTER__)
     32 static char *rcsid = "$Header$";
     33 #endif
     34 
     35 #include	<sys/types.h>
     36 #include	<sys/time.h>
     37 #include	<errno.h>
     38 #include	"server_internal.h"
     39 #include	<kadm5/admin.h>
     40 #include	<kdb.h>
     41 #include	<stdio.h>
     42 #include	<string.h>
     43 #include	<stdarg.h>
     44 #include	<stdlib.h>
     45 #ifdef USE_PASSWORD_SERVER
     46 #include	<sys/wait.h>
     47 #endif
     48 
     49 extern	krb5_principal	    master_princ;
     50 extern	krb5_principal	    hist_princ;
     51 extern	krb5_keyblock	    hist_key;
     52 extern	krb5_db_entry	    master_db;
     53 extern	krb5_db_entry	    hist_db;
     54 extern  krb5_kvno	    hist_kvno;
     55 
     56 static int decrypt_key_data(krb5_context context,
     57 			    krb5_keyblock *, int n_key_data, krb5_key_data *key_data,
     58 			    krb5_keyblock **keyblocks, int *n_keys);
     59 
     60 static krb5_error_code
     61 kadm5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_principal *outprinc)
     62 {
     63     register krb5_principal tempprinc;
     64     register int i, nelems;
     65 
     66     tempprinc = (krb5_principal)krb5_db_alloc(context, NULL, sizeof(krb5_principal_data));
     67 
     68     if (tempprinc == 0)
     69         return ENOMEM;
     70 
     71     memcpy(tempprinc, inprinc, sizeof(krb5_principal_data));
     72 
     73     nelems = (int) krb5_princ_size(context, inprinc);
     74     tempprinc->data = krb5_db_alloc(context, NULL, nelems * sizeof(krb5_data));
     75 
     76     if (tempprinc->data == 0) {
     77 	krb5_db_free(context, (char *)tempprinc);
     78         return ENOMEM;
     79     }
     80 
     81     for (i = 0; i < nelems; i++) {
     82         unsigned int len = krb5_princ_component(context, inprinc, i)->length;
     83         krb5_princ_component(context, tempprinc, i)->length = len;
     84         if (((krb5_princ_component(context, tempprinc, i)->data =
     85               krb5_db_alloc(context, NULL, len)) == 0) && len) {
     86             while (--i >= 0)
     87                 krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data);
     88             krb5_db_free (context, tempprinc->data);
     89             krb5_db_free (context, tempprinc);
     90             return ENOMEM;
     91         }
     92         if (len)
     93             memcpy(krb5_princ_component(context, tempprinc, i)->data,
     94                    krb5_princ_component(context, inprinc, i)->data, len);
     95     }
     96 
     97     tempprinc->realm.data =
     98 	krb5_db_alloc(context, NULL, tempprinc->realm.length = inprinc->realm.length);
     99     if (!tempprinc->realm.data && tempprinc->realm.length) {
    100             for (i = 0; i < nelems; i++)
    101 		krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data);
    102             krb5_db_free(context, tempprinc->data);
    103             krb5_db_free(context, tempprinc);
    104             return ENOMEM;
    105     }
    106     if (tempprinc->realm.length)
    107         memcpy(tempprinc->realm.data, inprinc->realm.data,
    108                inprinc->realm.length);
    109 
    110     *outprinc = tempprinc;
    111     return 0;
    112 }
    113 
    114 static void
    115 kadm5_free_principal(krb5_context context, krb5_principal val)
    116 {
    117     register krb5_int32 i;
    118 
    119     if (!val)
    120         return;
    121 
    122     if (val->data) {
    123         i = krb5_princ_size(context, val);
    124         while(--i >= 0)
    125             krb5_db_free(context, krb5_princ_component(context, val, i)->data);
    126         krb5_db_free(context, val->data);
    127     }
    128     if (val->realm.data)
    129         krb5_db_free(context, val->realm.data);
    130     krb5_db_free(context, val);
    131 }
    132 
    133 /*
    134  * XXX Functions that ought to be in libkrb5.a, but aren't.
    135  */
    136 kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
    137    krb5_context context;
    138    krb5_key_data *from, *to;
    139 {
    140      int i, idx;
    141 
    142      *to = *from;
    143 
    144      idx = (from->key_data_ver == 1 ? 1 : 2);
    145 
    146      for (i = 0; i < idx; i++) {
    147        if ( from->key_data_length[i] ) {
    148 	 to->key_data_contents[i] = malloc(from->key_data_length[i]);
    149 	 if (to->key_data_contents[i] == NULL) {
    150 	   for (i = 0; i < idx; i++) {
    151 	     if (to->key_data_contents[i]) {
    152 	       memset(to->key_data_contents[i], 0,
    153 		      to->key_data_length[i]);
    154 	       free(to->key_data_contents[i]);
    155 	     }
    156 	   }
    157 	   return ENOMEM;
    158 	 }
    159 	 memcpy(to->key_data_contents[i], from->key_data_contents[i],
    160 		from->key_data_length[i]);
    161        }
    162      }
    163      return 0;
    164 }
    165 
    166 static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
    167 {
    168      krb5_tl_data *n;
    169 
    170      n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
    171      if (n == NULL)
    172 	  return NULL;
    173      n->tl_data_contents = malloc(tl->tl_data_length);
    174      if (n->tl_data_contents == NULL) {
    175 	  free(n);
    176 	  return NULL;
    177      }
    178      memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
    179      n->tl_data_type = tl->tl_data_type;
    180      n->tl_data_length = tl->tl_data_length;
    181      n->tl_data_next = NULL;
    182      return n;
    183 }
    184 
    185 /* This is in lib/kdb/kdb_cpw.c, but is static */
    186 static void cleanup_key_data(context, count, data)
    187    krb5_context	  context;
    188    int			  count;
    189    krb5_key_data	* data;
    190 {
    191      int i, j;
    192 
    193      for (i = 0; i < count; i++)
    194 	  for (j = 0; j < data[i].key_data_ver; j++)
    195 	       if (data[i].key_data_length[j])
    196 		   krb5_db_free(context, data[i].key_data_contents[j]);
    197      krb5_db_free(context, data);
    198 }
    199 
    200 kadm5_ret_t
    201 kadm5_create_principal(void *server_handle,
    202 			    kadm5_principal_ent_t entry, long mask,
    203 			    char *password)
    204 {
    205     return
    206 	kadm5_create_principal_3(server_handle, entry, mask,
    207 				 0, NULL, password);
    208 }
    209 kadm5_ret_t
    210 kadm5_create_principal_3(void *server_handle,
    211 			 kadm5_principal_ent_t entry, long mask,
    212 			 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
    213 			 char *password)
    214 {
    215     krb5_db_entry		kdb;
    216     osa_princ_ent_rec		adb;
    217     kadm5_policy_ent_rec	polent;
    218     krb5_int32			now;
    219     krb5_tl_data		*tl_data_orig, *tl_data_tail;
    220     unsigned int		ret;
    221     kadm5_server_handle_t handle = server_handle;
    222 
    223     CHECK_HANDLE(server_handle);
    224 
    225     krb5_clear_error_message(handle->context);
    226 
    227     /*
    228      * Argument sanity checking, and opening up the DB
    229      */
    230     if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
    231        (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
    232        (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
    233        (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
    234        (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
    235        (mask & KADM5_FAIL_AUTH_COUNT))
    236 	return KADM5_BAD_MASK;
    237     if((mask & ~ALL_PRINC_MASK))
    238 	return KADM5_BAD_MASK;
    239     if (entry == (kadm5_principal_ent_t) NULL || password == NULL)
    240 	return EINVAL;
    241 
    242     /*
    243      * Check to see if the principal exists
    244      */
    245     ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
    246 
    247     switch(ret) {
    248     case KADM5_UNK_PRINC:
    249 	/* Solaris Kerberos */
    250 	memset(&kdb, 0, sizeof(krb5_db_entry));
    251 	memset(&adb, 0, sizeof(osa_princ_ent_rec));
    252 	break;
    253     case 0:
    254 	/*
    255 	 * Solaris Kerberos: this allows an addprinc to be done on a mix-in
    256 	 * princ which has no keys initially.
    257 	 */
    258 	if (kdb.n_key_data != 0) {
    259 		/* have a princ with keys, return dupe princ error */
    260 		kdb_free_entry(handle, &kdb, &adb);
    261 		return KADM5_DUP;
    262 	} else {
    263 		/*
    264 		 * have a princ with no keys, let's replace it.  Note, want to
    265 		 * keep the existing kdb tl_data (specifically the LDAP plugin
    266 		 * adds the DN to the tl_data which is needed to locate the dir.
    267 		 * entry).
    268 		 */
    269 		kdb_free_entry(handle, NULL, &adb);
    270 		memset(&adb, 0, sizeof(osa_princ_ent_rec));
    271 	}
    272 	break;
    273     default:
    274 	return ret;
    275     }
    276 
    277     /*
    278      * If a policy was specified, load it.
    279      * If we can not find the one specified return an error
    280      */
    281     if ((mask & KADM5_POLICY)) {
    282 	 if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
    283 				     &polent)) != KADM5_OK) {
    284 	    if(ret == EINVAL)
    285 		return KADM5_BAD_POLICY;
    286 	    else
    287 		return ret;
    288 	}
    289     }
    290     if ((ret = passwd_check(handle, password, (mask & KADM5_POLICY),
    291 			    &polent, entry->principal))) {
    292 	if (mask & KADM5_POLICY)
    293 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
    294 	return ret;
    295     }
    296     /*
    297      * Start populating the various DB fields, using the
    298      * "defaults" for fields that were not specified by the
    299      * mask.
    300      */
    301     if ((ret = krb5_timeofday(handle->context, &now))) {
    302 	 if (mask & KADM5_POLICY)
    303 	      (void) kadm5_free_policy_ent(handle->lhandle, &polent);
    304 	 return ret;
    305     }
    306 
    307     kdb.magic = KRB5_KDB_MAGIC_NUMBER;
    308     kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
    309 
    310     /*
    311      * Solaris Kerberos:
    312      * If KADM5_ATTRIBUTES is set, we want to rope in not only
    313      * entry->attributes, but also the generic params.flags
    314      * obtained previously via kadm5_get_config_params.
    315      */
    316     if ((mask & KADM5_ATTRIBUTES)) {
    317 	kdb.attributes = handle->params.flags;
    318 	kdb.attributes |= entry->attributes;
    319     } else {
    320 	kdb.attributes = handle->params.flags;
    321     }
    322 
    323     if ((mask & KADM5_MAX_LIFE))
    324 	kdb.max_life = entry->max_life;
    325     else
    326 	kdb.max_life = handle->params.max_life;
    327 
    328     if (mask & KADM5_MAX_RLIFE)
    329 	 kdb.max_renewable_life = entry->max_renewable_life;
    330     else
    331 	 kdb.max_renewable_life = handle->params.max_rlife;
    332 
    333     if ((mask & KADM5_PRINC_EXPIRE_TIME))
    334 	kdb.expiration = entry->princ_expire_time;
    335     else
    336 	kdb.expiration = handle->params.expiration;
    337 
    338     kdb.pw_expiration = 0;
    339     if ((mask & KADM5_POLICY)) {
    340 	if(polent.pw_max_life)
    341 	    kdb.pw_expiration = now + polent.pw_max_life;
    342 	else
    343 	    kdb.pw_expiration = 0;
    344     }
    345     if ((mask & KADM5_PW_EXPIRATION))
    346 	 kdb.pw_expiration = entry->pw_expiration;
    347 
    348     kdb.last_success = 0;
    349     kdb.last_failed = 0;
    350     kdb.fail_auth_count = 0;
    351 
    352     /* this is kind of gross, but in order to free the tl data, I need
    353        to free the entire kdb entry, and that will try to free the
    354        principal. */
    355 
    356     if ((ret = kadm5_copy_principal(handle->context,
    357 				    entry->principal, &(kdb.princ)))) {
    358 	if (mask & KADM5_POLICY)
    359 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
    360 	return(ret);
    361     }
    362 
    363     if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))) {
    364          krb5_db_free_principal(handle->context, &kdb, 1);
    365 	 if (mask & KADM5_POLICY)
    366 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
    367 	 return(ret);
    368     }
    369 
    370     if (mask & KADM5_TL_DATA) {
    371 	/* splice entry->tl_data onto the front of kdb.tl_data */
    372 	tl_data_orig = kdb.tl_data;
    373 	for (tl_data_tail = entry->tl_data; tl_data_tail;
    374 	     tl_data_tail = tl_data_tail->tl_data_next)
    375 	{
    376 	    ret = krb5_dbe_update_tl_data(handle->context, &kdb, tl_data_tail);
    377 	    if( ret )
    378 	    {
    379 		krb5_db_free_principal(handle->context, &kdb, 1);
    380 		if (mask & KADM5_POLICY)
    381 		    (void) kadm5_free_policy_ent(handle->lhandle, &polent);
    382 		return ret;
    383 	    }
    384 	}
    385     }
    386 
    387     /* initialize the keys */
    388 
    389     if ((ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock,
    390 			    n_ks_tuple?ks_tuple:handle->params.keysalts,
    391 			    n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
    392 			    password,
    393 			    (mask & KADM5_KVNO)?entry->kvno:1,
    394 			    FALSE, &kdb))) {
    395 	krb5_db_free_principal(handle->context, &kdb, 1);
    396 	if (mask & KADM5_POLICY)
    397 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
    398 	return(ret);
    399     }
    400 
    401     /* populate the admin-server-specific fields.  In the OV server,
    402        this used to be in a separate database.  Since there's already
    403        marshalling code for the admin fields, to keep things simple,
    404        I'm going to keep it, and make all the admin stuff occupy a
    405        single tl_data record, */
    406 
    407     adb.admin_history_kvno = hist_kvno;
    408     if ((mask & KADM5_POLICY)) {
    409 	adb.aux_attributes = KADM5_POLICY;
    410 
    411 	/* this does *not* need to be strdup'ed, because adb is xdr */
    412 	/* encoded in osa_adb_create_princ, and not ever freed */
    413 
    414 	adb.policy = entry->policy;
    415     }
    416 
    417     /* increment the policy ref count, if any */
    418 
    419     if ((mask & KADM5_POLICY)) {
    420 	polent.policy_refcnt++;
    421 	if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
    422 						    KADM5_REF_COUNT))
    423 	    != KADM5_OK) {
    424 	    krb5_db_free_principal(handle->context, &kdb, 1);
    425 	    if (mask & KADM5_POLICY)
    426 		 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
    427 	    return(ret);
    428 	}
    429     }
    430 
    431     /* In all cases key and the principal data is set, let the database provider know */
    432     kdb.mask = mask | KADM5_KEY_DATA | KADM5_PRINCIPAL ;
    433 
    434     /* store the new db entry */
    435     ret = kdb_put_entry(handle, &kdb, &adb);
    436 
    437     krb5_db_free_principal(handle->context, &kdb, 1);
    438 
    439     if (ret) {
    440 	if ((mask & KADM5_POLICY)) {
    441 	    /* decrement the policy ref count */
    442 
    443 	    polent.policy_refcnt--;
    444 	    /*
    445 	     * if this fails, there's nothing we can do anyway.  the
    446 	     * policy refcount wil be too high.
    447 	     */
    448 	    (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
    449 						     KADM5_REF_COUNT);
    450 	}
    451 
    452 	if (mask & KADM5_POLICY)
    453 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
    454 	return(ret);
    455     }
    456 
    457     if (mask & KADM5_POLICY)
    458 	 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
    459 
    460     return KADM5_OK;
    461 }
    462 
    463 
    464 kadm5_ret_t
    465 kadm5_delete_principal(void *server_handle, krb5_principal principal)
    466 {
    467     unsigned int		ret;
    468     kadm5_policy_ent_rec	polent;
    469     krb5_db_entry		kdb;
    470     osa_princ_ent_rec		adb;
    471     kadm5_server_handle_t handle = server_handle;
    472 
    473     CHECK_HANDLE(server_handle);
    474 
    475     krb5_clear_error_message(handle->context);
    476 
    477     if (principal == NULL)
    478 	return EINVAL;
    479 
    480     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
    481 	return(ret);
    482 
    483     if ((adb.aux_attributes & KADM5_POLICY)) {
    484 	if ((ret = kadm5_get_policy(handle->lhandle,
    485 				    adb.policy, &polent))
    486 	    == KADM5_OK) {
    487 	    polent.policy_refcnt--;
    488 	    if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
    489 							 KADM5_REF_COUNT))
    490 		!= KADM5_OK) {
    491 		(void) kadm5_free_policy_ent(handle->lhandle, &polent);
    492 		kdb_free_entry(handle, &kdb, &adb);
    493 		return(ret);
    494 	    }
    495 	}
    496 	if ((ret = kadm5_free_policy_ent(handle->lhandle, &polent))) {
    497 	     kdb_free_entry(handle, &kdb, &adb);
    498 	     return ret;
    499 	}
    500     }
    501 
    502     ret = kdb_delete_entry(handle, principal);
    503 
    504     kdb_free_entry(handle, &kdb, &adb);
    505 
    506     return ret;
    507 }
    508 
    509 kadm5_ret_t
    510 kadm5_modify_principal(void *server_handle,
    511 			    kadm5_principal_ent_t entry, long mask)
    512 {
    513     int			    ret, ret2, i;
    514     kadm5_policy_ent_rec    npol, opol;
    515     int			    have_npol = 0, have_opol = 0;
    516     krb5_db_entry	    kdb;
    517     krb5_tl_data	    *tl_data_orig;
    518     osa_princ_ent_rec	    adb;
    519     kadm5_server_handle_t handle = server_handle;
    520 
    521     CHECK_HANDLE(server_handle);
    522 
    523     krb5_clear_error_message(handle->context);
    524 
    525     if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
    526        (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
    527        (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
    528        (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
    529        (mask & KADM5_LAST_FAILED))
    530 	return KADM5_BAD_MASK;
    531     if((mask & ~ALL_PRINC_MASK))
    532 	return KADM5_BAD_MASK;
    533     if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
    534 	return KADM5_BAD_MASK;
    535     if(entry == (kadm5_principal_ent_t) NULL)
    536 	return EINVAL;
    537     if (mask & KADM5_TL_DATA) {
    538 	 tl_data_orig = entry->tl_data;
    539 	 while (tl_data_orig) {
    540 	      if (tl_data_orig->tl_data_type < 256)
    541 		   return KADM5_BAD_TL_TYPE;
    542 	      tl_data_orig = tl_data_orig->tl_data_next;
    543 	 }
    544     }
    545 
    546     ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
    547     if (ret)
    548 	return(ret);
    549 
    550     /*
    551      * This is pretty much the same as create ...
    552      */
    553 
    554     if ((mask & KADM5_POLICY)) {
    555 	 /* get the new policy */
    556 	 ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
    557 	 if (ret) {
    558 	      switch (ret) {
    559 	      case EINVAL:
    560 		   ret = KADM5_BAD_POLICY;
    561 		   break;
    562 	      case KADM5_UNK_POLICY:
    563 	      case KADM5_BAD_POLICY:
    564 		   ret =  KADM5_UNK_POLICY;
    565 		   break;
    566 	      }
    567 	      goto done;
    568 	 }
    569 	 have_npol = 1;
    570 
    571 	 /* if we already have a policy, get it to decrement the refcnt */
    572 	 if(adb.aux_attributes & KADM5_POLICY) {
    573 	      /* ... but not if the old and new are the same */
    574 	      if(strcmp(adb.policy, entry->policy)) {
    575 		   ret = kadm5_get_policy(handle->lhandle,
    576 					  adb.policy, &opol);
    577 		   switch(ret) {
    578 		   case EINVAL:
    579 		   case KADM5_BAD_POLICY:
    580 		   case KADM5_UNK_POLICY:
    581 			break;
    582 		   case KADM5_OK:
    583 			have_opol = 1;
    584 			opol.policy_refcnt--;
    585 			break;
    586 		   default:
    587 			goto done;
    588 			break;
    589 		   }
    590 		   npol.policy_refcnt++;
    591 	      }
    592 	 } else npol.policy_refcnt++;
    593 
    594 	 /* set us up to use the new policy */
    595 	 adb.aux_attributes |= KADM5_POLICY;
    596 	 if (adb.policy)
    597 	      free(adb.policy);
    598 	 adb.policy = strdup(entry->policy);
    599 
    600 	 /* set pw_max_life based on new policy */
    601 	 if (npol.pw_max_life) {
    602 	     ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
    603 						   &(kdb.pw_expiration));
    604 	     if (ret)
    605 		 goto done;
    606 	     kdb.pw_expiration += npol.pw_max_life;
    607 	 } else {
    608 	     kdb.pw_expiration = 0;
    609 	 }
    610     }
    611 
    612     if ((mask & KADM5_POLICY_CLR) &&
    613 	(adb.aux_attributes & KADM5_POLICY)) {
    614 	 ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
    615 	 switch(ret) {
    616 	 case EINVAL:
    617 	 case KADM5_BAD_POLICY:
    618 	 case KADM5_UNK_POLICY:
    619 	      ret = KADM5_BAD_DB;
    620 	      goto done;
    621 	      break;
    622 	 case KADM5_OK:
    623 	      have_opol = 1;
    624 	      if (adb.policy)
    625 		   free(adb.policy);
    626 	      adb.policy = NULL;
    627 	      adb.aux_attributes &= ~KADM5_POLICY;
    628 	      kdb.pw_expiration = 0;
    629 	      opol.policy_refcnt--;
    630 	      break;
    631 	 default:
    632 	      goto done;
    633 	      break;
    634 	 }
    635     }
    636 
    637     if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) &&
    638 	(((have_opol) &&
    639 	  (ret =
    640 	   kadm5_modify_policy_internal(handle->lhandle, &opol,
    641 					     KADM5_REF_COUNT))) ||
    642 	 ((have_npol) &&
    643 	  (ret =
    644 	   kadm5_modify_policy_internal(handle->lhandle, &npol,
    645 					     KADM5_REF_COUNT)))))
    646 	goto done;
    647 
    648     if ((mask & KADM5_ATTRIBUTES))
    649 	kdb.attributes = entry->attributes;
    650     if ((mask & KADM5_MAX_LIFE))
    651 	kdb.max_life = entry->max_life;
    652     if ((mask & KADM5_PRINC_EXPIRE_TIME))
    653 	kdb.expiration = entry->princ_expire_time;
    654     if (mask & KADM5_PW_EXPIRATION)
    655 	 kdb.pw_expiration = entry->pw_expiration;
    656     if (mask & KADM5_MAX_RLIFE)
    657 	 kdb.max_renewable_life = entry->max_renewable_life;
    658     if (mask & KADM5_FAIL_AUTH_COUNT)
    659 	 kdb.fail_auth_count = entry->fail_auth_count;
    660 
    661     if((mask & KADM5_KVNO)) {
    662 	 for (i = 0; i < kdb.n_key_data; i++)
    663 	      kdb.key_data[i].key_data_kvno = entry->kvno;
    664     }
    665 
    666     if (mask & KADM5_TL_DATA) {
    667 	 krb5_tl_data *tl;
    668 
    669 	 /* may have to change the version number of the API. Updates the list with the given tl_data rather than over-writting */
    670 
    671 	 for (tl = entry->tl_data; tl;
    672 	      tl = tl->tl_data_next)
    673 	 {
    674 	     ret = krb5_dbe_update_tl_data(handle->context, &kdb, tl);
    675 	     if( ret )
    676 	     {
    677 		 goto done;
    678 	     }
    679 	 }
    680     }
    681 
    682     /* let the mask propagate to the database provider */
    683     kdb.mask = mask;
    684 
    685     ret = kdb_put_entry(handle, &kdb, &adb);
    686     if (ret) goto done;
    687 
    688     ret = KADM5_OK;
    689 done:
    690     if (have_opol) {
    691 	 ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
    692 	 ret = ret ? ret : ret2;
    693     }
    694     if (have_npol) {
    695 	 ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
    696 	 ret = ret ? ret : ret2;
    697     }
    698     kdb_free_entry(handle, &kdb, &adb);
    699     return ret;
    700 }
    701 
    702 kadm5_ret_t
    703 kadm5_rename_principal(void *server_handle,
    704 			    krb5_principal source, krb5_principal target)
    705 {
    706     krb5_db_entry	kdb;
    707     osa_princ_ent_rec	adb;
    708     int			ret, i;
    709     kadm5_server_handle_t handle = server_handle;
    710 
    711     CHECK_HANDLE(server_handle);
    712 
    713     krb5_clear_error_message(handle->context);
    714 
    715     if (source == NULL || target == NULL)
    716 	return EINVAL;
    717 
    718     if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
    719 	kdb_free_entry(handle, &kdb, &adb);
    720 	return(KADM5_DUP);
    721     }
    722 
    723     if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
    724 	return ret;
    725 
    726     /* this is kinda gross, but unavoidable */
    727 
    728     for (i=0; i<kdb.n_key_data; i++) {
    729 	if ((kdb.key_data[i].key_data_ver == 1) ||
    730 	    (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
    731 	    ret = KADM5_NO_RENAME_SALT;
    732 	    goto done;
    733 	}
    734     }
    735 
    736     kadm5_free_principal(handle->context, kdb.princ);
    737     ret = kadm5_copy_principal(handle->context, target, &kdb.princ);
    738     if (ret) {
    739 	kdb.princ = NULL; /* so freeing the dbe doesn't lose */
    740 	goto done;
    741     }
    742 
    743     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
    744 	goto done;
    745 
    746     ret = kdb_delete_entry(handle, source);
    747 
    748 done:
    749     kdb_free_entry(handle, &kdb, &adb);
    750     return ret;
    751 }
    752 
    753 kadm5_ret_t
    754 kadm5_get_principal(void *server_handle, krb5_principal principal,
    755 		    kadm5_principal_ent_t entry,
    756 		    long in_mask)
    757 {
    758     krb5_db_entry		kdb;
    759     osa_princ_ent_rec		adb;
    760     krb5_error_code		ret = 0;
    761     long			mask;
    762     int i;
    763     kadm5_server_handle_t handle = server_handle;
    764     kadm5_principal_ent_rec	entry_local, *entry_orig;
    765 
    766     CHECK_HANDLE(server_handle);
    767 
    768     krb5_clear_error_message(handle->context);
    769 
    770     /*
    771      * In version 1, all the defined fields are always returned.
    772      * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
    773      * filled with allocated memory.
    774      */
    775     if (handle->api_version == KADM5_API_VERSION_1) {
    776 	 mask = KADM5_PRINCIPAL_NORMAL_MASK;
    777 	 entry_orig = entry;
    778 	 entry = &entry_local;
    779     } else {
    780 	 mask = in_mask;
    781     }
    782 
    783     memset((char *) entry, 0, sizeof(*entry));
    784 
    785     if (principal == NULL)
    786 	return EINVAL;
    787 
    788     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
    789 	return ret;
    790 
    791     if ((mask & KADM5_POLICY) &&
    792 	adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
    793 	if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) {
    794 	    ret = ENOMEM;
    795 	    goto done;
    796 	}
    797 	strcpy(entry->policy, adb.policy);
    798     }
    799 
    800     if (mask & KADM5_AUX_ATTRIBUTES)
    801 	 entry->aux_attributes = adb.aux_attributes;
    802 
    803     if ((mask & KADM5_PRINCIPAL) &&
    804 	(ret = krb5_copy_principal(handle->context, principal,
    805 				   &entry->principal))) {
    806 	goto done;
    807     }
    808 
    809     if (mask & KADM5_PRINC_EXPIRE_TIME)
    810 	 entry->princ_expire_time = kdb.expiration;
    811 
    812     if ((mask & KADM5_LAST_PWD_CHANGE) &&
    813 	(ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
    814 					       &(entry->last_pwd_change)))) {
    815 	goto done;
    816     }
    817 
    818     if (mask & KADM5_PW_EXPIRATION)
    819 	 entry->pw_expiration = kdb.pw_expiration;
    820     if (mask & KADM5_MAX_LIFE)
    821 	 entry->max_life = kdb.max_life;
    822 
    823     /* this is a little non-sensical because the function returns two */
    824     /* values that must be checked separately against the mask */
    825     if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
    826 	ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
    827 					     &(entry->mod_date),
    828 					     &(entry->mod_name));
    829 	if (ret) {
    830 	    goto done;
    831 	}
    832 
    833 	if (! (mask & KADM5_MOD_TIME))
    834 	    entry->mod_date = 0;
    835 	if (! (mask & KADM5_MOD_NAME)) {
    836 	    krb5_free_principal(handle->context, entry->principal);
    837 	    entry->principal = NULL;
    838 	}
    839     }
    840 
    841     if (mask & KADM5_ATTRIBUTES)
    842 	 entry->attributes = kdb.attributes;
    843 
    844     if (mask & KADM5_KVNO)
    845 	 for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
    846 	      if (kdb.key_data[i].key_data_kvno > entry->kvno)
    847 		   entry->kvno = kdb.key_data[i].key_data_kvno;
    848 
    849     if (handle->api_version == KADM5_API_VERSION_2)
    850 	 entry->mkvno = 0;
    851     else {
    852 	 /* XXX I'll be damned if I know how to deal with this one --marc */
    853 	 entry->mkvno = 1;
    854     }
    855 
    856     /*
    857      * The new fields that only exist in version 2 start here
    858      */
    859     if (handle->api_version == KADM5_API_VERSION_2) {
    860 	 if (mask & KADM5_MAX_RLIFE)
    861 	      entry->max_renewable_life = kdb.max_renewable_life;
    862 	 if (mask & KADM5_LAST_SUCCESS)
    863 	      entry->last_success = kdb.last_success;
    864 	 if (mask & KADM5_LAST_FAILED)
    865 	      entry->last_failed = kdb.last_failed;
    866 	 if (mask & KADM5_FAIL_AUTH_COUNT)
    867 	      entry->fail_auth_count = kdb.fail_auth_count;
    868 	 if (mask & KADM5_TL_DATA) {
    869 	      krb5_tl_data *tl, *tl2;
    870 
    871 	      entry->tl_data = NULL;
    872 
    873 	      tl = kdb.tl_data;
    874 	      while (tl) {
    875 		   if (tl->tl_data_type > 255) {
    876 			if ((tl2 = dup_tl_data(tl)) == NULL) {
    877 			     ret = ENOMEM;
    878 			     goto done;
    879 			}
    880 			tl2->tl_data_next = entry->tl_data;
    881 			entry->tl_data = tl2;
    882 			entry->n_tl_data++;
    883 		   }
    884 
    885 		   tl = tl->tl_data_next;
    886 	      }
    887 	 }
    888 	 if (mask & KADM5_KEY_DATA) {
    889 	      entry->n_key_data = kdb.n_key_data;
    890 	      if(entry->n_key_data) {
    891 		      entry->key_data = (krb5_key_data *)
    892 			      malloc(entry->n_key_data*sizeof(krb5_key_data));
    893 		      if (entry->key_data == NULL) {
    894 			      ret = ENOMEM;
    895 			      goto done;
    896 		      }
    897 	      } else
    898 		      entry->key_data = NULL;
    899 
    900 	      for (i = 0; i < entry->n_key_data; i++)
    901 		  ret = krb5_copy_key_data_contents(handle->context,
    902 						    &kdb.key_data[i],
    903 						    &entry->key_data[i]);
    904 		   if (ret)
    905 			goto done;
    906 	 }
    907     }
    908 
    909     /*
    910      * If KADM5_API_VERSION_1, we return an allocated structure, and
    911      * we need to convert the new structure back into the format the
    912      * caller is expecting.
    913      */
    914     if (handle->api_version == KADM5_API_VERSION_1) {
    915 	 kadm5_principal_ent_t_v1 newv1;
    916 
    917 	 newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
    918 	 if (newv1 == NULL) {
    919 	      ret = ENOMEM;
    920 	      goto done;
    921 	 }
    922 
    923 	 newv1->principal = entry->principal;
    924 	 newv1->princ_expire_time = entry->princ_expire_time;
    925 	 newv1->last_pwd_change = entry->last_pwd_change;
    926 	 newv1->pw_expiration = entry->pw_expiration;
    927 	 newv1->max_life = entry->max_life;
    928 	 newv1->mod_name = entry->mod_name;
    929 	 newv1->mod_date = entry->mod_date;
    930 	 newv1->attributes = entry->attributes;
    931 	 newv1->kvno = entry->kvno;
    932 	 newv1->mkvno = entry->mkvno;
    933 	 newv1->policy = entry->policy;
    934 	 newv1->aux_attributes = entry->aux_attributes;
    935 
    936 	 *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
    937     }
    938 
    939     ret = KADM5_OK;
    940 
    941 done:
    942     if (ret && entry->principal)
    943 	 krb5_free_principal(handle->context, entry->principal);
    944     kdb_free_entry(handle, &kdb, &adb);
    945 
    946     return ret;
    947 }
    948 
    949 /*
    950  * Function: check_pw_reuse
    951  *
    952  * Purpose: Check if a key appears in a list of keys, in order to
    953  * enforce password history.
    954  *
    955  * Arguments:
    956  *
    957  *	context			(r) the krb5 context
    958  *	hist_keyblock		(r) the key that hist_key_data is
    959  *				encrypted in
    960  *	n_new_key_data		(r) length of new_key_data
    961  *	new_key_data		(r) keys to check against
    962  *				pw_hist_data, encrypted in hist_keyblock
    963  *	n_pw_hist_data		(r) length of pw_hist_data
    964  *	pw_hist_data		(r) passwords to check new_key_data against
    965  *
    966  * Effects:
    967  * For each new_key in new_key_data:
    968  * 	decrypt new_key with the master_keyblock
    969  * 	for each password in pw_hist_data:
    970  *		for each hist_key in password:
    971  *			decrypt hist_key with hist_keyblock
    972  *			compare the new_key and hist_key
    973  *
    974  * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
    975  * new_key_data is the same as a key in pw_hist_data, or 0.
    976  */
    977 static kadm5_ret_t
    978 check_pw_reuse(krb5_context context,
    979 	       krb5_keyblock *master_keyblock,
    980 	       krb5_keyblock *hist_keyblock,
    981 	       int n_new_key_data, krb5_key_data *new_key_data,
    982 	       unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
    983 {
    984     int x, y, z;
    985     krb5_keyblock newkey, histkey;
    986     krb5_error_code ret;
    987 
    988     for (x = 0; x < n_new_key_data; x++) {
    989 	ret = krb5_dbekd_decrypt_key_data(context,
    990 					  master_keyblock,
    991 					  &(new_key_data[x]),
    992 					  &newkey, NULL);
    993 	if (ret)
    994 	    return(ret);
    995 	for (y = 0; y < n_pw_hist_data; y++) {
    996 	     for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
    997 		 ret = krb5_dbekd_decrypt_key_data(context,
    998 						   hist_keyblock,
    999 						   &pw_hist_data[y].key_data[z],
   1000 						   &histkey, NULL);
   1001 		 if (ret)
   1002 		     return(ret);
   1003 
   1004 		 if ((newkey.length == histkey.length) &&
   1005 		     (newkey.enctype == histkey.enctype) &&
   1006 		     (memcmp(newkey.contents, histkey.contents,
   1007 			     histkey.length) == 0)) {
   1008 		     krb5_free_keyblock_contents(context, &histkey);
   1009 		     krb5_free_keyblock_contents(context, &newkey);
   1010 
   1011 		     return(KADM5_PASS_REUSE);
   1012 		 }
   1013 		 krb5_free_keyblock_contents(context, &histkey);
   1014 	     }
   1015 	}
   1016 	krb5_free_keyblock_contents(context, &newkey);
   1017     }
   1018 
   1019     return(0);
   1020 }
   1021 
   1022 /*
   1023  * Function: create_history_entry
   1024  *
   1025  * Purpose: Creates a password history entry from an array of
   1026  * key_data.
   1027  *
   1028  * Arguments:
   1029  *
   1030  *	context		(r) krb5_context to use
   1031  *      master_keyblcok (r) master key block
   1032  *	n_key_data	(r) number of elements in key_data
   1033  *	key_data	(r) keys to add to the history entry
   1034  *	hist		(w) history entry to fill in
   1035  *
   1036  * Effects:
   1037  *
   1038  * hist->key_data is allocated to store n_key_data key_datas.  Each
   1039  * element of key_data is decrypted with master_keyblock, re-encrypted
   1040  * in hist_key, and added to hist->key_data.  hist->n_key_data is
   1041  * set to n_key_data.
   1042  */
   1043 static
   1044 int create_history_entry(krb5_context context,
   1045 	krb5_keyblock *master_keyblock,	int n_key_data,
   1046 	krb5_key_data *key_data, osa_pw_hist_ent *hist)
   1047 {
   1048      int i, ret;
   1049      krb5_keyblock key;
   1050      krb5_keysalt salt;
   1051 
   1052      hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
   1053      if (hist->key_data == NULL)
   1054 	  return ENOMEM;
   1055      memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
   1056 
   1057      for (i = 0; i < n_key_data; i++) {
   1058 	 ret = krb5_dbekd_decrypt_key_data(context,
   1059 					   master_keyblock,
   1060 					   &key_data[i],
   1061 					   &key, &salt);
   1062 	 if (ret)
   1063 	     return ret;
   1064 
   1065 	 ret = krb5_dbekd_encrypt_key_data(context, &hist_key,
   1066 					   &key, &salt,
   1067 					   key_data[i].key_data_kvno,
   1068 					   &hist->key_data[i]);
   1069 	 if (ret)
   1070 	     return ret;
   1071 
   1072 	 krb5_free_keyblock_contents(context, &key);
   1073 	 /* krb5_free_keysalt(context, &salt); */
   1074      }
   1075 
   1076      hist->n_key_data = n_key_data;
   1077      return 0;
   1078 }
   1079 
   1080 static
   1081 void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
   1082 {
   1083      int i;
   1084 
   1085      for (i = 0; i < hist->n_key_data; i++)
   1086 	  krb5_free_key_data_contents(context, &hist->key_data[i]);
   1087      free(hist->key_data);
   1088 }
   1089 
   1090 /*
   1091  * Function: add_to_history
   1092  *
   1093  * Purpose: Adds a password to a principal's password history.
   1094  *
   1095  * Arguments:
   1096  *
   1097  *	context		(r) krb5_context to use
   1098  *	adb		(r/w) admin principal entry to add keys to
   1099  *	pol		(r) adb's policy
   1100  *	pw		(r) keys for the password to add to adb's key history
   1101  *
   1102  * Effects:
   1103  *
   1104  * add_to_history adds a single password to adb's password history.
   1105  * pw contains n_key_data keys in its key_data, in storage should be
   1106  * allocated but not freed by the caller (XXX blech!).
   1107  *
   1108  * This function maintains adb->old_keys as a circular queue.  It
   1109  * starts empty, and grows each time this function is called until it
   1110  * is pol->pw_history_num items long.  adb->old_key_len holds the
   1111  * number of allocated entries in the array, and must therefore be [0,
   1112  * pol->pw_history_num).  adb->old_key_next is the index into the
   1113  * array where the next element should be written, and must be [0,
   1114  * adb->old_key_len).
   1115  */
   1116 #define	KADM_MOD(x)	(x + adb->old_key_next) % adb->old_key_len
   1117 static kadm5_ret_t add_to_history(krb5_context context,
   1118 				  osa_princ_ent_t adb,
   1119 				  kadm5_policy_ent_t pol,
   1120 				  osa_pw_hist_ent *pw)
   1121 {
   1122      osa_pw_hist_ent *histp;
   1123      uint32_t nhist;
   1124      unsigned int i, knext, nkeys;
   1125 
   1126      nhist = pol->pw_history_num;
   1127      /* A history of 1 means just check the current password */
   1128      if (nhist <= 1)
   1129 	  return 0;
   1130 
   1131      nkeys = adb->old_key_len;
   1132      knext = adb->old_key_next;
   1133      /* resize the adb->old_keys array if necessary */
   1134      if (nkeys + 1 < nhist) {
   1135 	  if (adb->old_keys == NULL) {
   1136 	       adb->old_keys = (osa_pw_hist_ent *)
   1137 		    malloc((nkeys + 1) * sizeof (osa_pw_hist_ent));
   1138 	  } else {
   1139 	       adb->old_keys = (osa_pw_hist_ent *)
   1140 		    realloc(adb->old_keys,
   1141 			    (nkeys + 1) * sizeof (osa_pw_hist_ent));
   1142 	  }
   1143 	  if (adb->old_keys == NULL)
   1144 	       return(ENOMEM);
   1145 
   1146 	  memset(&adb->old_keys[nkeys], 0, sizeof(osa_pw_hist_ent));
   1147      	  nkeys = ++adb->old_key_len;
   1148 	  /*
   1149 	   * To avoid losing old keys, shift forward each entry after
   1150 	   * knext.
   1151 	   */
   1152 	  for (i = nkeys - 1; i > knext; i--) {
   1153 	      adb->old_keys[i] = adb->old_keys[i - 1];
   1154 	  }
   1155 	  memset(&adb->old_keys[knext], 0, sizeof(osa_pw_hist_ent));
   1156      } else if (nkeys + 1 > nhist) {
   1157 	 /*
   1158 	  * The policy must have changed!  Shrink the array.
   1159 	  * Can't simply realloc() down, since it might be wrapped.
   1160 	  * To understand the arithmetic below, note that we are
   1161 	  * copying into new positions 0 .. N-1 from old positions
   1162 	  * old_key_next-N .. old_key_next-1, modulo old_key_len,
   1163 	  * where N = pw_history_num - 1 is the length of the
   1164 	  * shortened list.        Matt Crawford, FNAL
   1165 	  */
   1166 	 /*
   1167 	  * M = adb->old_key_len, N = pol->pw_history_num - 1
   1168 	  *
   1169 	  * tmp[0] .. tmp[N-1] = old[(knext-N)%M] .. old[(knext-1)%M]
   1170 	  */
   1171 	 int j;
   1172 	 osa_pw_hist_t tmp;
   1173 
   1174 	 tmp = (osa_pw_hist_ent *)
   1175 	     malloc((nhist - 1) * sizeof (osa_pw_hist_ent));
   1176 	 if (tmp == NULL)
   1177 	     return ENOMEM;
   1178 	 for (i = 0; i < nhist - 1; i++) {
   1179 	     /*
   1180 	      * Add nkeys once before taking remainder to avoid
   1181 	      * negative values.
   1182 	      */
   1183 	     j = (i + nkeys + knext - (nhist - 1)) % nkeys;
   1184 	     tmp[i] = adb->old_keys[j];
   1185 	 }
   1186 	 /* Now free the ones we don't keep (the oldest ones) */
   1187 	 for (i = 0; i < nkeys - (nhist - 1); i++) {
   1188 	     j = (i + nkeys + knext) % nkeys;
   1189 	     histp = &adb->old_keys[j];
   1190 	     for (j = 0; j < histp->n_key_data; j++) {
   1191 		 krb5_free_key_data_contents(context, &histp->key_data[j]);
   1192 	     }
   1193 	     free(histp->key_data);
   1194 	 }
   1195 	 free((void *)adb->old_keys);
   1196 	 adb->old_keys = tmp;
   1197 	 nkeys = adb->old_key_len = nhist - 1;
   1198 	 knext = adb->old_key_next = 0;
   1199      }
   1200 
   1201      /*
   1202       * If nhist decreased since the last password change, and nkeys+1
   1203       * is less than the previous nhist, it is possible for knext to
   1204       * index into unallocated space.  This condition would not be
   1205       * caught by the resizing code above.
   1206       */
   1207      if (knext + 1 > nkeys)
   1208 	 knext = adb->old_key_next = 0;
   1209      /* free the old pw history entry if it contains data */
   1210      histp = &adb->old_keys[knext];
   1211      for (i = 0; i < histp->n_key_data; i++)
   1212 	  krb5_free_key_data_contents(context, &histp->key_data[i]);
   1213      free(histp->key_data);
   1214 
   1215      /* store the new entry */
   1216      adb->old_keys[knext] = *pw;
   1217 
   1218      /* update the next pointer */
   1219      if (++adb->old_key_next == nhist - 1)
   1220 	 adb->old_key_next = 0;
   1221 
   1222      return(0);
   1223 }
   1224 #undef KADM_MOD
   1225 
   1226 #ifdef USE_PASSWORD_SERVER
   1227 /* FIXME: don't use global variable for this */
   1228 krb5_boolean use_password_server = 0;
   1229 
   1230 static krb5_boolean
   1231 kadm5_use_password_server (void)
   1232 {
   1233     return use_password_server;
   1234 }
   1235 
   1236 void
   1237 kadm5_set_use_password_server (void)
   1238 {
   1239     use_password_server = 1;
   1240 }
   1241 #endif
   1242 
   1243 #ifdef USE_PASSWORD_SERVER
   1244 
   1245 /*
   1246  * kadm5_launch_task () runs a program (task_path) to synchronize the
   1247  * Apple password server with the Kerberos database.  Password server
   1248  * programs can receive arguments on the command line (task_argv)
   1249  * and a block of data via stdin (data_buffer).
   1250  *
   1251  * Because a failure to communicate with the tool results in the
   1252  * password server falling out of sync with the database,
   1253  * kadm5_launch_task() always fails if it can't talk to the tool.
   1254  */
   1255 
   1256 static kadm5_ret_t
   1257 kadm5_launch_task (krb5_context context,
   1258                    const char *task_path, char * const task_argv[],
   1259                    const char *data_buffer)
   1260 {
   1261     kadm5_ret_t ret = 0;
   1262     int data_pipe[2];
   1263 
   1264     if (data_buffer != NULL) {
   1265         ret = pipe (data_pipe);
   1266         if (ret) { ret = errno; }
   1267     }
   1268 
   1269     if (!ret) {
   1270         pid_t pid = fork ();
   1271         if (pid == -1) {
   1272             ret = errno;
   1273         } else if (pid == 0) {
   1274             /* The child: */
   1275 
   1276             if (data_buffer != NULL) {
   1277                 if (dup2 (data_pipe[0], STDIN_FILENO) == -1) {
   1278                     _exit (1);
   1279                 }
   1280             } else {
   1281                 close (data_pipe[0]);
   1282             }
   1283 
   1284             close (data_pipe[1]);
   1285 
   1286             execv (task_path, task_argv);
   1287 
   1288             _exit (1); /* Fail if execv fails */
   1289         } else {
   1290             /* The parent: */
   1291             int status;
   1292 
   1293             if (data_buffer != NULL) {
   1294                 /* Write out the buffer to the child */
   1295                 if (krb5_net_write (context, data_pipe[1],
   1296                                     data_buffer, strlen (data_buffer)) < 0) {
   1297                     /* kill the child to make sure waitpid() won't hang later */
   1298                     ret = errno;
   1299                     kill (pid, SIGKILL);
   1300                 }
   1301             }
   1302 
   1303             close (data_buffer[0]);
   1304             close (data_buffer[1]);
   1305 
   1306             waitpid (pid, &status, 0);
   1307 
   1308             if (!ret) {
   1309                 if (WIFEXITED (status)) {
   1310                     /* child read password and exited.  Check the return value. */
   1311                     if ((WEXITSTATUS (status) != 0) && (WEXITSTATUS (status) != 252)) {
   1312                        ret = KRB5KDC_ERR_POLICY; /* password change rejected */
   1313                     }
   1314                 } else {
   1315                     /* child read password but crashed or was killed */
   1316                     ret = KRB5KRB_ERR_GENERIC; /* FIXME: better error */
   1317                 }
   1318             }
   1319         }
   1320     }
   1321 
   1322     return ret;
   1323 }
   1324 
   1325 #endif
   1326 
   1327 kadm5_ret_t
   1328 kadm5_chpass_principal(void *server_handle,
   1329 			    krb5_principal principal, char *password)
   1330 {
   1331     return
   1332 	kadm5_chpass_principal_3(server_handle, principal, FALSE,
   1333 				 0, NULL, password);
   1334 }
   1335 
   1336 kadm5_ret_t
   1337 kadm5_chpass_principal_3(void *server_handle,
   1338 			 krb5_principal principal, krb5_boolean keepold,
   1339 			 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
   1340 			 char *password)
   1341 {
   1342     krb5_int32			now;
   1343     kadm5_policy_ent_rec	pol;
   1344     osa_princ_ent_rec		adb;
   1345     krb5_db_entry		kdb, kdb_save;
   1346     int				ret, ret2, last_pwd, hist_added;
   1347     int				have_pol = 0;
   1348     kadm5_server_handle_t	handle = server_handle;
   1349     osa_pw_hist_ent		hist;
   1350 
   1351     CHECK_HANDLE(server_handle);
   1352 
   1353     krb5_clear_error_message(handle->context);
   1354 
   1355     hist_added = 0;
   1356     memset(&hist, 0, sizeof(hist));
   1357 
   1358     if (principal == NULL || password == NULL)
   1359 	return EINVAL;
   1360     if ((krb5_principal_compare(handle->context,
   1361 				principal, hist_princ)) == TRUE)
   1362 	return KADM5_PROTECT_PRINCIPAL;
   1363 
   1364     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
   1365        return(ret);
   1366 
   1367     /* we are going to need the current keys after the new keys are set */
   1368     if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
   1369 	 kdb_free_entry(handle, &kdb, &adb);
   1370 	 return(ret);
   1371     }
   1372 
   1373     if ((adb.aux_attributes & KADM5_POLICY)) {
   1374 	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
   1375 	     goto done;
   1376 	have_pol = 1;
   1377     }
   1378 
   1379     if ((ret = passwd_check(handle, password, adb.aux_attributes &
   1380 			    KADM5_POLICY, &pol, principal)))
   1381 	 goto done;
   1382 
   1383     ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock,
   1384 		       n_ks_tuple?ks_tuple:handle->params.keysalts,
   1385 		       n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
   1386 		       password, 0 /* increment kvno */,
   1387 		       keepold, &kdb);
   1388     if (ret)
   1389 	goto done;
   1390 
   1391     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
   1392 
   1393     ret = krb5_timeofday(handle->context, &now);
   1394     if (ret)
   1395 	 goto done;
   1396 
   1397     if ((adb.aux_attributes & KADM5_POLICY)) {
   1398        /* the policy was loaded before */
   1399 
   1400 	ret = krb5_dbe_lookup_last_pwd_change(handle->context,
   1401 					      &kdb, &last_pwd);
   1402 	if (ret)
   1403 	    goto done;
   1404 
   1405 #if 0
   1406 	 /*
   1407 	  * The spec says this check is overridden if the caller has
   1408 	  * modify privilege.  The admin server therefore makes this
   1409 	  * check itself (in chpass_principal_wrapper, misc.c). A
   1410 	  * local caller implicitly has all authorization bits.
   1411 	  */
   1412 	if ((now - last_pwd) < pol.pw_min_life &&
   1413 	    !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
   1414 	     ret = KADM5_PASS_TOOSOON;
   1415 	     goto done;
   1416 	}
   1417 #endif
   1418 
   1419 	ret = create_history_entry(handle->context,
   1420 				   &handle->master_keyblock, kdb_save.n_key_data,
   1421 				   kdb_save.key_data, &hist);
   1422 	if (ret)
   1423 	    goto done;
   1424 
   1425 	ret = check_pw_reuse(handle->context,
   1426 			     &handle->master_keyblock,
   1427 			     &hist_key,
   1428 			     kdb.n_key_data, kdb.key_data,
   1429 			     1, &hist);
   1430 	if (ret)
   1431 	    goto done;
   1432 
   1433 	if (pol.pw_history_num > 1) {
   1434 	    if (adb.admin_history_kvno != hist_kvno) {
   1435 		ret = KADM5_BAD_HIST_KEY;
   1436 		goto done;
   1437 	    }
   1438 
   1439 	    ret = check_pw_reuse(handle->context,
   1440 				&handle->master_keyblock,
   1441 				     &hist_key,
   1442 				 kdb.n_key_data, kdb.key_data,
   1443 				 adb.old_key_len, adb.old_keys);
   1444 	    if (ret)
   1445 		goto done;
   1446 
   1447 	    ret = add_to_history(handle->context, &adb, &pol, &hist);
   1448 	    if (ret)
   1449 		goto done;
   1450 	    hist_added = 1;
   1451        }
   1452 
   1453 	if (pol.pw_max_life)
   1454 	   kdb.pw_expiration = now + pol.pw_max_life;
   1455 	else
   1456 	   kdb.pw_expiration = 0;
   1457     } else {
   1458 	kdb.pw_expiration = 0;
   1459     }
   1460 
   1461 #ifdef USE_PASSWORD_SERVER
   1462     if (kadm5_use_password_server () &&
   1463         (krb5_princ_size (handle->context, principal) == 1)) {
   1464         krb5_data *princ = krb5_princ_component (handle->context, principal, 0);
   1465         const char *path = "/usr/sbin/mkpassdb";
   1466         char *argv[] = { "mkpassdb", "-setpassword", NULL, NULL };
   1467         char *pstring = NULL;
   1468         char pwbuf[256];
   1469         int pwlen = strlen (password);
   1470 
   1471         if (pwlen > 254) pwlen = 254;
   1472         strncpy (pwbuf, password, pwlen);
   1473         pwbuf[pwlen] = '\n';
   1474         pwbuf[pwlen + 1] = '\0';
   1475 
   1476         if (!ret) {
   1477             pstring = malloc ((princ->length + 1) * sizeof (char));
   1478             if (pstring == NULL) { ret = errno; }
   1479         }
   1480 
   1481         if (!ret) {
   1482             memcpy (pstring, princ->data, princ->length);
   1483             pstring [princ->length] = '\0';
   1484             argv[2] = pstring;
   1485 
   1486             ret = kadm5_launch_task (handle->context, path, argv, pwbuf);
   1487         }
   1488 
   1489         if (pstring != NULL)
   1490             free (pstring);
   1491 
   1492         if (ret)
   1493             goto done;
   1494     }
   1495 #endif
   1496 
   1497     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
   1498     if (ret)
   1499 	goto done;
   1500 
   1501     /* key data and attributes changed, let the database provider know */
   1502     /* Solaris Kerberos: adding support for key history in LDAP KDB */
   1503     if (hist_added == 1)
   1504 	kdb.mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_KEY_HIST
   1505 	    /* | KADM5_CPW_FUNCTION */;
   1506     else
   1507 	kdb.mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES /* | KADM5_CPW_FUNCTION */;
   1508 
   1509     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
   1510 	goto done;
   1511 
   1512     ret = KADM5_OK;
   1513 done:
   1514     if (!hist_added && hist.key_data)
   1515 	 free_history_entry(handle->context, &hist);
   1516     kdb_free_entry(handle, &kdb, &adb);
   1517     kdb_free_entry(handle, &kdb_save, NULL);
   1518     krb5_db_free_principal(handle->context, &kdb, 1);
   1519 
   1520     if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
   1521 	&& !ret)
   1522 	 ret = ret2;
   1523 
   1524     return ret;
   1525 }
   1526 
   1527 kadm5_ret_t
   1528 kadm5_randkey_principal(void *server_handle,
   1529 			krb5_principal principal,
   1530 			krb5_keyblock **keyblocks,
   1531 			int *n_keys)
   1532 {
   1533 	 /* Solaris Kerberos: */
   1534 	krb5_key_salt_tuple keysalts[2];
   1535 
   1536 	/*
   1537 	 * Anyone calling this routine is forced to use only DES
   1538 	 * enctypes to be compatible with earlier releases that
   1539 	 * did not support stronger crypto.
   1540 	 *
   1541 	 * S10 (and later) kadmin clients will not use this API,
   1542 	 * so we can assume the request is from an older version.
   1543 	 */
   1544 	keysalts[0].ks_enctype = ENCTYPE_DES_CBC_MD5;
   1545 	keysalts[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
   1546 	keysalts[1].ks_enctype = ENCTYPE_DES_CBC_CRC;
   1547 	keysalts[1].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
   1548 
   1549 	return (kadm5_randkey_principal_3(server_handle, principal,
   1550 			FALSE, 2, keysalts, keyblocks, n_keys));
   1551 }
   1552 kadm5_ret_t
   1553 kadm5_randkey_principal_3(void *server_handle,
   1554 			krb5_principal principal,
   1555 			krb5_boolean keepold,
   1556 			int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
   1557 			krb5_keyblock **keyblocks,
   1558 			int *n_keys)
   1559 {
   1560     krb5_db_entry		kdb;
   1561     osa_princ_ent_rec		adb;
   1562     krb5_int32			now;
   1563     kadm5_policy_ent_rec	pol;
   1564     krb5_key_data		*key_data;
   1565     int				ret, last_pwd, have_pol = 0;
   1566     kadm5_server_handle_t	handle = server_handle;
   1567 
   1568     if (keyblocks)
   1569 	 *keyblocks = NULL;
   1570 
   1571     CHECK_HANDLE(server_handle);
   1572 
   1573     krb5_clear_error_message(handle->context);
   1574 
   1575     if (principal == NULL)
   1576 	return EINVAL;
   1577     if (hist_princ && /* this will be NULL when initializing the databse */
   1578 	((krb5_principal_compare(handle->context,
   1579 				 principal, hist_princ)) == TRUE))
   1580 	return KADM5_PROTECT_PRINCIPAL;
   1581 
   1582     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
   1583        return(ret);
   1584 
   1585     ret = krb5_dbe_crk(handle->context, &handle->master_keyblock,
   1586 		       n_ks_tuple?ks_tuple:handle->params.keysalts,
   1587 		       n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
   1588 		       keepold,
   1589 		       &kdb);
   1590     if (ret)
   1591 	goto done;
   1592 
   1593     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
   1594 
   1595     ret = krb5_timeofday(handle->context, &now);
   1596     if (ret)
   1597 	goto done;
   1598 
   1599     if ((adb.aux_attributes & KADM5_POLICY)) {
   1600 	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
   1601 				    &pol)) != KADM5_OK)
   1602 	   goto done;
   1603 	have_pol = 1;
   1604 
   1605 	ret = krb5_dbe_lookup_last_pwd_change(handle->context,
   1606 					      &kdb, &last_pwd);
   1607 	if (ret)
   1608 	     goto done;
   1609 
   1610 #if 0
   1611 	 /*
   1612 	  * The spec says this check is overridden if the caller has
   1613 	  * modify privilege.  The admin server therefore makes this
   1614 	  * check itself (in chpass_principal_wrapper, misc.c).  A
   1615 	  * local caller implicitly has all authorization bits.
   1616 	  */
   1617 	if((now - last_pwd) < pol.pw_min_life &&
   1618 	   !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
   1619 	     ret = KADM5_PASS_TOOSOON;
   1620 	     goto done;
   1621 	}
   1622 #endif
   1623 
   1624 	if(pol.pw_history_num > 1) {
   1625 	    if(adb.admin_history_kvno != hist_kvno) {
   1626 		ret = KADM5_BAD_HIST_KEY;
   1627 		goto done;
   1628 	    }
   1629 
   1630 	    ret = check_pw_reuse(handle->context,
   1631 				 &handle->master_keyblock,
   1632 				 &hist_key,
   1633 				 kdb.n_key_data, kdb.key_data,
   1634 				 adb.old_key_len, adb.old_keys);
   1635 	    if (ret)
   1636 		goto done;
   1637 	}
   1638 	if (pol.pw_max_life)
   1639 	   kdb.pw_expiration = now + pol.pw_max_life;
   1640 	else
   1641 	   kdb.pw_expiration = 0;
   1642     } else {
   1643 	kdb.pw_expiration = 0;
   1644     }
   1645 
   1646     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
   1647     if (ret)
   1648 	 goto done;
   1649 
   1650     if (keyblocks) {
   1651 	 if (handle->api_version == KADM5_API_VERSION_1) {
   1652 	      /* Version 1 clients will expect to see a DES_CRC enctype. */
   1653 	     ret = krb5_dbe_find_enctype(handle->context, &kdb,
   1654 					 ENCTYPE_DES_CBC_CRC,
   1655 					 -1, -1, &key_data);
   1656 	     if (ret)
   1657 		 goto done;
   1658 
   1659 	     ret = decrypt_key_data(handle->context,
   1660 				&handle->master_keyblock, 1, key_data,
   1661 				     keyblocks, NULL);
   1662 	     if (ret)
   1663 		 goto done;
   1664 	 } else {
   1665 	     ret = decrypt_key_data(handle->context,
   1666 				     &handle->master_keyblock,
   1667 				     kdb.n_key_data, kdb.key_data,
   1668 				     keyblocks, n_keys);
   1669 	     if (ret)
   1670 		 goto done;
   1671 	 }
   1672     }
   1673 
   1674     /* key data changed, let the database provider know */
   1675     kdb.mask = KADM5_KEY_DATA /* | KADM5_RANDKEY_USED */;
   1676 
   1677     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
   1678 	goto done;
   1679 
   1680     ret = KADM5_OK;
   1681 done:
   1682     kdb_free_entry(handle, &kdb, &adb);
   1683     if (have_pol)
   1684 	 kadm5_free_policy_ent(handle->lhandle, &pol);
   1685 
   1686     return ret;
   1687 }
   1688 
   1689 #if 0 /* Solaris Kerberos */
   1690 /*
   1691  * kadm5_setv4key_principal:
   1692  *
   1693  * Set only ONE key of the principal, removing all others.  This key
   1694  * must have the DES_CBC_CRC enctype and is entered as having the
   1695  * krb4 salttype.  This is to enable things like kadmind4 to work.
   1696  */
   1697 kadm5_ret_t
   1698 kadm5_setv4key_principal(void *server_handle,
   1699 		       krb5_principal principal,
   1700 		       krb5_keyblock *keyblock)
   1701 {
   1702     krb5_db_entry		kdb;
   1703     osa_princ_ent_rec		adb;
   1704     krb5_int32			now;
   1705     kadm5_policy_ent_rec	pol;
   1706     krb5_keysalt		keysalt;
   1707     int				i, k, kvno, ret, have_pol = 0;
   1708 #if 0
   1709     int                         last_pwd;
   1710 #endif
   1711     kadm5_server_handle_t	handle = server_handle;
   1712     krb5_key_data               tmp_key_data;
   1713 
   1714     memset( &tmp_key_data, 0, sizeof(tmp_key_data));
   1715 
   1716     CHECK_HANDLE(server_handle);
   1717 
   1718     krb5_clear_error_message(handle->context);
   1719 
   1720     if (principal == NULL || keyblock == NULL)
   1721 	return EINVAL;
   1722     if (hist_princ && /* this will be NULL when initializing the databse */
   1723 	((krb5_principal_compare(handle->context,
   1724 				 principal, hist_princ)) == TRUE))
   1725 	return KADM5_PROTECT_PRINCIPAL;
   1726 
   1727     if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
   1728 	return KADM5_SETV4KEY_INVAL_ENCTYPE;
   1729 
   1730     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
   1731        return(ret);
   1732 
   1733     for (kvno = 0, i=0; i<kdb.n_key_data; i++)
   1734 	 if (kdb.key_data[i].key_data_kvno > kvno)
   1735 	      kvno = kdb.key_data[i].key_data_kvno;
   1736 
   1737     if (kdb.key_data != NULL)
   1738 	 cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
   1739 
   1740     kdb.key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, sizeof(krb5_key_data));
   1741     if (kdb.key_data == NULL)
   1742 	 return ENOMEM;
   1743     memset(kdb.key_data, 0, sizeof(krb5_key_data));
   1744     kdb.n_key_data = 1;
   1745     keysalt.type = KRB5_KDB_SALTTYPE_V4;
   1746     /* XXX data.magic? */
   1747     keysalt.data.length = 0;
   1748     keysalt.data.data = NULL;
   1749 
   1750     /* use tmp_key_data as temporary location and reallocate later */
   1751     ret = krb5_dbekd_encrypt_key_data(handle->context, &master_keyblock,
   1752 				      keyblock, &keysalt, kvno + 1,
   1753 				      &tmp_key_data);
   1754     if (ret) {
   1755 	goto done;
   1756     }
   1757 
   1758     for (k = 0; k < tmp_key_data.key_data_ver; k++) {
   1759 	kdb.key_data->key_data_type[k] = tmp_key_data.key_data_type[k];
   1760 	kdb.key_data->key_data_length[k] = tmp_key_data.key_data_length[k];
   1761 	if (tmp_key_data.key_data_contents[k]) {
   1762 	    kdb.key_data->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
   1763 	    if (kdb.key_data->key_data_contents[k] == NULL) {
   1764 		cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
   1765 		kdb.key_data = NULL;
   1766 		kdb.n_key_data = 0;
   1767 		ret = ENOMEM;
   1768 		goto done;
   1769 	    }
   1770 	    memcpy (kdb.key_data->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
   1771 
   1772 	    memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
   1773 	    free (tmp_key_data.key_data_contents[k]);
   1774 	    tmp_key_data.key_data_contents[k] = NULL;
   1775 	}
   1776     }
   1777 
   1778 
   1779 
   1780     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
   1781 
   1782     ret = krb5_timeofday(handle->context, &now);
   1783     if (ret)
   1784 	goto done;
   1785 
   1786     if ((adb.aux_attributes & KADM5_POLICY)) {
   1787 	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
   1788 				    &pol)) != KADM5_OK)
   1789 	   goto done;
   1790 	have_pol = 1;
   1791 
   1792 #if 0
   1793 	/*
   1794 	  * The spec says this check is overridden if the caller has
   1795 	  * modify privilege.  The admin server therefore makes this
   1796 	  * check itself (in chpass_principal_wrapper, misc.c).  A
   1797 	  * local caller implicitly has all authorization bits.
   1798 	  */
   1799 	if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
   1800 						  &kdb, &last_pwd))
   1801 	     goto done;
   1802 	if((now - last_pwd) < pol.pw_min_life &&
   1803 	   !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
   1804 	     ret = KADM5_PASS_TOOSOON;
   1805 	     goto done;
   1806 	}
   1807 #endif
   1808 #if 0
   1809 	/*
   1810 	 * Should we be checking/updating pw history here?
   1811 	 */
   1812 	if(pol.pw_history_num > 1) {
   1813 	    if(adb.admin_history_kvno != hist_kvno) {
   1814 		ret = KADM5_BAD_HIST_KEY;
   1815 		goto done;
   1816 	    }
   1817 
   1818 	    if (ret = check_pw_reuse(handle->context,
   1819 				     &hist_key,
   1820 				     kdb.n_key_data, kdb.key_data,
   1821 				     adb.old_key_len, adb.old_keys))
   1822 		goto done;
   1823 	}
   1824 #endif
   1825 
   1826 	if (pol.pw_max_life)
   1827 	   kdb.pw_expiration = now + pol.pw_max_life;
   1828 	else
   1829 	   kdb.pw_expiration = 0;
   1830     } else {
   1831 	kdb.pw_expiration = 0;
   1832     }
   1833 
   1834     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
   1835     if (ret)
   1836 	 goto done;
   1837 
   1838     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
   1839 	goto done;
   1840 
   1841     ret = KADM5_OK;
   1842 done:
   1843     for (i = 0; i < tmp_key_data.key_data_ver; i++) {
   1844 	if (tmp_key_data.key_data_contents[i]) {
   1845 	    memset (tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
   1846 	    free (tmp_key_data.key_data_contents[i]);
   1847 	}
   1848     }
   1849 
   1850     kdb_free_entry(handle, &kdb, &adb);
   1851     if (have_pol)
   1852 	 kadm5_free_policy_ent(handle->lhandle, &pol);
   1853 
   1854     return ret;
   1855 }
   1856 #endif
   1857 
   1858 kadm5_ret_t
   1859 kadm5_setkey_principal(void *server_handle,
   1860 		       krb5_principal principal,
   1861 		       krb5_keyblock *keyblocks,
   1862 		       int n_keys)
   1863 {
   1864     return
   1865 	kadm5_setkey_principal_3(server_handle, principal,
   1866 				 FALSE, 0, NULL,
   1867 				 keyblocks, n_keys);
   1868 }
   1869 
   1870 kadm5_ret_t
   1871 kadm5_setkey_principal_3(void *server_handle,
   1872 			 krb5_principal principal,
   1873 			 krb5_boolean keepold,
   1874 			 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
   1875 			 krb5_keyblock *keyblocks,
   1876 			 int n_keys)
   1877 {
   1878     krb5_db_entry		kdb;
   1879     osa_princ_ent_rec		adb;
   1880     krb5_int32			now;
   1881     kadm5_policy_ent_rec	pol;
   1882     krb5_key_data		*old_key_data;
   1883     int				n_old_keys;
   1884     int				i, j, k, kvno, ret, have_pol = 0;
   1885 #if 0
   1886     int                         last_pwd;
   1887 #endif
   1888     kadm5_server_handle_t	handle = server_handle;
   1889     krb5_boolean		similar;
   1890     krb5_keysalt		keysalt;
   1891     krb5_key_data         tmp_key_data;
   1892     krb5_key_data        *tptr;
   1893 
   1894     CHECK_HANDLE(server_handle);
   1895 
   1896     krb5_clear_error_message(handle->context);
   1897 
   1898     if (principal == NULL || keyblocks == NULL)
   1899 	return EINVAL;
   1900     if (hist_princ && /* this will be NULL when initializing the databse */
   1901 	((krb5_principal_compare(handle->context,
   1902 				 principal, hist_princ)) == TRUE))
   1903 	return KADM5_PROTECT_PRINCIPAL;
   1904 
   1905     for (i = 0; i < n_keys; i++) {
   1906 	for (j = i+1; j < n_keys; j++) {
   1907 	    if ((ret = krb5_c_enctype_compare(handle->context,
   1908 					      keyblocks[i].enctype,
   1909 					      keyblocks[j].enctype,
   1910 					      &similar)))
   1911 		return(ret);
   1912 	    if (similar) {
   1913 		if (n_ks_tuple) {
   1914 		    if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
   1915 			return KADM5_SETKEY_DUP_ENCTYPES;
   1916 		} else
   1917 		    return KADM5_SETKEY_DUP_ENCTYPES;
   1918 	    }
   1919 	}
   1920     }
   1921 
   1922     if (n_ks_tuple && n_ks_tuple != n_keys)
   1923 	return KADM5_SETKEY3_ETYPE_MISMATCH;
   1924 
   1925     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
   1926        return(ret);
   1927 
   1928     for (kvno = 0, i=0; i<kdb.n_key_data; i++)
   1929 	 if (kdb.key_data[i].key_data_kvno > kvno)
   1930 	      kvno = kdb.key_data[i].key_data_kvno;
   1931 
   1932     if (keepold) {
   1933 	old_key_data = kdb.key_data;
   1934 	n_old_keys = kdb.n_key_data;
   1935     } else {
   1936 	if (kdb.key_data != NULL)
   1937 	    cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
   1938 	n_old_keys = 0;
   1939 	old_key_data = NULL;
   1940     }
   1941 
   1942     kdb.key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, (n_keys+n_old_keys)
   1943 						 *sizeof(krb5_key_data));
   1944     if (kdb.key_data == NULL) {
   1945 	ret = ENOMEM;
   1946 	goto done;
   1947     }
   1948 
   1949     memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
   1950     kdb.n_key_data = 0;
   1951 
   1952     for (i = 0; i < n_keys; i++) {
   1953 	if (n_ks_tuple) {
   1954 	    keysalt.type = ks_tuple[i].ks_salttype;
   1955 	    keysalt.data.length = 0;
   1956 	    keysalt.data.data = NULL;
   1957 	    if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
   1958 		ret = KADM5_SETKEY3_ETYPE_MISMATCH;
   1959 		goto done;
   1960 	    }
   1961 	}
   1962 	memset (&tmp_key_data, 0, sizeof(tmp_key_data));
   1963 
   1964 	ret = krb5_dbekd_encrypt_key_data(handle->context,
   1965 					  &handle->master_keyblock,
   1966 					  &keyblocks[i],
   1967 					  n_ks_tuple ? &keysalt : NULL,
   1968 					  kvno + 1,
   1969 					  &tmp_key_data);
   1970 	if (ret) {
   1971 	    goto done;
   1972 	}
   1973 	tptr = &kdb.key_data[i];
   1974 	for (k = 0; k < tmp_key_data.key_data_ver; k++) {
   1975 	    tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
   1976 	    tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
   1977 	    if (tmp_key_data.key_data_contents[k]) {
   1978 		tptr->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
   1979 		if (tptr->key_data_contents[k] == NULL) {
   1980 		    int i1;
   1981 		    for (i1 = k; i1 < tmp_key_data.key_data_ver; i1++) {
   1982 			if (tmp_key_data.key_data_contents[i1]) {
   1983 			    memset (tmp_key_data.key_data_contents[i1], 0, tmp_key_data.key_data_length[i1]);
   1984 			    free (tmp_key_data.key_data_contents[i1]);
   1985 			}
   1986 		    }
   1987 
   1988 		    ret =  ENOMEM;
   1989 		    goto done;
   1990 		}
   1991 		memcpy (tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
   1992 
   1993 		memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
   1994 		free (tmp_key_data.key_data_contents[k]);
   1995 		tmp_key_data.key_data_contents[k] = NULL;
   1996 	    }
   1997 	}
   1998 	kdb.n_key_data++;
   1999     }
   2000 
   2001     /* copy old key data if necessary */
   2002     for (i = 0; i < n_old_keys; i++) {
   2003 	kdb.key_data[i+n_keys] = old_key_data[i];
   2004 	memset(&old_key_data[i], 0, sizeof (krb5_key_data));
   2005 	kdb.n_key_data++;
   2006     }
   2007 
   2008     if (old_key_data)
   2009 	krb5_db_free(handle->context, old_key_data);
   2010 
   2011     /* assert(kdb.n_key_data == n_keys + n_old_keys) */
   2012     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
   2013 
   2014     if ((ret = krb5_timeofday(handle->context, &now)))
   2015 	goto done;
   2016 
   2017     if ((adb.aux_attributes & KADM5_POLICY)) {
   2018 	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
   2019 				    &pol)) != KADM5_OK)
   2020 	   goto done;
   2021 	have_pol = 1;
   2022 
   2023 #if 0
   2024 	/*
   2025 	  * The spec says this check is overridden if the caller has
   2026 	  * modify privilege.  The admin server therefore makes this
   2027 	  * check itself (in chpass_principal_wrapper, misc.c).  A
   2028 	  * local caller implicitly has all authorization bits.
   2029 	  */
   2030 	if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
   2031 						  &kdb, &last_pwd))
   2032 	     goto done;
   2033 	if((now - last_pwd) < pol.pw_min_life &&
   2034 	   !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
   2035 	     ret = KADM5_PASS_TOOSOON;
   2036 	     goto done;
   2037 	}
   2038 #endif
   2039 #if 0
   2040 	/*
   2041 	 * Should we be checking/updating pw history here?
   2042 	 */
   2043 	if (pol.pw_history_num > 1) {
   2044 	    if(adb.admin_history_kvno != hist_kvno) {
   2045 		ret = KADM5_BAD_HIST_KEY;
   2046 		goto done;
   2047 	    }
   2048 
   2049 	    if (ret = check_pw_reuse(handle->context,
   2050 				&handle->master_keyblock,
   2051 				     &hist_key,
   2052 				     kdb.n_key_data, kdb.key_data,
   2053 				     adb.old_key_len, adb.old_keys))
   2054 		goto done;
   2055 	}
   2056 #endif
   2057 
   2058 	if (pol.pw_max_life)
   2059 	   kdb.pw_expiration = now + pol.pw_max_life;
   2060 	else
   2061 	   kdb.pw_expiration = 0;
   2062     } else {
   2063 	kdb.pw_expiration = 0;
   2064     }
   2065 
   2066     if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)))
   2067         goto done;
   2068 
   2069     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
   2070 	goto done;
   2071 
   2072     ret = KADM5_OK;
   2073 done:
   2074     kdb_free_entry(handle, &kdb, &adb);
   2075     if (have_pol)
   2076 	 kadm5_free_policy_ent(handle->lhandle, &pol);
   2077 
   2078     return ret;
   2079 }
   2080 
   2081 /*
   2082  * Allocate an array of n_key_data krb5_keyblocks, fill in each
   2083  * element with the results of decrypting the nth key in key_data with
   2084  * master_keyblock, and if n_keys is not NULL fill it in with the
   2085  * number of keys decrypted.
   2086  */
   2087 static int decrypt_key_data(krb5_context context,
   2088 			    krb5_keyblock *master_keyblock,
   2089 			    int n_key_data, krb5_key_data *key_data,
   2090 			    krb5_keyblock **keyblocks, int *n_keys)
   2091 {
   2092      krb5_keyblock *keys;
   2093      int ret, i;
   2094 
   2095      keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
   2096      if (keys == NULL)
   2097 	  return ENOMEM;
   2098      memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
   2099 
   2100      for (i = 0; i < n_key_data; i++) {
   2101           ret = krb5_dbekd_decrypt_key_data(context,
   2102 					    master_keyblock,
   2103 					    &key_data[i],
   2104 					    &keys[i], NULL);
   2105 	  if (ret) {
   2106 	       for (; i >= 0; i--) {
   2107 		   if (keys[i].contents) {
   2108 		       memset (keys[i].contents, 0, keys[i].length);
   2109 		       free( keys[i].contents );
   2110 		   }
   2111 	       }
   2112 
   2113 	       memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
   2114 	       free(keys);
   2115 	       return ret;
   2116 	  }
   2117      }
   2118 
   2119      *keyblocks = keys;
   2120      if (n_keys)
   2121 	  *n_keys = n_key_data;
   2122 
   2123      return 0;
   2124 }
   2125 
   2126 /*
   2127  * Function: kadm5_decrypt_key
   2128  *
   2129  * Purpose: Retrieves and decrypts a principal key.
   2130  *
   2131  * Arguments:
   2132  *
   2133  *	server_handle	(r) kadm5 handle
   2134  *	entry		(r) principal retrieved with kadm5_get_principal
   2135  *	ktype		(r) enctype to search for, or -1 to ignore
   2136  *	stype		(r) salt type to search for, or -1 to ignore
   2137  *	kvno		(r) kvno to search for, -1 for max, 0 for max
   2138  *			only if it also matches ktype and stype
   2139  *	keyblock	(w) keyblock to fill in
   2140  *	keysalt		(w) keysalt to fill in, or NULL
   2141  *	kvnop		(w) kvno to fill in, or NULL
   2142  *
   2143  * Effects: Searches the key_data array of entry, which must have been
   2144  * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
   2145  * find a key with a specified enctype, salt type, and kvno in a
   2146  * principal entry.  If not found, return ENOENT.  Otherwise, decrypt
   2147  * it with the master key, and return the key in keyblock, the salt
   2148  * in salttype, and the key version number in kvno.
   2149  *
   2150  * If ktype or stype is -1, it is ignored for the search.  If kvno is
   2151  * -1, ktype and stype are ignored and the key with the max kvno is
   2152  * returned.  If kvno is 0, only the key with the max kvno is returned
   2153  * and only if it matches the ktype and stype; otherwise, ENOENT is
   2154  * returned.
   2155  */
   2156 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
   2157 			      kadm5_principal_ent_t entry, krb5_int32
   2158 			      ktype, krb5_int32 stype, krb5_int32
   2159 			      kvno, krb5_keyblock *keyblock,
   2160 			      krb5_keysalt *keysalt, int *kvnop)
   2161 {
   2162     kadm5_server_handle_t handle = server_handle;
   2163     krb5_db_entry dbent;
   2164     krb5_key_data *key_data;
   2165     int ret;
   2166 
   2167     CHECK_HANDLE(server_handle);
   2168 
   2169     if (entry->n_key_data == 0 || entry->key_data == NULL)
   2170 	 return EINVAL;
   2171 
   2172     /* find_enctype only uses these two fields */
   2173     dbent.n_key_data = entry->n_key_data;
   2174     dbent.key_data = entry->key_data;
   2175     if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
   2176 				    stype, kvno, &key_data)))
   2177 	 return ret;
   2178 
   2179     if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
   2180 					   &handle->master_keyblock, key_data,
   2181 					   keyblock, keysalt)))
   2182 	 return ret;
   2183 
   2184     /*
   2185      * Coerce the enctype of the output keyblock in case we got an
   2186      * inexact match on the enctype; this behavior will go away when
   2187      * the key storage architecture gets redesigned for 1.3.
   2188      */
   2189     keyblock->enctype = ktype;
   2190 
   2191     if (kvnop)
   2192 	 *kvnop = key_data->key_data_kvno;
   2193 
   2194     return KADM5_OK;
   2195 }
   2196 
   2197