xref: /onnv-gate/usr/src/lib/krb5/kadm5/srv/svr_principal.c (revision 2881:ea6360e7e1c5)
10Sstevel@tonic-gate /*
2*2881Smp153739  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate 
60Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
70Sstevel@tonic-gate 
80Sstevel@tonic-gate /*
90Sstevel@tonic-gate  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
100Sstevel@tonic-gate  *
110Sstevel@tonic-gate  *	Openvision retains the copyright to derivative works of
120Sstevel@tonic-gate  *	this source code.  Do *NOT* create a derivative of this
130Sstevel@tonic-gate  *	source code before consulting with your legal department.
140Sstevel@tonic-gate  *	Do *NOT* integrate *ANY* of this source code into another
150Sstevel@tonic-gate  *	product before consulting with your legal department.
160Sstevel@tonic-gate  *
170Sstevel@tonic-gate  *	For further information, read the top-level Openvision
180Sstevel@tonic-gate  *	copyright which is contained in the top-level MIT Kerberos
190Sstevel@tonic-gate  *	copyright.
200Sstevel@tonic-gate  *
210Sstevel@tonic-gate  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
220Sstevel@tonic-gate  *
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
280Sstevel@tonic-gate  *
29*2881Smp153739  * $Header: /cvs/krbdev/krb5/src/lib/kadm5/srv/svr_principal.c,v 1.30.8.1 2004/12/20 21:16:20 tlyu Exp $
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #if !defined(lint) && !defined(__CODECENTER__)
33*2881Smp153739 static char *rcsid = "$Header: /cvs/krbdev/krb5/src/lib/kadm5/srv/svr_principal.c,v 1.30.8.1 2004/12/20 21:16:20 tlyu Exp $";
340Sstevel@tonic-gate #endif
350Sstevel@tonic-gate 
360Sstevel@tonic-gate #include	<sys/types.h>
370Sstevel@tonic-gate #include	<sys/time.h>
380Sstevel@tonic-gate #include	<kadm5/admin.h>
390Sstevel@tonic-gate #include	"adb.h"
400Sstevel@tonic-gate #include	"k5-int.h"
410Sstevel@tonic-gate #include	<krb5/kdb.h>
420Sstevel@tonic-gate #include	<stdio.h>
430Sstevel@tonic-gate #include	<string.h>
440Sstevel@tonic-gate #include	"server_internal.h"
450Sstevel@tonic-gate #include	<stdarg.h>
460Sstevel@tonic-gate #include	<stdlib.h>
47*2881Smp153739 #ifdef USE_PASSWORD_SERVER
48*2881Smp153739 #include	<sys/wait.h>
49*2881Smp153739 #endif
500Sstevel@tonic-gate 
510Sstevel@tonic-gate extern	krb5_principal	    master_princ;
520Sstevel@tonic-gate extern	krb5_principal	    hist_princ;
530Sstevel@tonic-gate extern	krb5_keyblock	    hist_key;
540Sstevel@tonic-gate extern	krb5_db_entry	    master_db;
550Sstevel@tonic-gate extern	krb5_db_entry	    hist_db;
560Sstevel@tonic-gate extern  krb5_kvno	    hist_kvno;
570Sstevel@tonic-gate 
580Sstevel@tonic-gate extern	kadm5_ret_t
590Sstevel@tonic-gate krb5_free_key_data_contents(krb5_context context, krb5_key_data *key);
600Sstevel@tonic-gate 
610Sstevel@tonic-gate static int decrypt_key_data(krb5_context context,
62*2881Smp153739 			    krb5_keyblock *, int n_key_data, krb5_key_data *key_data,
63*2881Smp153739 			    krb5_keyblock **keyblocks, int *n_keys);
640Sstevel@tonic-gate 
650Sstevel@tonic-gate /*
660Sstevel@tonic-gate  * XXX Functions that ought to be in libkrb5.a, but aren't.
670Sstevel@tonic-gate  */
680Sstevel@tonic-gate kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
690Sstevel@tonic-gate    krb5_context context;
700Sstevel@tonic-gate    krb5_key_data *from, *to;
710Sstevel@tonic-gate {
720Sstevel@tonic-gate      int i, idx;
730Sstevel@tonic-gate 
740Sstevel@tonic-gate      *to = *from;
750Sstevel@tonic-gate 
760Sstevel@tonic-gate      idx = (from->key_data_ver == 1 ? 1 : 2);
770Sstevel@tonic-gate 
780Sstevel@tonic-gate      for (i = 0; i < idx; i++) {
790Sstevel@tonic-gate        if ( from->key_data_length[i] ) {
800Sstevel@tonic-gate 	 to->key_data_contents[i] = malloc(from->key_data_length[i]);
810Sstevel@tonic-gate 	 if (to->key_data_contents[i] == NULL) {
820Sstevel@tonic-gate 	   for (i = 0; i < idx; i++) {
830Sstevel@tonic-gate 	     if (to->key_data_contents[i]) {
840Sstevel@tonic-gate 	       memset(to->key_data_contents[i], 0,
850Sstevel@tonic-gate 		      to->key_data_length[i]);
860Sstevel@tonic-gate 	       free(to->key_data_contents[i]);
870Sstevel@tonic-gate 	     }
880Sstevel@tonic-gate 	   }
890Sstevel@tonic-gate 	   return ENOMEM;
900Sstevel@tonic-gate 	 }
910Sstevel@tonic-gate 	 memcpy(to->key_data_contents[i], from->key_data_contents[i],
920Sstevel@tonic-gate 		from->key_data_length[i]);
930Sstevel@tonic-gate        }
940Sstevel@tonic-gate      }
950Sstevel@tonic-gate      return 0;
960Sstevel@tonic-gate }
970Sstevel@tonic-gate 
980Sstevel@tonic-gate static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
990Sstevel@tonic-gate {
1000Sstevel@tonic-gate      krb5_tl_data *n;
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate      n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
1030Sstevel@tonic-gate      if (n == NULL)
1040Sstevel@tonic-gate 	  return NULL;
1050Sstevel@tonic-gate      n->tl_data_contents = malloc(tl->tl_data_length);
1060Sstevel@tonic-gate      if (n->tl_data_contents == NULL) {
1070Sstevel@tonic-gate 	  free(n);
1080Sstevel@tonic-gate 	  return NULL;
1090Sstevel@tonic-gate      }
1100Sstevel@tonic-gate      memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
1110Sstevel@tonic-gate      n->tl_data_type = tl->tl_data_type;
1120Sstevel@tonic-gate      n->tl_data_length = tl->tl_data_length;
1130Sstevel@tonic-gate      n->tl_data_next = NULL;
1140Sstevel@tonic-gate      return n;
1150Sstevel@tonic-gate }
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate /* This is in lib/kdb/kdb_cpw.c, but is static */
1180Sstevel@tonic-gate static void cleanup_key_data(context, count, data)
1190Sstevel@tonic-gate    krb5_context	  context;
1200Sstevel@tonic-gate    int			  count;
1210Sstevel@tonic-gate    krb5_key_data	* data;
1220Sstevel@tonic-gate {
1230Sstevel@tonic-gate      int i, j;
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate      for (i = 0; i < count; i++)
1260Sstevel@tonic-gate 	  for (j = 0; j < data[i].key_data_ver; j++)
1270Sstevel@tonic-gate 	       if (data[i].key_data_length[j])
1280Sstevel@tonic-gate 		    free(data[i].key_data_contents[j]);
1290Sstevel@tonic-gate      free(data);
1300Sstevel@tonic-gate }
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate kadm5_ret_t
1330Sstevel@tonic-gate kadm5_create_principal(void *server_handle,
1340Sstevel@tonic-gate 			    kadm5_principal_ent_t entry, long mask,
1350Sstevel@tonic-gate 			    char *password)
1360Sstevel@tonic-gate {
1370Sstevel@tonic-gate 	/*
1380Sstevel@tonic-gate 	 * Default to using the new API with the default set of
1390Sstevel@tonic-gate 	 * key/salt combinations.
1400Sstevel@tonic-gate 	 */
141*2881Smp153739     return
142*2881Smp153739 	kadm5_create_principal_3(server_handle, entry, mask,
143*2881Smp153739 				 0, NULL, password);
1440Sstevel@tonic-gate }
1450Sstevel@tonic-gate kadm5_ret_t
1460Sstevel@tonic-gate kadm5_create_principal_3(void *server_handle,
1470Sstevel@tonic-gate 			 kadm5_principal_ent_t entry, long mask,
1480Sstevel@tonic-gate 			 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1490Sstevel@tonic-gate 			 char *password)
1500Sstevel@tonic-gate {
1510Sstevel@tonic-gate     krb5_db_entry		kdb;
1520Sstevel@tonic-gate     osa_princ_ent_rec		adb;
1530Sstevel@tonic-gate     kadm5_policy_ent_rec	polent;
1540Sstevel@tonic-gate     krb5_int32			now;
1550Sstevel@tonic-gate     krb5_tl_data		*tl_data_orig, *tl_data_tail;
1560Sstevel@tonic-gate     unsigned int		ret;
1570Sstevel@tonic-gate     kadm5_server_handle_t handle = server_handle;
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate     /*
1620Sstevel@tonic-gate      * Argument sanity checking, and opening up the DB
1630Sstevel@tonic-gate      */
1640Sstevel@tonic-gate     if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
1650Sstevel@tonic-gate        (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
1660Sstevel@tonic-gate        (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
1670Sstevel@tonic-gate        (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
1680Sstevel@tonic-gate        (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
1690Sstevel@tonic-gate        (mask & KADM5_FAIL_AUTH_COUNT))
1700Sstevel@tonic-gate 	return KADM5_BAD_MASK;
1710Sstevel@tonic-gate     if((mask & ~ALL_PRINC_MASK))
1720Sstevel@tonic-gate 	return KADM5_BAD_MASK;
1730Sstevel@tonic-gate     if (entry == (kadm5_principal_ent_t) NULL || password == NULL)
1740Sstevel@tonic-gate 	return EINVAL;
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate     /*
1770Sstevel@tonic-gate      * Check to see if the principal exists
1780Sstevel@tonic-gate      */
1790Sstevel@tonic-gate     ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate     switch(ret) {
1820Sstevel@tonic-gate     case KADM5_UNK_PRINC:
1830Sstevel@tonic-gate 	break;
1840Sstevel@tonic-gate     case 0:
1850Sstevel@tonic-gate 	kdb_free_entry(handle, &kdb, &adb);
1860Sstevel@tonic-gate 	return KADM5_DUP;
1870Sstevel@tonic-gate     default:
1880Sstevel@tonic-gate 	return ret;
1890Sstevel@tonic-gate     }
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate     memset(&kdb, 0, sizeof(krb5_db_entry));
1920Sstevel@tonic-gate     memset(&adb, 0, sizeof(osa_princ_ent_rec));
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate     /*
1950Sstevel@tonic-gate      * If a policy was specified, load it.
1960Sstevel@tonic-gate      * If we can not find the one specified return an error
1970Sstevel@tonic-gate      */
1980Sstevel@tonic-gate     if ((mask & KADM5_POLICY)) {
1990Sstevel@tonic-gate 	 if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
2000Sstevel@tonic-gate 				     &polent)) != KADM5_OK) {
2010Sstevel@tonic-gate 	    if(ret == EINVAL)
2020Sstevel@tonic-gate 		return KADM5_BAD_POLICY;
2030Sstevel@tonic-gate 	    else
2040Sstevel@tonic-gate 		return ret;
2050Sstevel@tonic-gate 	}
2060Sstevel@tonic-gate     }
207*2881Smp153739     if ((ret = passwd_check(handle, password, (mask & KADM5_POLICY),
208*2881Smp153739 			    &polent, entry->principal))) {
2090Sstevel@tonic-gate 	if (mask & KADM5_POLICY)
2100Sstevel@tonic-gate 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
2110Sstevel@tonic-gate 	return ret;
2120Sstevel@tonic-gate     }
2130Sstevel@tonic-gate     /*
2140Sstevel@tonic-gate      * Start populating the various DB fields, using the
2150Sstevel@tonic-gate      * "defaults" for fields that were not specified by the
2160Sstevel@tonic-gate      * mask.
2170Sstevel@tonic-gate      */
218*2881Smp153739     if ((ret = krb5_timeofday(handle->context, &now))) {
219*2881Smp153739 	 if (mask & KADM5_POLICY)
220*2881Smp153739 	      (void) kadm5_free_policy_ent(handle->lhandle, &polent);
221*2881Smp153739 	 return ret;
2220Sstevel@tonic-gate     }
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate     kdb.magic = KRB5_KDB_MAGIC_NUMBER;
2250Sstevel@tonic-gate     kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate     /*
2280Sstevel@tonic-gate      * If KADM5_ATTRIBUTES is set, we want to rope in not only
2290Sstevel@tonic-gate      * entry->attributes, but also the generic params.flags
2300Sstevel@tonic-gate      * obtained previously via kadm5_get_config_params.
2310Sstevel@tonic-gate      */
2320Sstevel@tonic-gate     if ((mask & KADM5_ATTRIBUTES)) {
2330Sstevel@tonic-gate 	kdb.attributes = handle->params.flags;
2340Sstevel@tonic-gate 	kdb.attributes |= entry->attributes;
2350Sstevel@tonic-gate     } else {
236*2881Smp153739     kdb.attributes = handle->params.flags;
2370Sstevel@tonic-gate     }
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate     if ((mask & KADM5_MAX_LIFE))
2400Sstevel@tonic-gate 	kdb.max_life = entry->max_life;
2410Sstevel@tonic-gate     else
2420Sstevel@tonic-gate 	kdb.max_life = handle->params.max_life;
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate     if (mask & KADM5_MAX_RLIFE)
2450Sstevel@tonic-gate 	 kdb.max_renewable_life = entry->max_renewable_life;
2460Sstevel@tonic-gate     else
2470Sstevel@tonic-gate 	 kdb.max_renewable_life = handle->params.max_rlife;
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate     if ((mask & KADM5_PRINC_EXPIRE_TIME))
2500Sstevel@tonic-gate 	kdb.expiration = entry->princ_expire_time;
2510Sstevel@tonic-gate     else
2520Sstevel@tonic-gate 	kdb.expiration = handle->params.expiration;
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate     kdb.pw_expiration = 0;
2550Sstevel@tonic-gate     if ((mask & KADM5_POLICY)) {
2560Sstevel@tonic-gate 	if(polent.pw_max_life)
2570Sstevel@tonic-gate 	    kdb.pw_expiration = now + polent.pw_max_life;
2580Sstevel@tonic-gate 	else
2590Sstevel@tonic-gate 	    kdb.pw_expiration = 0;
2600Sstevel@tonic-gate     }
2610Sstevel@tonic-gate     if ((mask & KADM5_PW_EXPIRATION))
2620Sstevel@tonic-gate 	 kdb.pw_expiration = entry->pw_expiration;
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate     kdb.last_success = 0;
2650Sstevel@tonic-gate     kdb.last_failed = 0;
2660Sstevel@tonic-gate     kdb.fail_auth_count = 0;
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate     /* this is kind of gross, but in order to free the tl data, I need
2690Sstevel@tonic-gate        to free the entire kdb entry, and that will try to free the
2700Sstevel@tonic-gate        principal. */
2710Sstevel@tonic-gate 
272*2881Smp153739     if ((ret = krb5_copy_principal(handle->context,
273*2881Smp153739 				   entry->principal, &(kdb.princ)))) {
2740Sstevel@tonic-gate 	if (mask & KADM5_POLICY)
2750Sstevel@tonic-gate 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
2760Sstevel@tonic-gate 	return(ret);
2770Sstevel@tonic-gate     }
2780Sstevel@tonic-gate 
279*2881Smp153739     if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))) {
280*2881Smp153739          krb5_dbe_free_contents(handle->context, &kdb);
281*2881Smp153739 	 if (mask & KADM5_POLICY)
2820Sstevel@tonic-gate 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
283*2881Smp153739 	 return(ret);
2840Sstevel@tonic-gate     }
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate     /* initialize the keys */
2870Sstevel@tonic-gate 
288*2881Smp153739     if ((ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock,
289*2881Smp153739 			    n_ks_tuple?ks_tuple:handle->params.keysalts,
290*2881Smp153739 			    n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
291*2881Smp153739 			    password,
292*2881Smp153739 			    (mask & KADM5_KVNO)?entry->kvno:1,
293*2881Smp153739 			    FALSE, &kdb))) {
2940Sstevel@tonic-gate 	krb5_dbe_free_contents(handle->context, &kdb);
2950Sstevel@tonic-gate 	if (mask & KADM5_POLICY)
2960Sstevel@tonic-gate 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
2970Sstevel@tonic-gate 	return(ret);
2980Sstevel@tonic-gate     }
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate     /* populate the admin-server-specific fields.  In the OV server,
3010Sstevel@tonic-gate        this used to be in a separate database.  Since there's already
3020Sstevel@tonic-gate        marshalling code for the admin fields, to keep things simple,
3030Sstevel@tonic-gate        I'm going to keep it, and make all the admin stuff occupy a
3040Sstevel@tonic-gate        single tl_data record, */
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate     adb.admin_history_kvno = hist_kvno;
3070Sstevel@tonic-gate     if ((mask & KADM5_POLICY)) {
3080Sstevel@tonic-gate 	adb.aux_attributes = KADM5_POLICY;
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 	/* this does *not* need to be strdup'ed, because adb is xdr */
3110Sstevel@tonic-gate 	/* encoded in osa_adb_create_princ, and not ever freed */
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 	adb.policy = entry->policy;
3140Sstevel@tonic-gate     }
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate     /* increment the policy ref count, if any */
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate     if ((mask & KADM5_POLICY)) {
3190Sstevel@tonic-gate 	polent.policy_refcnt++;
3200Sstevel@tonic-gate 	if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
3210Sstevel@tonic-gate 						    KADM5_REF_COUNT))
3220Sstevel@tonic-gate 	    != KADM5_OK) {
3230Sstevel@tonic-gate 	    krb5_dbe_free_contents(handle->context, &kdb);
3240Sstevel@tonic-gate 	    if (mask & KADM5_POLICY)
3250Sstevel@tonic-gate 		 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
3260Sstevel@tonic-gate 	    return(ret);
3270Sstevel@tonic-gate 	}
3280Sstevel@tonic-gate     }
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate     if (mask & KADM5_TL_DATA) {
3310Sstevel@tonic-gate 	 /* splice entry->tl_data onto the front of kdb.tl_data */
3320Sstevel@tonic-gate 	 tl_data_orig = kdb.tl_data;
3330Sstevel@tonic-gate 	 for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next;
3340Sstevel@tonic-gate 	      tl_data_tail = tl_data_tail->tl_data_next)
3350Sstevel@tonic-gate 	      ;
3360Sstevel@tonic-gate 	 tl_data_tail->tl_data_next = kdb.tl_data;
3370Sstevel@tonic-gate 	 kdb.tl_data = entry->tl_data;
3380Sstevel@tonic-gate     }
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate     /* store the new db entry */
3410Sstevel@tonic-gate     ret = kdb_put_entry(handle, &kdb, &adb);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate     if (mask & KADM5_TL_DATA) {
3440Sstevel@tonic-gate 	 /* remove entry->tl_data from the front of kdb.tl_data */
3450Sstevel@tonic-gate 	 tl_data_tail->tl_data_next = NULL;
3460Sstevel@tonic-gate 	 kdb.tl_data = tl_data_orig;
3470Sstevel@tonic-gate     }
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate     krb5_dbe_free_contents(handle->context, &kdb);
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate     if (ret) {
3520Sstevel@tonic-gate 	if ((mask & KADM5_POLICY)) {
3530Sstevel@tonic-gate 	    /* decrement the policy ref count */
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate 	    polent.policy_refcnt--;
3560Sstevel@tonic-gate 	    /*
3570Sstevel@tonic-gate 	     * if this fails, there's nothing we can do anyway.  the
3580Sstevel@tonic-gate 	     * policy refcount wil be too high.
3590Sstevel@tonic-gate 	     */
3600Sstevel@tonic-gate 	    (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
3610Sstevel@tonic-gate 						     KADM5_REF_COUNT);
3620Sstevel@tonic-gate 	}
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	if (mask & KADM5_POLICY)
3650Sstevel@tonic-gate 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
3660Sstevel@tonic-gate 	return(ret);
3670Sstevel@tonic-gate     }
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate     if (mask & KADM5_POLICY)
3700Sstevel@tonic-gate 	 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate     return KADM5_OK;
3730Sstevel@tonic-gate }
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate kadm5_ret_t
3770Sstevel@tonic-gate kadm5_delete_principal(void *server_handle, krb5_principal principal)
3780Sstevel@tonic-gate {
3790Sstevel@tonic-gate     unsigned int		ret;
3800Sstevel@tonic-gate     kadm5_policy_ent_rec	polent;
3810Sstevel@tonic-gate     krb5_db_entry		kdb;
3820Sstevel@tonic-gate     osa_princ_ent_rec		adb;
3830Sstevel@tonic-gate     kadm5_server_handle_t handle = server_handle;
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate     if (principal == NULL)
3880Sstevel@tonic-gate 	return EINVAL;
3890Sstevel@tonic-gate 
390*2881Smp153739     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
3910Sstevel@tonic-gate 	return(ret);
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate     if ((adb.aux_attributes & KADM5_POLICY)) {
3940Sstevel@tonic-gate 	if ((ret = kadm5_get_policy(handle->lhandle,
3950Sstevel@tonic-gate 				    adb.policy, &polent))
3960Sstevel@tonic-gate 	    == KADM5_OK) {
3970Sstevel@tonic-gate 	    polent.policy_refcnt--;
3980Sstevel@tonic-gate 	    if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
3990Sstevel@tonic-gate 							 KADM5_REF_COUNT))
4000Sstevel@tonic-gate 		!= KADM5_OK) {
4010Sstevel@tonic-gate 		(void) kadm5_free_policy_ent(handle->lhandle, &polent);
4020Sstevel@tonic-gate 		kdb_free_entry(handle, &kdb, &adb);
4030Sstevel@tonic-gate 		return(ret);
4040Sstevel@tonic-gate 	    }
4050Sstevel@tonic-gate 	}
406*2881Smp153739 	if ((ret = kadm5_free_policy_ent(handle->lhandle, &polent))) {
407*2881Smp153739 	     kdb_free_entry(handle, &kdb, &adb);
408*2881Smp153739 	     return ret;
4090Sstevel@tonic-gate 	}
4100Sstevel@tonic-gate     }
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate     ret = kdb_delete_entry(handle, principal);
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate     return ret;
4170Sstevel@tonic-gate }
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate kadm5_ret_t
4200Sstevel@tonic-gate kadm5_modify_principal(void *server_handle,
4210Sstevel@tonic-gate 			    kadm5_principal_ent_t entry, long mask)
4220Sstevel@tonic-gate {
4230Sstevel@tonic-gate     int			    ret, ret2, i;
4240Sstevel@tonic-gate     kadm5_policy_ent_rec    npol, opol;
4250Sstevel@tonic-gate     int			    have_npol = 0, have_opol = 0;
4260Sstevel@tonic-gate     krb5_db_entry	    kdb;
427*2881Smp153739     krb5_tl_data	    *tl_data_orig;
4280Sstevel@tonic-gate     osa_princ_ent_rec	    adb;
4290Sstevel@tonic-gate     kadm5_server_handle_t handle = server_handle;
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate     if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
4340Sstevel@tonic-gate        (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
4350Sstevel@tonic-gate        (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
4360Sstevel@tonic-gate        (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
4370Sstevel@tonic-gate        (mask & KADM5_LAST_FAILED))
4380Sstevel@tonic-gate 	return KADM5_BAD_MASK;
4390Sstevel@tonic-gate     if((mask & ~ALL_PRINC_MASK))
4400Sstevel@tonic-gate 	return KADM5_BAD_MASK;
4410Sstevel@tonic-gate     if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
4420Sstevel@tonic-gate 	return KADM5_BAD_MASK;
4430Sstevel@tonic-gate     if(entry == (kadm5_principal_ent_t) NULL)
4440Sstevel@tonic-gate 	return EINVAL;
4450Sstevel@tonic-gate     if (mask & KADM5_TL_DATA) {
4460Sstevel@tonic-gate 	 tl_data_orig = entry->tl_data;
4470Sstevel@tonic-gate 	 while (tl_data_orig) {
4480Sstevel@tonic-gate 	      if (tl_data_orig->tl_data_type < 256)
4490Sstevel@tonic-gate 		   return KADM5_BAD_TL_TYPE;
4500Sstevel@tonic-gate 	      tl_data_orig = tl_data_orig->tl_data_next;
4510Sstevel@tonic-gate 	 }
4520Sstevel@tonic-gate     }
4530Sstevel@tonic-gate 
454*2881Smp153739     ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
455*2881Smp153739     if (ret)
4560Sstevel@tonic-gate 	return(ret);
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate     /*
4590Sstevel@tonic-gate      * This is pretty much the same as create ...
4600Sstevel@tonic-gate      */
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate     if ((mask & KADM5_POLICY)) {
4630Sstevel@tonic-gate 	 /* get the new policy */
4640Sstevel@tonic-gate 	 ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
4650Sstevel@tonic-gate 	 if (ret) {
4660Sstevel@tonic-gate 	      switch (ret) {
4670Sstevel@tonic-gate 	      case EINVAL:
4680Sstevel@tonic-gate 		   ret = KADM5_BAD_POLICY;
4690Sstevel@tonic-gate 		   break;
4700Sstevel@tonic-gate 	      case KADM5_UNK_POLICY:
4710Sstevel@tonic-gate 	      case KADM5_BAD_POLICY:
4720Sstevel@tonic-gate 		   ret =  KADM5_UNK_POLICY;
4730Sstevel@tonic-gate 		   break;
4740Sstevel@tonic-gate 	      }
4750Sstevel@tonic-gate 	      goto done;
4760Sstevel@tonic-gate 	 }
4770Sstevel@tonic-gate 	 have_npol = 1;
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 	 /* if we already have a policy, get it to decrement the refcnt */
4800Sstevel@tonic-gate 	 if(adb.aux_attributes & KADM5_POLICY) {
4810Sstevel@tonic-gate 	      /* ... but not if the old and new are the same */
4820Sstevel@tonic-gate 	      if(strcmp(adb.policy, entry->policy)) {
4830Sstevel@tonic-gate 		   ret = kadm5_get_policy(handle->lhandle,
4840Sstevel@tonic-gate 					  adb.policy, &opol);
4850Sstevel@tonic-gate 		   switch(ret) {
4860Sstevel@tonic-gate 		   case EINVAL:
4870Sstevel@tonic-gate 		   case KADM5_BAD_POLICY:
4880Sstevel@tonic-gate 		   case KADM5_UNK_POLICY:
4890Sstevel@tonic-gate 			break;
4900Sstevel@tonic-gate 		   case KADM5_OK:
4910Sstevel@tonic-gate 			have_opol = 1;
4920Sstevel@tonic-gate 			opol.policy_refcnt--;
4930Sstevel@tonic-gate 			break;
4940Sstevel@tonic-gate 		   default:
4950Sstevel@tonic-gate 			goto done;
496*2881Smp153739 			break;
4970Sstevel@tonic-gate 		   }
4980Sstevel@tonic-gate 		   npol.policy_refcnt++;
4990Sstevel@tonic-gate 	      }
5000Sstevel@tonic-gate 	 } else npol.policy_refcnt++;
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	 /* set us up to use the new policy */
5030Sstevel@tonic-gate 	 adb.aux_attributes |= KADM5_POLICY;
5040Sstevel@tonic-gate 	 if (adb.policy)
5050Sstevel@tonic-gate 	      free(adb.policy);
5060Sstevel@tonic-gate 	 adb.policy = strdup(entry->policy);
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	 /* set pw_max_life based on new policy */
5090Sstevel@tonic-gate 	 if (npol.pw_max_life) {
510*2881Smp153739 	     ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
511*2881Smp153739 						   &(kdb.pw_expiration));
512*2881Smp153739 	     if (ret)
513*2881Smp153739 		 goto done;
514*2881Smp153739 	     kdb.pw_expiration += npol.pw_max_life;
5150Sstevel@tonic-gate 	 } else {
516*2881Smp153739 	     kdb.pw_expiration = 0;
5170Sstevel@tonic-gate 	 }
5180Sstevel@tonic-gate     }
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate     if ((mask & KADM5_POLICY_CLR) &&
5210Sstevel@tonic-gate 	(adb.aux_attributes & KADM5_POLICY)) {
5220Sstevel@tonic-gate 	 ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
5230Sstevel@tonic-gate 	 switch(ret) {
5240Sstevel@tonic-gate 	 case EINVAL:
5250Sstevel@tonic-gate 	 case KADM5_BAD_POLICY:
5260Sstevel@tonic-gate 	 case KADM5_UNK_POLICY:
5270Sstevel@tonic-gate 	      ret = KADM5_BAD_DB;
5280Sstevel@tonic-gate 	      goto done;
529*2881Smp153739 	      break;
5300Sstevel@tonic-gate 	 case KADM5_OK:
5310Sstevel@tonic-gate 	      have_opol = 1;
5320Sstevel@tonic-gate 	      if (adb.policy)
5330Sstevel@tonic-gate 		   free(adb.policy);
5340Sstevel@tonic-gate 	      adb.policy = NULL;
5350Sstevel@tonic-gate 	      adb.aux_attributes &= ~KADM5_POLICY;
5360Sstevel@tonic-gate 	      kdb.pw_expiration = 0;
5370Sstevel@tonic-gate 	      opol.policy_refcnt--;
5380Sstevel@tonic-gate 	      break;
5390Sstevel@tonic-gate 	 default:
5400Sstevel@tonic-gate 	      goto done;
541*2881Smp153739 	      break;
5420Sstevel@tonic-gate 	 }
5430Sstevel@tonic-gate     }
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate     if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) &&
5460Sstevel@tonic-gate 	(((have_opol) &&
5470Sstevel@tonic-gate 	  (ret =
5480Sstevel@tonic-gate 	   kadm5_modify_policy_internal(handle->lhandle, &opol,
5490Sstevel@tonic-gate 					     KADM5_REF_COUNT))) ||
5500Sstevel@tonic-gate 	 ((have_npol) &&
5510Sstevel@tonic-gate 	  (ret =
5520Sstevel@tonic-gate 	   kadm5_modify_policy_internal(handle->lhandle, &npol,
5530Sstevel@tonic-gate 					     KADM5_REF_COUNT)))))
5540Sstevel@tonic-gate 	goto done;
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate     if ((mask & KADM5_ATTRIBUTES))
5570Sstevel@tonic-gate 	kdb.attributes = entry->attributes;
5580Sstevel@tonic-gate     if ((mask & KADM5_MAX_LIFE))
5590Sstevel@tonic-gate 	kdb.max_life = entry->max_life;
5600Sstevel@tonic-gate     if ((mask & KADM5_PRINC_EXPIRE_TIME))
5610Sstevel@tonic-gate 	kdb.expiration = entry->princ_expire_time;
5620Sstevel@tonic-gate     if (mask & KADM5_PW_EXPIRATION)
5630Sstevel@tonic-gate 	 kdb.pw_expiration = entry->pw_expiration;
5640Sstevel@tonic-gate     if (mask & KADM5_MAX_RLIFE)
5650Sstevel@tonic-gate 	 kdb.max_renewable_life = entry->max_renewable_life;
5660Sstevel@tonic-gate     if (mask & KADM5_FAIL_AUTH_COUNT)
5670Sstevel@tonic-gate 	 kdb.fail_auth_count = entry->fail_auth_count;
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate     if((mask & KADM5_KVNO)) {
5700Sstevel@tonic-gate 	 for (i = 0; i < kdb.n_key_data; i++)
5710Sstevel@tonic-gate 	      kdb.key_data[i].key_data_kvno = entry->kvno;
5720Sstevel@tonic-gate     }
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate     if (mask & KADM5_TL_DATA) {
5750Sstevel@tonic-gate 	 krb5_tl_data *tl, *tl2;
5760Sstevel@tonic-gate 	 /*
5770Sstevel@tonic-gate 	  * Replace kdb.tl_data with what was passed in.  The
5780Sstevel@tonic-gate 	  * KRB5_TL_KADM_DATA will be re-added (based on adb) by
5790Sstevel@tonic-gate 	  * kdb_put_entry, below.
5800Sstevel@tonic-gate 	  *
5810Sstevel@tonic-gate 	  * Note that we have to duplicate the passed in tl_data
5820Sstevel@tonic-gate 	  * before adding it to kdb.  The reason is that kdb_put_entry
5830Sstevel@tonic-gate 	  * will add its own tl_data entries that we will need to
5840Sstevel@tonic-gate 	  * free, but we cannot free the caller's tl_data (an
5850Sstevel@tonic-gate 	  * alternative would be to scan the tl_data after put_entry
5860Sstevel@tonic-gate 	  * and only free those entries that were not passed in).
5870Sstevel@tonic-gate 	  */
5880Sstevel@tonic-gate 	 while (kdb.tl_data) {
5890Sstevel@tonic-gate 	      tl = kdb.tl_data->tl_data_next;
5900Sstevel@tonic-gate 	      free(kdb.tl_data->tl_data_contents);
5910Sstevel@tonic-gate 	      free(kdb.tl_data);
5920Sstevel@tonic-gate 	      kdb.tl_data = tl;
5930Sstevel@tonic-gate 	 }
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 	 kdb.n_tl_data = entry->n_tl_data;
5960Sstevel@tonic-gate 	 kdb.tl_data = NULL;
5970Sstevel@tonic-gate 	 tl2 = entry->tl_data;
5980Sstevel@tonic-gate 	 while (tl2) {
5990Sstevel@tonic-gate 	      tl = dup_tl_data(tl2);
6000Sstevel@tonic-gate 	      tl->tl_data_next = kdb.tl_data;
6010Sstevel@tonic-gate 	      kdb.tl_data = tl;
6020Sstevel@tonic-gate 	      tl2 = tl2->tl_data_next;
6030Sstevel@tonic-gate 	 }
6040Sstevel@tonic-gate     }
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate     ret = kdb_put_entry(handle, &kdb, &adb);
6070Sstevel@tonic-gate     if (ret) goto done;
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate     ret = KADM5_OK;
6100Sstevel@tonic-gate done:
6110Sstevel@tonic-gate     if (have_opol) {
6120Sstevel@tonic-gate 	 ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
6130Sstevel@tonic-gate 	 ret = ret ? ret : ret2;
6140Sstevel@tonic-gate     }
6150Sstevel@tonic-gate     if (have_npol) {
6160Sstevel@tonic-gate 	 ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
6170Sstevel@tonic-gate 	 ret = ret ? ret : ret2;
6180Sstevel@tonic-gate     }
6190Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
6200Sstevel@tonic-gate     return ret;
6210Sstevel@tonic-gate }
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate kadm5_ret_t
6240Sstevel@tonic-gate kadm5_rename_principal(void *server_handle,
6250Sstevel@tonic-gate 			    krb5_principal source, krb5_principal target)
6260Sstevel@tonic-gate {
6270Sstevel@tonic-gate     krb5_db_entry	kdb;
6280Sstevel@tonic-gate     osa_princ_ent_rec	adb;
6290Sstevel@tonic-gate     int			ret, i;
6300Sstevel@tonic-gate     kadm5_server_handle_t handle = server_handle;
6310Sstevel@tonic-gate 
6320Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate     if (source == NULL || target == NULL)
6350Sstevel@tonic-gate 	return EINVAL;
6360Sstevel@tonic-gate 
6370Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
6380Sstevel@tonic-gate 	kdb_free_entry(handle, &kdb, &adb);
6390Sstevel@tonic-gate 	return(KADM5_DUP);
6400Sstevel@tonic-gate     }
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
6430Sstevel@tonic-gate 	return ret;
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate     /* this is kinda gross, but unavoidable */
6460Sstevel@tonic-gate 
6470Sstevel@tonic-gate     for (i=0; i<kdb.n_key_data; i++) {
6480Sstevel@tonic-gate 	if ((kdb.key_data[i].key_data_ver == 1) ||
6490Sstevel@tonic-gate 	    (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
6500Sstevel@tonic-gate 	    ret = KADM5_NO_RENAME_SALT;
6510Sstevel@tonic-gate 	    goto done;
6520Sstevel@tonic-gate 	}
6530Sstevel@tonic-gate     }
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate     krb5_free_principal(handle->context, kdb.princ);
656*2881Smp153739     ret = krb5_copy_principal(handle->context, target, &kdb.princ);
657*2881Smp153739     if (ret) {
6580Sstevel@tonic-gate 	kdb.princ = NULL; /* so freeing the dbe doesn't lose */
6590Sstevel@tonic-gate 	goto done;
6600Sstevel@tonic-gate     }
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
6630Sstevel@tonic-gate 	goto done;
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate     ret = kdb_delete_entry(handle, source);
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate done:
6680Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
6690Sstevel@tonic-gate     return ret;
6700Sstevel@tonic-gate }
6710Sstevel@tonic-gate 
6720Sstevel@tonic-gate kadm5_ret_t
6730Sstevel@tonic-gate kadm5_get_principal(void *server_handle, krb5_principal principal,
6740Sstevel@tonic-gate 		    kadm5_principal_ent_t entry,
6750Sstevel@tonic-gate 		    long in_mask)
6760Sstevel@tonic-gate {
6770Sstevel@tonic-gate     krb5_db_entry		kdb;
6780Sstevel@tonic-gate     osa_princ_ent_rec		adb;
6790Sstevel@tonic-gate     osa_adb_ret_t		ret = 0;
6800Sstevel@tonic-gate     long			mask;
6810Sstevel@tonic-gate     int i;
6820Sstevel@tonic-gate     kadm5_server_handle_t handle = server_handle;
6830Sstevel@tonic-gate     kadm5_principal_ent_rec	entry_local, *entry_orig;
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate     /*
6880Sstevel@tonic-gate      * In version 1, all the defined fields are always returned.
6890Sstevel@tonic-gate      * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
6900Sstevel@tonic-gate      * filled with allocated memory.
6910Sstevel@tonic-gate      */
6920Sstevel@tonic-gate     if (handle->api_version == KADM5_API_VERSION_1) {
6930Sstevel@tonic-gate 	 mask = KADM5_PRINCIPAL_NORMAL_MASK;
6940Sstevel@tonic-gate 	 entry_orig = entry;
6950Sstevel@tonic-gate 	 entry = &entry_local;
6960Sstevel@tonic-gate     } else {
6970Sstevel@tonic-gate 	 mask = in_mask;
6980Sstevel@tonic-gate     }
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate     memset((char *) entry, 0, sizeof(*entry));
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate     if (principal == NULL)
7030Sstevel@tonic-gate 	return EINVAL;
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
7060Sstevel@tonic-gate 	return ret;
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate     if ((mask & KADM5_POLICY) &&
7090Sstevel@tonic-gate 	adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
7100Sstevel@tonic-gate 	if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) {
7110Sstevel@tonic-gate 	    ret = ENOMEM;
7120Sstevel@tonic-gate 	    goto done;
7130Sstevel@tonic-gate 	}
7140Sstevel@tonic-gate 	strcpy(entry->policy, adb.policy);
7150Sstevel@tonic-gate     }
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate     if (mask & KADM5_AUX_ATTRIBUTES)
7180Sstevel@tonic-gate 	 entry->aux_attributes = adb.aux_attributes;
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate     if ((mask & KADM5_PRINCIPAL) &&
7210Sstevel@tonic-gate 	(ret = krb5_copy_principal(handle->context, principal,
7220Sstevel@tonic-gate 				   &entry->principal))) {
7230Sstevel@tonic-gate 	goto done;
7240Sstevel@tonic-gate     }
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate     if (mask & KADM5_PRINC_EXPIRE_TIME)
7270Sstevel@tonic-gate 	 entry->princ_expire_time = kdb.expiration;
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate     if ((mask & KADM5_LAST_PWD_CHANGE) &&
7300Sstevel@tonic-gate 	(ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
7310Sstevel@tonic-gate 					       &(entry->last_pwd_change)))) {
7320Sstevel@tonic-gate 	goto done;
7330Sstevel@tonic-gate     }
7340Sstevel@tonic-gate 
7350Sstevel@tonic-gate     if (mask & KADM5_PW_EXPIRATION)
7360Sstevel@tonic-gate 	 entry->pw_expiration = kdb.pw_expiration;
7370Sstevel@tonic-gate     if (mask & KADM5_MAX_LIFE)
7380Sstevel@tonic-gate 	 entry->max_life = kdb.max_life;
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate     /* this is a little non-sensical because the function returns two */
7410Sstevel@tonic-gate     /* values that must be checked separately against the mask */
7420Sstevel@tonic-gate     if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
743*2881Smp153739 	ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
744*2881Smp153739 					     &(entry->mod_date),
745*2881Smp153739 					     &(entry->mod_name));
746*2881Smp153739 	if (ret) {
747*2881Smp153739 	    goto done;
748*2881Smp153739 	}
749*2881Smp153739 
750*2881Smp153739 	if (! (mask & KADM5_MOD_TIME))
751*2881Smp153739 	    entry->mod_date = 0;
752*2881Smp153739 	if (! (mask & KADM5_MOD_NAME)) {
753*2881Smp153739 	    krb5_free_principal(handle->context, entry->principal);
754*2881Smp153739 	    entry->principal = NULL;
755*2881Smp153739 	}
7560Sstevel@tonic-gate     }
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate     if (mask & KADM5_ATTRIBUTES)
7590Sstevel@tonic-gate 	 entry->attributes = kdb.attributes;
7600Sstevel@tonic-gate 
7610Sstevel@tonic-gate     if (mask & KADM5_KVNO)
7620Sstevel@tonic-gate 	 for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
7630Sstevel@tonic-gate 	      if (kdb.key_data[i].key_data_kvno > entry->kvno)
7640Sstevel@tonic-gate 		   entry->kvno = kdb.key_data[i].key_data_kvno;
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate     if (handle->api_version == KADM5_API_VERSION_2)
7670Sstevel@tonic-gate 	 entry->mkvno = 0;
7680Sstevel@tonic-gate     else {
7690Sstevel@tonic-gate 	 /* XXX I'll be damned if I know how to deal with this one --marc */
7700Sstevel@tonic-gate 	 entry->mkvno = 1;
7710Sstevel@tonic-gate     }
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate     /*
7740Sstevel@tonic-gate      * The new fields that only exist in version 2 start here
7750Sstevel@tonic-gate      */
7760Sstevel@tonic-gate     if (handle->api_version == KADM5_API_VERSION_2) {
7770Sstevel@tonic-gate 	 if (mask & KADM5_MAX_RLIFE)
7780Sstevel@tonic-gate 	      entry->max_renewable_life = kdb.max_renewable_life;
7790Sstevel@tonic-gate 	 if (mask & KADM5_LAST_SUCCESS)
7800Sstevel@tonic-gate 	      entry->last_success = kdb.last_success;
7810Sstevel@tonic-gate 	 if (mask & KADM5_LAST_FAILED)
7820Sstevel@tonic-gate 	      entry->last_failed = kdb.last_failed;
7830Sstevel@tonic-gate 	 if (mask & KADM5_FAIL_AUTH_COUNT)
7840Sstevel@tonic-gate 	      entry->fail_auth_count = kdb.fail_auth_count;
7850Sstevel@tonic-gate 	 if (mask & KADM5_TL_DATA) {
786*2881Smp153739 	      krb5_tl_data *tl, *tl2;
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate 	      entry->tl_data = NULL;
7890Sstevel@tonic-gate 
7900Sstevel@tonic-gate 	      tl = kdb.tl_data;
7910Sstevel@tonic-gate 	      while (tl) {
7920Sstevel@tonic-gate 		   if (tl->tl_data_type > 255) {
7930Sstevel@tonic-gate 			if ((tl2 = dup_tl_data(tl)) == NULL) {
7940Sstevel@tonic-gate 			     ret = ENOMEM;
7950Sstevel@tonic-gate 			     goto done;
7960Sstevel@tonic-gate 			}
7970Sstevel@tonic-gate 			tl2->tl_data_next = entry->tl_data;
7980Sstevel@tonic-gate 			entry->tl_data = tl2;
7990Sstevel@tonic-gate 			entry->n_tl_data++;
8000Sstevel@tonic-gate 		   }
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate 		   tl = tl->tl_data_next;
8030Sstevel@tonic-gate 	      }
8040Sstevel@tonic-gate 	 }
8050Sstevel@tonic-gate 	 if (mask & KADM5_KEY_DATA) {
8060Sstevel@tonic-gate 	      entry->n_key_data = kdb.n_key_data;
8070Sstevel@tonic-gate 	      if(entry->n_key_data) {
8080Sstevel@tonic-gate 		      entry->key_data = (krb5_key_data *)
8090Sstevel@tonic-gate 			      malloc(entry->n_key_data*sizeof(krb5_key_data));
8100Sstevel@tonic-gate 		      if (entry->key_data == NULL) {
8110Sstevel@tonic-gate 			      ret = ENOMEM;
8120Sstevel@tonic-gate 			      goto done;
8130Sstevel@tonic-gate 		      }
8140Sstevel@tonic-gate 	      } else
8150Sstevel@tonic-gate 		      entry->key_data = NULL;
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate 	      for (i = 0; i < entry->n_key_data; i++)
818*2881Smp153739 		  ret = krb5_copy_key_data_contents(handle->context,
819*2881Smp153739 						    &kdb.key_data[i],
820*2881Smp153739 						    &entry->key_data[i]);
821*2881Smp153739 		   if (ret)
8220Sstevel@tonic-gate 			goto done;
8230Sstevel@tonic-gate 	 }
8240Sstevel@tonic-gate     }
8250Sstevel@tonic-gate 
8260Sstevel@tonic-gate     /*
8270Sstevel@tonic-gate      * If KADM5_API_VERSION_1, we return an allocated structure, and
8280Sstevel@tonic-gate      * we need to convert the new structure back into the format the
8290Sstevel@tonic-gate      * caller is expecting.
8300Sstevel@tonic-gate      */
8310Sstevel@tonic-gate     if (handle->api_version == KADM5_API_VERSION_1) {
8320Sstevel@tonic-gate 	 kadm5_principal_ent_t_v1 newv1;
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate 	 newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
8350Sstevel@tonic-gate 	 if (newv1 == NULL) {
8360Sstevel@tonic-gate 	      ret = ENOMEM;
8370Sstevel@tonic-gate 	      goto done;
8380Sstevel@tonic-gate 	 }
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 	 newv1->principal = entry->principal;
8410Sstevel@tonic-gate 	 newv1->princ_expire_time = entry->princ_expire_time;
8420Sstevel@tonic-gate 	 newv1->last_pwd_change = entry->last_pwd_change;
8430Sstevel@tonic-gate 	 newv1->pw_expiration = entry->pw_expiration;
8440Sstevel@tonic-gate 	 newv1->max_life = entry->max_life;
8450Sstevel@tonic-gate 	 newv1->mod_name = entry->mod_name;
8460Sstevel@tonic-gate 	 newv1->mod_date = entry->mod_date;
8470Sstevel@tonic-gate 	 newv1->attributes = entry->attributes;
8480Sstevel@tonic-gate 	 newv1->kvno = entry->kvno;
8490Sstevel@tonic-gate 	 newv1->mkvno = entry->mkvno;
8500Sstevel@tonic-gate 	 newv1->policy = entry->policy;
8510Sstevel@tonic-gate 	 newv1->aux_attributes = entry->aux_attributes;
8520Sstevel@tonic-gate 
8530Sstevel@tonic-gate 	 *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
8540Sstevel@tonic-gate     }
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate     ret = KADM5_OK;
8570Sstevel@tonic-gate 
8580Sstevel@tonic-gate done:
8590Sstevel@tonic-gate     if (ret && entry->principal)
8600Sstevel@tonic-gate 	 krb5_free_principal(handle->context, entry->principal);
8610Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate     return ret;
8640Sstevel@tonic-gate }
8650Sstevel@tonic-gate 
8660Sstevel@tonic-gate /*
8670Sstevel@tonic-gate  * Function: check_pw_reuse
8680Sstevel@tonic-gate  *
8690Sstevel@tonic-gate  * Purpose: Check if a key appears in a list of keys, in order to
8700Sstevel@tonic-gate  * enforce password history.
8710Sstevel@tonic-gate  *
8720Sstevel@tonic-gate  * Arguments:
8730Sstevel@tonic-gate  *
8740Sstevel@tonic-gate  *	context			(r) the krb5 context
8750Sstevel@tonic-gate  *	hist_keyblock		(r) the key that hist_key_data is
8760Sstevel@tonic-gate  *				encrypted in
8770Sstevel@tonic-gate  *	n_new_key_data		(r) length of new_key_data
8780Sstevel@tonic-gate  *	new_key_data		(r) keys to check against
8790Sstevel@tonic-gate  *				pw_hist_data, encrypted in hist_keyblock
8800Sstevel@tonic-gate  *	n_pw_hist_data		(r) length of pw_hist_data
8810Sstevel@tonic-gate  *	pw_hist_data		(r) passwords to check new_key_data against
8820Sstevel@tonic-gate  *
8830Sstevel@tonic-gate  * Effects:
8840Sstevel@tonic-gate  * For each new_key in new_key_data:
8850Sstevel@tonic-gate  * 	decrypt new_key with the master_keyblock
8860Sstevel@tonic-gate  * 	for each password in pw_hist_data:
8870Sstevel@tonic-gate  *		for each hist_key in password:
8880Sstevel@tonic-gate  *			decrypt hist_key with hist_keyblock
8890Sstevel@tonic-gate  *			compare the new_key and hist_key
8900Sstevel@tonic-gate  *
8910Sstevel@tonic-gate  * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
8920Sstevel@tonic-gate  * new_key_data is the same as a key in pw_hist_data, or 0.
8930Sstevel@tonic-gate  */
8940Sstevel@tonic-gate static kadm5_ret_t
8950Sstevel@tonic-gate check_pw_reuse(krb5_context context,
8960Sstevel@tonic-gate 	       krb5_keyblock *master_keyblock,
8970Sstevel@tonic-gate 	       krb5_keyblock *hist_keyblock,
8980Sstevel@tonic-gate 	       int n_new_key_data, krb5_key_data *new_key_data,
899*2881Smp153739 	       unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
9000Sstevel@tonic-gate {
9010Sstevel@tonic-gate     int x, y, z;
9020Sstevel@tonic-gate     krb5_keyblock newkey, histkey;
9030Sstevel@tonic-gate     krb5_error_code ret;
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate     for (x = 0; x < n_new_key_data; x++) {
906*2881Smp153739 	ret = krb5_dbekd_decrypt_key_data(context,
907*2881Smp153739 					  master_keyblock,
908*2881Smp153739 					  &(new_key_data[x]),
909*2881Smp153739 					  &newkey, NULL);
910*2881Smp153739 	if (ret)
9110Sstevel@tonic-gate 	    return(ret);
9120Sstevel@tonic-gate 	for (y = 0; y < n_pw_hist_data; y++) {
9130Sstevel@tonic-gate 	     for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
914*2881Smp153739 		 ret = krb5_dbekd_decrypt_key_data(context,
915*2881Smp153739 						   hist_keyblock,
916*2881Smp153739 						   &pw_hist_data[y].key_data[z],
917*2881Smp153739 						   &histkey, NULL);
918*2881Smp153739 		 if (ret)
919*2881Smp153739 		     return(ret);
920*2881Smp153739 
921*2881Smp153739 		 if ((newkey.length == histkey.length) &&
922*2881Smp153739 		     (newkey.enctype == histkey.enctype) &&
923*2881Smp153739 		     (memcmp(newkey.contents, histkey.contents,
924*2881Smp153739 			     histkey.length) == 0)) {
925*2881Smp153739 		     krb5_free_keyblock_contents(context, &histkey);
926*2881Smp153739 		     krb5_free_keyblock_contents(context, &newkey);
927*2881Smp153739 
928*2881Smp153739 		     return(KADM5_PASS_REUSE);
929*2881Smp153739 		 }
930*2881Smp153739 		 krb5_free_keyblock_contents(context, &histkey);
9310Sstevel@tonic-gate 	     }
9320Sstevel@tonic-gate 	}
9330Sstevel@tonic-gate 	krb5_free_keyblock_contents(context, &newkey);
9340Sstevel@tonic-gate     }
9350Sstevel@tonic-gate 
9360Sstevel@tonic-gate     return(0);
9370Sstevel@tonic-gate }
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate /*
9400Sstevel@tonic-gate  * Function: create_history_entry
9410Sstevel@tonic-gate  *
9420Sstevel@tonic-gate  * Purpose: Creates a password history entry from an array of
9430Sstevel@tonic-gate  * key_data.
9440Sstevel@tonic-gate  *
9450Sstevel@tonic-gate  * Arguments:
9460Sstevel@tonic-gate  *
9470Sstevel@tonic-gate  *	context		(r) krb5_context to use
9480Sstevel@tonic-gate  *      master_keyblcok (r) master key block
9490Sstevel@tonic-gate  *	n_key_data	(r) number of elements in key_data
9500Sstevel@tonic-gate  *	key_data	(r) keys to add to the history entry
9510Sstevel@tonic-gate  *	hist		(w) history entry to fill in
9520Sstevel@tonic-gate  *
9530Sstevel@tonic-gate  * Effects:
9540Sstevel@tonic-gate  *
9550Sstevel@tonic-gate  * hist->key_data is allocated to store n_key_data key_datas.  Each
9560Sstevel@tonic-gate  * element of key_data is decrypted with master_keyblock, re-encrypted
9570Sstevel@tonic-gate  * in hist_key, and added to hist->key_data.  hist->n_key_data is
9580Sstevel@tonic-gate  * set to n_key_data.
9590Sstevel@tonic-gate  */
9600Sstevel@tonic-gate static
9610Sstevel@tonic-gate int create_history_entry(krb5_context context,
9620Sstevel@tonic-gate 	krb5_keyblock *master_keyblock,	int n_key_data,
9630Sstevel@tonic-gate 	krb5_key_data *key_data, osa_pw_hist_ent *hist)
9640Sstevel@tonic-gate {
9650Sstevel@tonic-gate      int i, ret;
9660Sstevel@tonic-gate      krb5_keyblock key;
9670Sstevel@tonic-gate      krb5_keysalt salt;
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate      hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
9700Sstevel@tonic-gate      if (hist->key_data == NULL)
9710Sstevel@tonic-gate 	  return ENOMEM;
9720Sstevel@tonic-gate      memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate      for (i = 0; i < n_key_data; i++) {
975*2881Smp153739 	 ret = krb5_dbekd_decrypt_key_data(context,
976*2881Smp153739 					   master_keyblock,
977*2881Smp153739 					   &key_data[i],
978*2881Smp153739 					   &key, &salt);
979*2881Smp153739 	 if (ret)
980*2881Smp153739 	     return ret;
981*2881Smp153739 
982*2881Smp153739 	 ret = krb5_dbekd_encrypt_key_data(context, &hist_key,
983*2881Smp153739 					   &key, &salt,
984*2881Smp153739 					   key_data[i].key_data_kvno,
985*2881Smp153739 					   &hist->key_data[i]);
986*2881Smp153739 	 if (ret)
987*2881Smp153739 	     return ret;
988*2881Smp153739 
989*2881Smp153739 	 krb5_free_keyblock_contents(context, &key);
990*2881Smp153739 	 /* krb5_free_keysalt(context, &salt); */
9910Sstevel@tonic-gate      }
9920Sstevel@tonic-gate 
9930Sstevel@tonic-gate      hist->n_key_data = n_key_data;
9940Sstevel@tonic-gate      return 0;
9950Sstevel@tonic-gate }
9960Sstevel@tonic-gate 
997*2881Smp153739 static
9980Sstevel@tonic-gate void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
9990Sstevel@tonic-gate {
10000Sstevel@tonic-gate      int i;
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate      for (i = 0; i < hist->n_key_data; i++)
10030Sstevel@tonic-gate 	  krb5_free_key_data_contents(context, &hist->key_data[i]);
10040Sstevel@tonic-gate      free(hist->key_data);
10050Sstevel@tonic-gate }
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate /*
10080Sstevel@tonic-gate  * Function: add_to_history
10090Sstevel@tonic-gate  *
10100Sstevel@tonic-gate  * Purpose: Adds a password to a principal's password history.
10110Sstevel@tonic-gate  *
10120Sstevel@tonic-gate  * Arguments:
10130Sstevel@tonic-gate  *
10140Sstevel@tonic-gate  *	context		(r) krb5_context to use
10150Sstevel@tonic-gate  *	adb		(r/w) admin principal entry to add keys to
10160Sstevel@tonic-gate  *	pol		(r) adb's policy
10170Sstevel@tonic-gate  *	pw		(r) keys for the password to add to adb's key history
10180Sstevel@tonic-gate  *
10190Sstevel@tonic-gate  * Effects:
10200Sstevel@tonic-gate  *
10210Sstevel@tonic-gate  * add_to_history adds a single password to adb's password history.
10220Sstevel@tonic-gate  * pw contains n_key_data keys in its key_data, in storage should be
10230Sstevel@tonic-gate  * allocated but not freed by the caller (XXX blech!).
10240Sstevel@tonic-gate  *
10250Sstevel@tonic-gate  * This function maintains adb->old_keys as a circular queue.  It
10260Sstevel@tonic-gate  * starts empty, and grows each time this function is called until it
10270Sstevel@tonic-gate  * is pol->pw_history_num items long.  adb->old_key_len holds the
10280Sstevel@tonic-gate  * number of allocated entries in the array, and must therefore be [0,
10290Sstevel@tonic-gate  * pol->pw_history_num).  adb->old_key_next is the index into the
10300Sstevel@tonic-gate  * array where the next element should be written, and must be [0,
10310Sstevel@tonic-gate  * adb->old_key_len).
10320Sstevel@tonic-gate  */
10330Sstevel@tonic-gate #define	KADM_MOD(x)	(x + adb->old_key_next) % adb->old_key_len
1034*2881Smp153739 static kadm5_ret_t add_to_history(krb5_context context,
1035*2881Smp153739 				  osa_princ_ent_t adb,
1036*2881Smp153739 				  kadm5_policy_ent_t pol,
1037*2881Smp153739 				  osa_pw_hist_ent *pw)
10380Sstevel@tonic-gate {
1039*2881Smp153739      osa_pw_hist_ent *histp;
1040*2881Smp153739      int i;
10410Sstevel@tonic-gate 
10420Sstevel@tonic-gate 	/* A history of 1 means just check the current password */
10430Sstevel@tonic-gate 	if (pol->pw_history_num == 1)
10440Sstevel@tonic-gate 		return (0);
10450Sstevel@tonic-gate 
10460Sstevel@tonic-gate 	/* resize the adb->old_keys array if necessary */
10470Sstevel@tonic-gate 	if (adb->old_key_len < pol->pw_history_num-1) {
10480Sstevel@tonic-gate 		if (adb->old_keys == NULL) {
10490Sstevel@tonic-gate 			adb->old_keys = (osa_pw_hist_ent *)
10500Sstevel@tonic-gate 						malloc((adb->old_key_len + 1) *
10510Sstevel@tonic-gate 						sizeof (osa_pw_hist_ent));
10520Sstevel@tonic-gate 		} else {
10530Sstevel@tonic-gate 			adb->old_keys = (osa_pw_hist_ent *)
10540Sstevel@tonic-gate 			realloc(adb->old_keys,
10550Sstevel@tonic-gate 				(adb->old_key_len + 1) *
10560Sstevel@tonic-gate 				sizeof (osa_pw_hist_ent));
10570Sstevel@tonic-gate 		}
10580Sstevel@tonic-gate 		if (adb->old_keys == NULL)
10590Sstevel@tonic-gate 			return (ENOMEM);
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate 		memset(&adb->old_keys[adb->old_key_len], 0,
10620Sstevel@tonic-gate 					sizeof (osa_pw_hist_ent));
10630Sstevel@tonic-gate 		adb->old_key_len++;
10640Sstevel@tonic-gate 		for (i = adb->old_key_len - 1; i > adb->old_key_next; i--)
10650Sstevel@tonic-gate 			adb->old_keys[i] = adb->old_keys[i - 1];
10660Sstevel@tonic-gate 		memset(&adb->old_keys[adb->old_key_next], 0,
10670Sstevel@tonic-gate 					sizeof (osa_pw_hist_ent));
10680Sstevel@tonic-gate 	} else if (adb->old_key_len > pol->pw_history_num-1) {
10690Sstevel@tonic-gate 		/*
10700Sstevel@tonic-gate 		 * The policy must have changed!  Shrink the array.
10710Sstevel@tonic-gate 		 * Can't simply realloc() down, since it might be wrapped.
10720Sstevel@tonic-gate 		 * To understand the arithmetic below, note that we are
10730Sstevel@tonic-gate 		 * copying into new positions 0 .. N-1 from old positions
10740Sstevel@tonic-gate 		 * old_key_next-N .. old_key_next-1, modulo old_key_len,
10750Sstevel@tonic-gate 		 * where N = pw_history_num - 1 is the length of the
10760Sstevel@tonic-gate 		 * shortened list.	Matt Crawford, FNAL
10770Sstevel@tonic-gate 		 */
10780Sstevel@tonic-gate 		int j;
10790Sstevel@tonic-gate 		histp = (osa_pw_hist_ent *)
10800Sstevel@tonic-gate 		malloc((pol->pw_history_num - 1) * sizeof (osa_pw_hist_ent));
10810Sstevel@tonic-gate 		if (histp) {
10820Sstevel@tonic-gate 			for (i = 0; i < pol->pw_history_num - 1; i++) {
10830Sstevel@tonic-gate 				/*
10840Sstevel@tonic-gate 				 * We need the number we use the modulus
10850Sstevel@tonic-gate 				 * operator on to be positive, so after
10860Sstevel@tonic-gate 				 * subtracting pol->pw_history_num-1, we
10870Sstevel@tonic-gate 				 * add back adb->old_key_len.
10880Sstevel@tonic-gate 				 */
10890Sstevel@tonic-gate 				j = KADM_MOD(i - (pol->pw_history_num - 1) +
10900Sstevel@tonic-gate 							adb->old_key_len);
10910Sstevel@tonic-gate 				histp[i] = adb->old_keys[j];
10920Sstevel@tonic-gate 			}
10930Sstevel@tonic-gate 			/* Now free the ones we don't keep (the oldest ones) */
10940Sstevel@tonic-gate 			for (i = 0; i < adb->old_key_len -   \
10950Sstevel@tonic-gate 					(pol->pw_history_num-1); i++) {
10960Sstevel@tonic-gate 				for (j = 0; j <   \
10970Sstevel@tonic-gate 				    adb->old_keys[KADM_MOD(i)].n_key_data; j++)
10980Sstevel@tonic-gate 					krb5_free_key_data_contents(context,
10990Sstevel@tonic-gate 					    &adb->old_keys[KADM_MOD(i)].
11000Sstevel@tonic-gate 					    key_data[j]);
11010Sstevel@tonic-gate 				free(adb->old_keys[KADM_MOD(i)].key_data);
11020Sstevel@tonic-gate 			}
11030Sstevel@tonic-gate 			free((void *)adb->old_keys);
11040Sstevel@tonic-gate 			adb->old_keys = histp;
11050Sstevel@tonic-gate 			adb->old_key_len = pol->pw_history_num - 1;
11060Sstevel@tonic-gate 			adb->old_key_next = 0;
11070Sstevel@tonic-gate 		} else {
11080Sstevel@tonic-gate 			return (ENOMEM);
11090Sstevel@tonic-gate 		}
11100Sstevel@tonic-gate 	}
11110Sstevel@tonic-gate 
11120Sstevel@tonic-gate 	if (adb->old_key_next + 1 > adb->old_key_len)
11130Sstevel@tonic-gate 		adb->old_key_next = 0;
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 	/* free the old pw history entry if it contains data */
11160Sstevel@tonic-gate 	histp = &adb->old_keys[adb->old_key_next];
11170Sstevel@tonic-gate 	for (i = 0; i < histp->n_key_data; i++)
11180Sstevel@tonic-gate 		krb5_free_key_data_contents(context, &histp->key_data[i]);
11190Sstevel@tonic-gate 	free(histp->key_data);
11200Sstevel@tonic-gate 
11210Sstevel@tonic-gate 	/* store the new entry */
11220Sstevel@tonic-gate 	adb->old_keys[adb->old_key_next] = *pw;
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 	/* update the next pointer */
11250Sstevel@tonic-gate 	if (++adb->old_key_next == pol->pw_history_num-1)
11260Sstevel@tonic-gate 		adb->old_key_next = 0;
11270Sstevel@tonic-gate 
11280Sstevel@tonic-gate 	return (0);
11290Sstevel@tonic-gate }
11300Sstevel@tonic-gate #undef KADM_MOD
11310Sstevel@tonic-gate 
11320Sstevel@tonic-gate kadm5_ret_t
11330Sstevel@tonic-gate kadm5_chpass_principal(void *server_handle,
11340Sstevel@tonic-gate 			    krb5_principal principal, char *password)
11350Sstevel@tonic-gate {
11360Sstevel@tonic-gate 	/*
11370Sstevel@tonic-gate 	 * Default to using the new API with the default set of
11380Sstevel@tonic-gate 	 * key/salt combinations.
11390Sstevel@tonic-gate 	 */
1140*2881Smp153739     return
1141*2881Smp153739 	kadm5_chpass_principal_3(server_handle, principal, FALSE,
1142*2881Smp153739 				 0, NULL, password);
11430Sstevel@tonic-gate }
11440Sstevel@tonic-gate 
11450Sstevel@tonic-gate kadm5_ret_t
11460Sstevel@tonic-gate kadm5_chpass_principal_3(void *server_handle,
11470Sstevel@tonic-gate 			 krb5_principal principal, krb5_boolean keepold,
11480Sstevel@tonic-gate 			 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
11490Sstevel@tonic-gate 			 char *password)
11500Sstevel@tonic-gate {
11510Sstevel@tonic-gate     krb5_int32			now;
11520Sstevel@tonic-gate     kadm5_policy_ent_rec	pol;
11530Sstevel@tonic-gate     osa_princ_ent_rec		adb;
11540Sstevel@tonic-gate     krb5_db_entry		kdb, kdb_save;
1155*2881Smp153739     int				ret, ret2, last_pwd, hist_added;
11560Sstevel@tonic-gate     int				have_pol = 0;
11570Sstevel@tonic-gate     kadm5_server_handle_t	handle = server_handle;
11580Sstevel@tonic-gate     osa_pw_hist_ent		hist;
11590Sstevel@tonic-gate 
11600Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
11610Sstevel@tonic-gate 
11620Sstevel@tonic-gate     hist_added = 0;
11630Sstevel@tonic-gate     memset(&hist, 0, sizeof(hist));
11640Sstevel@tonic-gate 
11650Sstevel@tonic-gate     if (principal == NULL || password == NULL)
11660Sstevel@tonic-gate 	return EINVAL;
11670Sstevel@tonic-gate     if ((krb5_principal_compare(handle->context,
11680Sstevel@tonic-gate 				principal, hist_princ)) == TRUE)
11690Sstevel@tonic-gate 	return KADM5_PROTECT_PRINCIPAL;
11700Sstevel@tonic-gate 
11710Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
11720Sstevel@tonic-gate        return(ret);
11730Sstevel@tonic-gate 
11740Sstevel@tonic-gate     /* we are going to need the current keys after the new keys are set */
11750Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
11760Sstevel@tonic-gate 	 kdb_free_entry(handle, &kdb, &adb);
11770Sstevel@tonic-gate 	 return(ret);
11780Sstevel@tonic-gate     }
11790Sstevel@tonic-gate 
11800Sstevel@tonic-gate     if ((adb.aux_attributes & KADM5_POLICY)) {
11810Sstevel@tonic-gate 	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
11820Sstevel@tonic-gate 	     goto done;
11830Sstevel@tonic-gate 	have_pol = 1;
11840Sstevel@tonic-gate     }
11850Sstevel@tonic-gate 
11860Sstevel@tonic-gate     if ((ret = passwd_check(handle, password, adb.aux_attributes &
11870Sstevel@tonic-gate 			    KADM5_POLICY, &pol, principal)))
11880Sstevel@tonic-gate 	 goto done;
11890Sstevel@tonic-gate 
1190*2881Smp153739     ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock,
1191*2881Smp153739 		       n_ks_tuple?ks_tuple:handle->params.keysalts,
1192*2881Smp153739 		       n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1193*2881Smp153739 		       password, 0 /* increment kvno */,
1194*2881Smp153739 		       keepold, &kdb);
1195*2881Smp153739     if (ret)
11960Sstevel@tonic-gate 	goto done;
11970Sstevel@tonic-gate 
11980Sstevel@tonic-gate     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
11990Sstevel@tonic-gate 
1200*2881Smp153739     ret = krb5_timeofday(handle->context, &now);
1201*2881Smp153739     if (ret)
12020Sstevel@tonic-gate 	 goto done;
12030Sstevel@tonic-gate 
12040Sstevel@tonic-gate     if ((adb.aux_attributes & KADM5_POLICY)) {
12050Sstevel@tonic-gate        /* the policy was loaded before */
12060Sstevel@tonic-gate 
1207*2881Smp153739 	ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1208*2881Smp153739 					      &kdb, &last_pwd);
1209*2881Smp153739 	if (ret)
1210*2881Smp153739 	    goto done;
12110Sstevel@tonic-gate 
12120Sstevel@tonic-gate #if 0
12130Sstevel@tonic-gate 	 /*
12140Sstevel@tonic-gate 	  * The spec says this check is overridden if the caller has
12150Sstevel@tonic-gate 	  * modify privilege.  The admin server therefore makes this
12160Sstevel@tonic-gate 	  * check itself (in chpass_principal_wrapper, misc.c). A
12170Sstevel@tonic-gate 	  * local caller implicitly has all authorization bits.
12180Sstevel@tonic-gate 	  */
12190Sstevel@tonic-gate 	if ((now - last_pwd) < pol.pw_min_life &&
12200Sstevel@tonic-gate 	    !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
12210Sstevel@tonic-gate 	     ret = KADM5_PASS_TOOSOON;
12220Sstevel@tonic-gate 	     goto done;
12230Sstevel@tonic-gate 	}
12240Sstevel@tonic-gate #endif
12250Sstevel@tonic-gate 
1226*2881Smp153739 	ret = create_history_entry(handle->context,
1227*2881Smp153739 				   &handle->master_keyblock, kdb_save.n_key_data,
1228*2881Smp153739 				   kdb_save.key_data, &hist);
1229*2881Smp153739 	if (ret)
1230*2881Smp153739 	    goto done;
12310Sstevel@tonic-gate 
1232*2881Smp153739 	ret = check_pw_reuse(handle->context,
1233*2881Smp153739 			     &handle->master_keyblock,
1234*2881Smp153739 			     &hist_key,
1235*2881Smp153739 			     kdb.n_key_data, kdb.key_data,
1236*2881Smp153739 			     1, &hist);
1237*2881Smp153739 	if (ret)
1238*2881Smp153739 	    goto done;
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate 	if (pol.pw_history_num > 1) {
12410Sstevel@tonic-gate 	    if (adb.admin_history_kvno != hist_kvno) {
12420Sstevel@tonic-gate 		ret = KADM5_BAD_HIST_KEY;
12430Sstevel@tonic-gate 		goto done;
12440Sstevel@tonic-gate 	    }
12450Sstevel@tonic-gate 
1246*2881Smp153739 	    ret = check_pw_reuse(handle->context,
12470Sstevel@tonic-gate 				&handle->master_keyblock,
12480Sstevel@tonic-gate 				     &hist_key,
1249*2881Smp153739 				 kdb.n_key_data, kdb.key_data,
1250*2881Smp153739 				 adb.old_key_len, adb.old_keys);
1251*2881Smp153739 	    if (ret)
12520Sstevel@tonic-gate 		goto done;
12530Sstevel@tonic-gate 
1254*2881Smp153739 	    ret = add_to_history(handle->context, &adb, &pol, &hist);
1255*2881Smp153739 	    if (ret)
1256*2881Smp153739 		goto done;
12570Sstevel@tonic-gate 	    hist_added = 1;
12580Sstevel@tonic-gate        }
12590Sstevel@tonic-gate 
12600Sstevel@tonic-gate 	if (pol.pw_max_life)
12610Sstevel@tonic-gate 	   kdb.pw_expiration = now + pol.pw_max_life;
12620Sstevel@tonic-gate 	else
12630Sstevel@tonic-gate 	   kdb.pw_expiration = 0;
12640Sstevel@tonic-gate     } else {
12650Sstevel@tonic-gate 	kdb.pw_expiration = 0;
12660Sstevel@tonic-gate     }
12670Sstevel@tonic-gate 
1268*2881Smp153739     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1269*2881Smp153739     if (ret)
12700Sstevel@tonic-gate 	goto done;
12710Sstevel@tonic-gate 
12720Sstevel@tonic-gate     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
12730Sstevel@tonic-gate 	goto done;
12740Sstevel@tonic-gate 
12750Sstevel@tonic-gate     ret = KADM5_OK;
12760Sstevel@tonic-gate done:
12770Sstevel@tonic-gate     if (!hist_added && hist.key_data)
12780Sstevel@tonic-gate 	 free_history_entry(handle->context, &hist);
12790Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
12800Sstevel@tonic-gate     kdb_free_entry(handle, &kdb_save, NULL);
12810Sstevel@tonic-gate     krb5_dbe_free_contents(handle->context, &kdb);
12820Sstevel@tonic-gate 
12830Sstevel@tonic-gate     if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
12840Sstevel@tonic-gate 	&& !ret)
12850Sstevel@tonic-gate 	 ret = ret2;
12860Sstevel@tonic-gate 
12870Sstevel@tonic-gate     return ret;
12880Sstevel@tonic-gate }
12890Sstevel@tonic-gate 
12900Sstevel@tonic-gate kadm5_ret_t
12910Sstevel@tonic-gate kadm5_randkey_principal(void *server_handle,
12920Sstevel@tonic-gate 			krb5_principal principal,
12930Sstevel@tonic-gate 			krb5_keyblock **keyblocks,
12940Sstevel@tonic-gate 			int *n_keys)
12950Sstevel@tonic-gate {
12960Sstevel@tonic-gate 	krb5_key_salt_tuple keysalts[2];
12970Sstevel@tonic-gate 
12980Sstevel@tonic-gate 	/*
12990Sstevel@tonic-gate 	 * Anyone calling this routine is forced to use only DES
13000Sstevel@tonic-gate 	 * enctypes to be compatible with earlier releases that
13010Sstevel@tonic-gate 	 * did not support stronger crypto.
13020Sstevel@tonic-gate 	 *
13030Sstevel@tonic-gate 	 * S10 (and later) kadmin clients will not use this API,
13040Sstevel@tonic-gate 	 * so we can assume the request is from an older version.
13050Sstevel@tonic-gate 	 */
13060Sstevel@tonic-gate 	keysalts[0].ks_enctype = ENCTYPE_DES_CBC_MD5;
13070Sstevel@tonic-gate 	keysalts[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
13080Sstevel@tonic-gate 	keysalts[1].ks_enctype = ENCTYPE_DES_CBC_CRC;
13090Sstevel@tonic-gate 	keysalts[1].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
13100Sstevel@tonic-gate 
13110Sstevel@tonic-gate 	return (kadm5_randkey_principal_3(server_handle, principal,
13120Sstevel@tonic-gate 			FALSE, 2, keysalts, keyblocks, n_keys));
13130Sstevel@tonic-gate }
13140Sstevel@tonic-gate 
13150Sstevel@tonic-gate kadm5_ret_t
13160Sstevel@tonic-gate kadm5_randkey_principal_3(void *server_handle,
13170Sstevel@tonic-gate 			krb5_principal principal,
13180Sstevel@tonic-gate 			krb5_boolean keepold,
13190Sstevel@tonic-gate 			int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
13200Sstevel@tonic-gate 			krb5_keyblock **keyblocks,
13210Sstevel@tonic-gate 			int *n_keys)
13220Sstevel@tonic-gate {
13230Sstevel@tonic-gate     krb5_db_entry		kdb;
13240Sstevel@tonic-gate     osa_princ_ent_rec		adb;
13250Sstevel@tonic-gate     krb5_int32			now;
13260Sstevel@tonic-gate     kadm5_policy_ent_rec	pol;
13270Sstevel@tonic-gate     krb5_key_data		*key_data;
13280Sstevel@tonic-gate     krb5_keyblock		*keyblock;
13290Sstevel@tonic-gate     int				ret, last_pwd, have_pol = 0;
13300Sstevel@tonic-gate     kadm5_server_handle_t	handle = server_handle;
13310Sstevel@tonic-gate 
13320Sstevel@tonic-gate     if (keyblocks)
13330Sstevel@tonic-gate 	 *keyblocks = NULL;
13340Sstevel@tonic-gate 
13350Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
13360Sstevel@tonic-gate 
13370Sstevel@tonic-gate     if (principal == NULL)
13380Sstevel@tonic-gate 	return EINVAL;
13390Sstevel@tonic-gate     if (hist_princ && /* this will be NULL when initializing the databse */
13400Sstevel@tonic-gate 	((krb5_principal_compare(handle->context,
13410Sstevel@tonic-gate 				 principal, hist_princ)) == TRUE))
13420Sstevel@tonic-gate 	return KADM5_PROTECT_PRINCIPAL;
13430Sstevel@tonic-gate 
13440Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
13450Sstevel@tonic-gate        return(ret);
13460Sstevel@tonic-gate 
1347*2881Smp153739     ret = krb5_dbe_crk(handle->context, &handle->master_keyblock,
1348*2881Smp153739 		       n_ks_tuple?ks_tuple:handle->params.keysalts,
1349*2881Smp153739 		       n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1350*2881Smp153739 		       keepold,
1351*2881Smp153739 		       &kdb);
1352*2881Smp153739     if (ret)
1353*2881Smp153739 	goto done;
13540Sstevel@tonic-gate 
13550Sstevel@tonic-gate     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
13560Sstevel@tonic-gate 
1357*2881Smp153739     ret = krb5_timeofday(handle->context, &now);
1358*2881Smp153739     if (ret)
13590Sstevel@tonic-gate 	goto done;
13600Sstevel@tonic-gate 
13610Sstevel@tonic-gate     if ((adb.aux_attributes & KADM5_POLICY)) {
13620Sstevel@tonic-gate 	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
13630Sstevel@tonic-gate 				    &pol)) != KADM5_OK)
13640Sstevel@tonic-gate 	   goto done;
13650Sstevel@tonic-gate 	have_pol = 1;
13660Sstevel@tonic-gate 
1367*2881Smp153739 	ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1368*2881Smp153739 					      &kdb, &last_pwd);
1369*2881Smp153739 	if (ret)
13700Sstevel@tonic-gate 	     goto done;
13710Sstevel@tonic-gate 
13720Sstevel@tonic-gate #if 0
13730Sstevel@tonic-gate 	 /*
13740Sstevel@tonic-gate 	  * The spec says this check is overridden if the caller has
13750Sstevel@tonic-gate 	  * modify privilege.  The admin server therefore makes this
13760Sstevel@tonic-gate 	  * check itself (in chpass_principal_wrapper, misc.c).  A
13770Sstevel@tonic-gate 	  * local caller implicitly has all authorization bits.
13780Sstevel@tonic-gate 	  */
13790Sstevel@tonic-gate 	if((now - last_pwd) < pol.pw_min_life &&
13800Sstevel@tonic-gate 	   !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
13810Sstevel@tonic-gate 	     ret = KADM5_PASS_TOOSOON;
13820Sstevel@tonic-gate 	     goto done;
13830Sstevel@tonic-gate 	}
13840Sstevel@tonic-gate #endif
13850Sstevel@tonic-gate 
13860Sstevel@tonic-gate 	if(pol.pw_history_num > 1) {
13870Sstevel@tonic-gate 	    if(adb.admin_history_kvno != hist_kvno) {
13880Sstevel@tonic-gate 		ret = KADM5_BAD_HIST_KEY;
13890Sstevel@tonic-gate 		goto done;
13900Sstevel@tonic-gate 	    }
13910Sstevel@tonic-gate 
1392*2881Smp153739 	    ret = check_pw_reuse(handle->context,
1393*2881Smp153739 				 &handle->master_keyblock,
1394*2881Smp153739 				 &hist_key,
1395*2881Smp153739 				 kdb.n_key_data, kdb.key_data,
1396*2881Smp153739 				 adb.old_key_len, adb.old_keys);
1397*2881Smp153739 	    if (ret)
13980Sstevel@tonic-gate 		goto done;
13990Sstevel@tonic-gate 	}
14000Sstevel@tonic-gate 	if (pol.pw_max_life)
14010Sstevel@tonic-gate 	   kdb.pw_expiration = now + pol.pw_max_life;
14020Sstevel@tonic-gate 	else
14030Sstevel@tonic-gate 	   kdb.pw_expiration = 0;
14040Sstevel@tonic-gate     } else {
14050Sstevel@tonic-gate 	kdb.pw_expiration = 0;
14060Sstevel@tonic-gate     }
14070Sstevel@tonic-gate 
1408*2881Smp153739     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1409*2881Smp153739     if (ret)
14100Sstevel@tonic-gate 	 goto done;
14110Sstevel@tonic-gate 
14120Sstevel@tonic-gate     if (keyblocks) {
14130Sstevel@tonic-gate 	 if (handle->api_version == KADM5_API_VERSION_1) {
14140Sstevel@tonic-gate 	      /* Version 1 clients will expect to see a DES_CRC enctype. */
1415*2881Smp153739 	     ret = krb5_dbe_find_enctype(handle->context, &kdb,
1416*2881Smp153739 					 ENCTYPE_DES_CBC_CRC,
1417*2881Smp153739 					 -1, -1, &key_data);
1418*2881Smp153739 	     if (ret)
1419*2881Smp153739 		 goto done;
1420*2881Smp153739 
1421*2881Smp153739 	     ret = decrypt_key_data(handle->context,
14220Sstevel@tonic-gate 				&handle->master_keyblock, 1, key_data,
1423*2881Smp153739 				     keyblocks, NULL);
1424*2881Smp153739 	     if (ret)
1425*2881Smp153739 		 goto done;
14260Sstevel@tonic-gate 	 } else {
1427*2881Smp153739 	     ret = decrypt_key_data(handle->context,
1428*2881Smp153739 				     &handle->master_keyblock,
1429*2881Smp153739 				     kdb.n_key_data, kdb.key_data,
1430*2881Smp153739 				     keyblocks, n_keys);
1431*2881Smp153739 	     if (ret)
1432*2881Smp153739 		 goto done;
14330Sstevel@tonic-gate 	 }
14340Sstevel@tonic-gate     }
14350Sstevel@tonic-gate 
14360Sstevel@tonic-gate     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
14370Sstevel@tonic-gate 	goto done;
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate     ret = KADM5_OK;
14400Sstevel@tonic-gate done:
14410Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
14420Sstevel@tonic-gate     if (have_pol)
14430Sstevel@tonic-gate 	 kadm5_free_policy_ent(handle->lhandle, &pol);
14440Sstevel@tonic-gate 
14450Sstevel@tonic-gate     return ret;
14460Sstevel@tonic-gate }
14470Sstevel@tonic-gate 
14480Sstevel@tonic-gate kadm5_ret_t
14490Sstevel@tonic-gate kadm5_setkey_principal(void *server_handle,
14500Sstevel@tonic-gate 		       krb5_principal principal,
14510Sstevel@tonic-gate 		       krb5_keyblock *keyblocks,
14520Sstevel@tonic-gate 		       int n_keys)
14530Sstevel@tonic-gate {
1454*2881Smp153739     return
1455*2881Smp153739 	kadm5_setkey_principal_3(server_handle, principal,
1456*2881Smp153739 				 FALSE, 0, NULL,
1457*2881Smp153739 				 keyblocks, n_keys);
14580Sstevel@tonic-gate }
14590Sstevel@tonic-gate 
14600Sstevel@tonic-gate kadm5_ret_t
14610Sstevel@tonic-gate kadm5_setkey_principal_3(void *server_handle,
14620Sstevel@tonic-gate 			 krb5_principal principal,
14630Sstevel@tonic-gate 			 krb5_boolean keepold,
14640Sstevel@tonic-gate 			 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
14650Sstevel@tonic-gate 			 krb5_keyblock *keyblocks,
14660Sstevel@tonic-gate 			 int n_keys)
14670Sstevel@tonic-gate {
14680Sstevel@tonic-gate     krb5_db_entry		kdb;
14690Sstevel@tonic-gate     osa_princ_ent_rec		adb;
14700Sstevel@tonic-gate     krb5_int32			now;
14710Sstevel@tonic-gate     kadm5_policy_ent_rec	pol;
14720Sstevel@tonic-gate     krb5_key_data		*old_key_data;
14730Sstevel@tonic-gate     int				n_old_keys;
14740Sstevel@tonic-gate     int				i, j, kvno, ret, last_pwd, have_pol = 0;
14750Sstevel@tonic-gate     kadm5_server_handle_t	handle = server_handle;
14760Sstevel@tonic-gate     krb5_boolean		similar;
14770Sstevel@tonic-gate     krb5_keysalt		keysalt;
14780Sstevel@tonic-gate 
14790Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
14800Sstevel@tonic-gate 
14810Sstevel@tonic-gate     if (principal == NULL || keyblocks == NULL)
14820Sstevel@tonic-gate 	return EINVAL;
14830Sstevel@tonic-gate     if (hist_princ && /* this will be NULL when initializing the databse */
14840Sstevel@tonic-gate 	((krb5_principal_compare(handle->context,
14850Sstevel@tonic-gate 				 principal, hist_princ)) == TRUE))
14860Sstevel@tonic-gate 	return KADM5_PROTECT_PRINCIPAL;
14870Sstevel@tonic-gate 
14880Sstevel@tonic-gate     for (i = 0; i < n_keys; i++) {
14890Sstevel@tonic-gate 	for (j = i+1; j < n_keys; j++) {
1490*2881Smp153739 	    if ((ret = krb5_c_enctype_compare(handle->context,
1491*2881Smp153739 					      keyblocks[i].enctype,
1492*2881Smp153739 					      keyblocks[j].enctype,
1493*2881Smp153739 					      &similar)))
14940Sstevel@tonic-gate 		return(ret);
1495*2881Smp153739 	    if (similar) {
14960Sstevel@tonic-gate 		if (n_ks_tuple) {
14970Sstevel@tonic-gate 		    if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
14980Sstevel@tonic-gate 			return KADM5_SETKEY_DUP_ENCTYPES;
14990Sstevel@tonic-gate 		} else
15000Sstevel@tonic-gate 		    return KADM5_SETKEY_DUP_ENCTYPES;
1501*2881Smp153739 	    }
15020Sstevel@tonic-gate 	}
15030Sstevel@tonic-gate     }
15040Sstevel@tonic-gate 
1505*2881Smp153739     if (n_ks_tuple && n_ks_tuple != n_keys)
15060Sstevel@tonic-gate 	return KADM5_SETKEY3_ETYPE_MISMATCH;
15070Sstevel@tonic-gate 
15080Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
15090Sstevel@tonic-gate        return(ret);
15100Sstevel@tonic-gate 
15110Sstevel@tonic-gate     for (kvno = 0, i=0; i<kdb.n_key_data; i++)
15120Sstevel@tonic-gate 	 if (kdb.key_data[i].key_data_kvno > kvno)
15130Sstevel@tonic-gate 	      kvno = kdb.key_data[i].key_data_kvno;
15140Sstevel@tonic-gate 
15150Sstevel@tonic-gate     if (keepold) {
15160Sstevel@tonic-gate 	old_key_data = kdb.key_data;
15170Sstevel@tonic-gate 	n_old_keys = kdb.n_key_data;
15180Sstevel@tonic-gate     } else {
15190Sstevel@tonic-gate 	if (kdb.key_data != NULL)
15200Sstevel@tonic-gate 	    cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
15210Sstevel@tonic-gate 	n_old_keys = 0;
15220Sstevel@tonic-gate 	old_key_data = NULL;
15230Sstevel@tonic-gate     }
15240Sstevel@tonic-gate 
15250Sstevel@tonic-gate     kdb.key_data = (krb5_key_data*)malloc((n_keys+n_old_keys)
15260Sstevel@tonic-gate 					  *sizeof(krb5_key_data));
15270Sstevel@tonic-gate     if (kdb.key_data == NULL)
15280Sstevel@tonic-gate 	 return ENOMEM;
15290Sstevel@tonic-gate     memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
15300Sstevel@tonic-gate     kdb.n_key_data = 0;
15310Sstevel@tonic-gate 
15320Sstevel@tonic-gate     for (i = 0; i < n_keys; i++) {
15330Sstevel@tonic-gate 	if (n_ks_tuple) {
15340Sstevel@tonic-gate 	    keysalt.type = ks_tuple[i].ks_salttype;
15350Sstevel@tonic-gate 	    keysalt.data.length = 0;
15360Sstevel@tonic-gate 	    keysalt.data.data = NULL;
15370Sstevel@tonic-gate 	    if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
15380Sstevel@tonic-gate 		cleanup_key_data(handle->context, kdb.n_key_data,
15390Sstevel@tonic-gate 				 kdb.key_data);
15400Sstevel@tonic-gate 		return KADM5_SETKEY3_ETYPE_MISMATCH;
15410Sstevel@tonic-gate 	    }
15420Sstevel@tonic-gate 	}
15430Sstevel@tonic-gate 	ret = krb5_dbekd_encrypt_key_data(handle->context,
15440Sstevel@tonic-gate 					  &handle->master_keyblock,
15450Sstevel@tonic-gate 					  &keyblocks[i],
15460Sstevel@tonic-gate 					  n_ks_tuple ? &keysalt : NULL,
15470Sstevel@tonic-gate 					  kvno + 1,
15480Sstevel@tonic-gate 					  &kdb.key_data[i]);
15490Sstevel@tonic-gate 	if (ret) {
15500Sstevel@tonic-gate 	    cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
15510Sstevel@tonic-gate 	    return ret;
15520Sstevel@tonic-gate 	}
15530Sstevel@tonic-gate 	kdb.n_key_data++;
15540Sstevel@tonic-gate     }
15550Sstevel@tonic-gate 
15560Sstevel@tonic-gate     /* copy old key data if necessary */
15570Sstevel@tonic-gate     for (i = 0; i < n_old_keys; i++) {
15580Sstevel@tonic-gate 	kdb.key_data[i+n_keys] = old_key_data[i];
15590Sstevel@tonic-gate 	memset(&old_key_data[i], 0, sizeof (krb5_key_data));
15600Sstevel@tonic-gate 	kdb.n_key_data++;
15610Sstevel@tonic-gate     }
15620Sstevel@tonic-gate     /* assert(kdb.n_key_data == n_keys + n_old_keys) */
15630Sstevel@tonic-gate     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
15640Sstevel@tonic-gate 
1565*2881Smp153739     if ((ret = krb5_timeofday(handle->context, &now)))
15660Sstevel@tonic-gate 	goto done;
15670Sstevel@tonic-gate 
15680Sstevel@tonic-gate     if ((adb.aux_attributes & KADM5_POLICY)) {
15690Sstevel@tonic-gate 	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
15700Sstevel@tonic-gate 				    &pol)) != KADM5_OK)
15710Sstevel@tonic-gate 	   goto done;
15720Sstevel@tonic-gate 	have_pol = 1;
15730Sstevel@tonic-gate 
15740Sstevel@tonic-gate #if 0
15750Sstevel@tonic-gate 	/*
15760Sstevel@tonic-gate 	  * The spec says this check is overridden if the caller has
15770Sstevel@tonic-gate 	  * modify privilege.  The admin server therefore makes this
15780Sstevel@tonic-gate 	  * check itself (in chpass_principal_wrapper, misc.c).  A
15790Sstevel@tonic-gate 	  * local caller implicitly has all authorization bits.
15800Sstevel@tonic-gate 	  */
15810Sstevel@tonic-gate 	if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
15820Sstevel@tonic-gate 						  &kdb, &last_pwd))
15830Sstevel@tonic-gate 	     goto done;
15840Sstevel@tonic-gate 	if((now - last_pwd) < pol.pw_min_life &&
15850Sstevel@tonic-gate 	   !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
15860Sstevel@tonic-gate 	     ret = KADM5_PASS_TOOSOON;
15870Sstevel@tonic-gate 	     goto done;
15880Sstevel@tonic-gate 	}
15890Sstevel@tonic-gate #endif
15900Sstevel@tonic-gate #if 0
15910Sstevel@tonic-gate 	/*
15920Sstevel@tonic-gate 	 * Should we be checking/updating pw history here?
15930Sstevel@tonic-gate 	 */
15940Sstevel@tonic-gate 	if(pol.pw_history_num > 1) {
15950Sstevel@tonic-gate 	    if(adb.admin_history_kvno != hist_kvno) {
15960Sstevel@tonic-gate 		ret = KADM5_BAD_HIST_KEY;
15970Sstevel@tonic-gate 		goto done;
15980Sstevel@tonic-gate 	    }
15990Sstevel@tonic-gate 
16000Sstevel@tonic-gate 	    if (ret = check_pw_reuse(handle->context,
16010Sstevel@tonic-gate 				&handle->master_keyblock,
16020Sstevel@tonic-gate 				     &hist_key,
16030Sstevel@tonic-gate 				     kdb.n_key_data, kdb.key_data,
16040Sstevel@tonic-gate 				     adb.old_key_len, adb.old_keys))
16050Sstevel@tonic-gate 		goto done;
16060Sstevel@tonic-gate 	}
16070Sstevel@tonic-gate #endif
16080Sstevel@tonic-gate 
16090Sstevel@tonic-gate 	if (pol.pw_max_life)
16100Sstevel@tonic-gate 	   kdb.pw_expiration = now + pol.pw_max_life;
16110Sstevel@tonic-gate 	else
16120Sstevel@tonic-gate 	   kdb.pw_expiration = 0;
16130Sstevel@tonic-gate     } else {
16140Sstevel@tonic-gate 	kdb.pw_expiration = 0;
16150Sstevel@tonic-gate     }
16160Sstevel@tonic-gate 
1617*2881Smp153739     if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)))
1618*2881Smp153739         goto done;
16190Sstevel@tonic-gate 
16200Sstevel@tonic-gate     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
16210Sstevel@tonic-gate 	goto done;
16220Sstevel@tonic-gate 
16230Sstevel@tonic-gate     ret = KADM5_OK;
16240Sstevel@tonic-gate done:
16250Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
16260Sstevel@tonic-gate     if (have_pol)
16270Sstevel@tonic-gate 	 kadm5_free_policy_ent(handle->lhandle, &pol);
16280Sstevel@tonic-gate 
16290Sstevel@tonic-gate     return ret;
16300Sstevel@tonic-gate }
16310Sstevel@tonic-gate 
16320Sstevel@tonic-gate /*
16330Sstevel@tonic-gate  * Allocate an array of n_key_data krb5_keyblocks, fill in each
16340Sstevel@tonic-gate  * element with the results of decrypting the nth key in key_data with
16350Sstevel@tonic-gate  * master_keyblock, and if n_keys is not NULL fill it in with the
16360Sstevel@tonic-gate  * number of keys decrypted.
16370Sstevel@tonic-gate  */
16380Sstevel@tonic-gate static int decrypt_key_data(krb5_context context,
1639*2881Smp153739 			    krb5_keyblock *master_keyblock,
1640*2881Smp153739 			    int n_key_data, krb5_key_data *key_data,
1641*2881Smp153739 			    krb5_keyblock **keyblocks, int *n_keys)
16420Sstevel@tonic-gate {
16430Sstevel@tonic-gate      krb5_keyblock *keys;
16440Sstevel@tonic-gate      int ret, i;
16450Sstevel@tonic-gate 
16460Sstevel@tonic-gate      keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
16470Sstevel@tonic-gate      if (keys == NULL)
16480Sstevel@tonic-gate 	  return ENOMEM;
16490Sstevel@tonic-gate      memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
16500Sstevel@tonic-gate 
16510Sstevel@tonic-gate      for (i = 0; i < n_key_data; i++) {
1652*2881Smp153739           ret = krb5_dbekd_decrypt_key_data(context,
1653*2881Smp153739 					    master_keyblock,
1654*2881Smp153739 					    &key_data[i],
1655*2881Smp153739 					    &keys[i], NULL);
1656*2881Smp153739 	  if (ret) {
16570Sstevel@tonic-gate 
16580Sstevel@tonic-gate 	       memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
16590Sstevel@tonic-gate 	       free(keys);
16600Sstevel@tonic-gate 	       return ret;
16610Sstevel@tonic-gate 	  }
16620Sstevel@tonic-gate      }
16630Sstevel@tonic-gate 
16640Sstevel@tonic-gate      *keyblocks = keys;
16650Sstevel@tonic-gate      if (n_keys)
16660Sstevel@tonic-gate 	  *n_keys = n_key_data;
16670Sstevel@tonic-gate 
16680Sstevel@tonic-gate      return 0;
16690Sstevel@tonic-gate }
16700Sstevel@tonic-gate 
16710Sstevel@tonic-gate /*
16720Sstevel@tonic-gate  * Function: kadm5_decrypt_key
16730Sstevel@tonic-gate  *
16740Sstevel@tonic-gate  * Purpose: Retrieves and decrypts a principal key.
16750Sstevel@tonic-gate  *
16760Sstevel@tonic-gate  * Arguments:
16770Sstevel@tonic-gate  *
16780Sstevel@tonic-gate  *	server_handle	(r) kadm5 handle
16790Sstevel@tonic-gate  *	entry		(r) principal retrieved with kadm5_get_principal
16800Sstevel@tonic-gate  *	ktype		(r) enctype to search for, or -1 to ignore
16810Sstevel@tonic-gate  *	stype		(r) salt type to search for, or -1 to ignore
16820Sstevel@tonic-gate  *	kvno		(r) kvno to search for, -1 for max, 0 for max
16830Sstevel@tonic-gate  *			only if it also matches ktype and stype
16840Sstevel@tonic-gate  *	keyblock	(w) keyblock to fill in
16850Sstevel@tonic-gate  *	keysalt		(w) keysalt to fill in, or NULL
16860Sstevel@tonic-gate  *	kvnop		(w) kvno to fill in, or NULL
16870Sstevel@tonic-gate  *
16880Sstevel@tonic-gate  * Effects: Searches the key_data array of entry, which must have been
16890Sstevel@tonic-gate  * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
16900Sstevel@tonic-gate  * find a key with a specified enctype, salt type, and kvno in a
16910Sstevel@tonic-gate  * principal entry.  If not found, return ENOENT.  Otherwise, decrypt
16920Sstevel@tonic-gate  * it with the master key, and return the key in keyblock, the salt
16930Sstevel@tonic-gate  * in salttype, and the key version number in kvno.
16940Sstevel@tonic-gate  *
16950Sstevel@tonic-gate  * If ktype or stype is -1, it is ignored for the search.  If kvno is
16960Sstevel@tonic-gate  * -1, ktype and stype are ignored and the key with the max kvno is
16970Sstevel@tonic-gate  * returned.  If kvno is 0, only the key with the max kvno is returned
16980Sstevel@tonic-gate  * and only if it matches the ktype and stype; otherwise, ENOENT is
16990Sstevel@tonic-gate  * returned.
17000Sstevel@tonic-gate  */
17010Sstevel@tonic-gate kadm5_ret_t kadm5_decrypt_key(void *server_handle,
17020Sstevel@tonic-gate 			      kadm5_principal_ent_t entry, krb5_int32
17030Sstevel@tonic-gate 			      ktype, krb5_int32 stype, krb5_int32
17040Sstevel@tonic-gate 			      kvno, krb5_keyblock *keyblock,
17050Sstevel@tonic-gate 			      krb5_keysalt *keysalt, int *kvnop)
17060Sstevel@tonic-gate {
17070Sstevel@tonic-gate     kadm5_server_handle_t handle = server_handle;
17080Sstevel@tonic-gate     krb5_db_entry dbent;
17090Sstevel@tonic-gate     krb5_key_data *key_data;
17100Sstevel@tonic-gate     int ret;
17110Sstevel@tonic-gate 
17120Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
17130Sstevel@tonic-gate 
17140Sstevel@tonic-gate     if (entry->n_key_data == 0 || entry->key_data == NULL)
17150Sstevel@tonic-gate 	 return EINVAL;
17160Sstevel@tonic-gate 
17170Sstevel@tonic-gate     /* find_enctype only uses these two fields */
17180Sstevel@tonic-gate     dbent.n_key_data = entry->n_key_data;
17190Sstevel@tonic-gate     dbent.key_data = entry->key_data;
1720*2881Smp153739     if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
1721*2881Smp153739 				    stype, kvno, &key_data)))
17220Sstevel@tonic-gate 	 return ret;
17230Sstevel@tonic-gate 
1724*2881Smp153739     if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
1725*2881Smp153739 					   &handle->master_keyblock, key_data,
1726*2881Smp153739 					   keyblock, keysalt)))
17270Sstevel@tonic-gate 	 return ret;
17280Sstevel@tonic-gate 
17290Sstevel@tonic-gate     if (kvnop)
17300Sstevel@tonic-gate 	 *kvnop = key_data->key_data_kvno;
17310Sstevel@tonic-gate 
17320Sstevel@tonic-gate     return KADM5_OK;
17330Sstevel@tonic-gate }
1734