xref: /onnv-gate/usr/src/lib/krb5/kadm5/srv/svr_principal.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3*0Sstevel@tonic-gate  * Use is subject to license terms.
4*0Sstevel@tonic-gate  */
5*0Sstevel@tonic-gate 
6*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
7*0Sstevel@tonic-gate 
8*0Sstevel@tonic-gate /*
9*0Sstevel@tonic-gate  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
10*0Sstevel@tonic-gate  *
11*0Sstevel@tonic-gate  *	Openvision retains the copyright to derivative works of
12*0Sstevel@tonic-gate  *	this source code.  Do *NOT* create a derivative of this
13*0Sstevel@tonic-gate  *	source code before consulting with your legal department.
14*0Sstevel@tonic-gate  *	Do *NOT* integrate *ANY* of this source code into another
15*0Sstevel@tonic-gate  *	product before consulting with your legal department.
16*0Sstevel@tonic-gate  *
17*0Sstevel@tonic-gate  *	For further information, read the top-level Openvision
18*0Sstevel@tonic-gate  *	copyright which is contained in the top-level MIT Kerberos
19*0Sstevel@tonic-gate  *	copyright.
20*0Sstevel@tonic-gate  *
21*0Sstevel@tonic-gate  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
22*0Sstevel@tonic-gate  *
23*0Sstevel@tonic-gate  */
24*0Sstevel@tonic-gate 
25*0Sstevel@tonic-gate 
26*0Sstevel@tonic-gate /*
27*0Sstevel@tonic-gate  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
28*0Sstevel@tonic-gate  *
29*0Sstevel@tonic-gate  * $Header: /cvs/krbdev/krb5/src/lib/kadm5/srv/svr_principal.c,v 1.19 2000/02/27 22:18:16 tlyu Exp $
30*0Sstevel@tonic-gate  */
31*0Sstevel@tonic-gate 
32*0Sstevel@tonic-gate #if !defined(lint) && !defined(__CODECENTER__)
33*0Sstevel@tonic-gate static char *rcsid = "$Header: /cvs/krbdev/krb5/src/lib/kadm5/srv/svr_principal.c,v 1.19 2000/02/27 22:18:16 tlyu Exp $";
34*0Sstevel@tonic-gate #endif
35*0Sstevel@tonic-gate 
36*0Sstevel@tonic-gate #include	<sys/types.h>
37*0Sstevel@tonic-gate #include	<sys/time.h>
38*0Sstevel@tonic-gate #include	<kadm5/admin.h>
39*0Sstevel@tonic-gate #include	"adb.h"
40*0Sstevel@tonic-gate #include	"k5-int.h"
41*0Sstevel@tonic-gate #include	<krb5/kdb.h>
42*0Sstevel@tonic-gate #include	<stdio.h>
43*0Sstevel@tonic-gate #include	<string.h>
44*0Sstevel@tonic-gate #include	"server_internal.h"
45*0Sstevel@tonic-gate #include	<stdarg.h>
46*0Sstevel@tonic-gate #include	<stdlib.h>
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate extern	krb5_principal	    master_princ;
49*0Sstevel@tonic-gate extern	krb5_principal	    hist_princ;
50*0Sstevel@tonic-gate extern	krb5_keyblock	    hist_key;
51*0Sstevel@tonic-gate extern	krb5_db_entry	    master_db;
52*0Sstevel@tonic-gate extern	krb5_db_entry	    hist_db;
53*0Sstevel@tonic-gate extern  krb5_kvno	    hist_kvno;
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate extern	kadm5_ret_t
56*0Sstevel@tonic-gate krb5_free_key_data_contents(krb5_context context, krb5_key_data *key);
57*0Sstevel@tonic-gate 
58*0Sstevel@tonic-gate static int decrypt_key_data(krb5_context context,
59*0Sstevel@tonic-gate 	krb5_keyblock *, int n_key_data, krb5_key_data *key_data,
60*0Sstevel@tonic-gate 	krb5_keyblock **keyblocks, int *n_keys);
61*0Sstevel@tonic-gate 
62*0Sstevel@tonic-gate /*
63*0Sstevel@tonic-gate  * XXX Functions that ought to be in libkrb5.a, but aren't.
64*0Sstevel@tonic-gate  */
65*0Sstevel@tonic-gate kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
66*0Sstevel@tonic-gate    krb5_context context;
67*0Sstevel@tonic-gate    krb5_key_data *from, *to;
68*0Sstevel@tonic-gate {
69*0Sstevel@tonic-gate      int i, idx;
70*0Sstevel@tonic-gate 
71*0Sstevel@tonic-gate      *to = *from;
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate      idx = (from->key_data_ver == 1 ? 1 : 2);
74*0Sstevel@tonic-gate 
75*0Sstevel@tonic-gate      for (i = 0; i < idx; i++) {
76*0Sstevel@tonic-gate        if ( from->key_data_length[i] ) {
77*0Sstevel@tonic-gate 	 to->key_data_contents[i] = malloc(from->key_data_length[i]);
78*0Sstevel@tonic-gate 	 if (to->key_data_contents[i] == NULL) {
79*0Sstevel@tonic-gate 	   for (i = 0; i < idx; i++) {
80*0Sstevel@tonic-gate 	     if (to->key_data_contents[i]) {
81*0Sstevel@tonic-gate 	       memset(to->key_data_contents[i], 0,
82*0Sstevel@tonic-gate 		      to->key_data_length[i]);
83*0Sstevel@tonic-gate 	       free(to->key_data_contents[i]);
84*0Sstevel@tonic-gate 	     }
85*0Sstevel@tonic-gate 	   }
86*0Sstevel@tonic-gate 	   return ENOMEM;
87*0Sstevel@tonic-gate 	 }
88*0Sstevel@tonic-gate 	 memcpy(to->key_data_contents[i], from->key_data_contents[i],
89*0Sstevel@tonic-gate 		from->key_data_length[i]);
90*0Sstevel@tonic-gate        }
91*0Sstevel@tonic-gate      }
92*0Sstevel@tonic-gate      return 0;
93*0Sstevel@tonic-gate }
94*0Sstevel@tonic-gate 
95*0Sstevel@tonic-gate static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
96*0Sstevel@tonic-gate {
97*0Sstevel@tonic-gate      krb5_tl_data *n;
98*0Sstevel@tonic-gate 
99*0Sstevel@tonic-gate      n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
100*0Sstevel@tonic-gate      if (n == NULL)
101*0Sstevel@tonic-gate 	  return NULL;
102*0Sstevel@tonic-gate      n->tl_data_contents = malloc(tl->tl_data_length);
103*0Sstevel@tonic-gate      if (n->tl_data_contents == NULL) {
104*0Sstevel@tonic-gate 	  free(n);
105*0Sstevel@tonic-gate 	  return NULL;
106*0Sstevel@tonic-gate      }
107*0Sstevel@tonic-gate      memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
108*0Sstevel@tonic-gate      n->tl_data_type = tl->tl_data_type;
109*0Sstevel@tonic-gate      n->tl_data_length = tl->tl_data_length;
110*0Sstevel@tonic-gate      n->tl_data_next = NULL;
111*0Sstevel@tonic-gate      return n;
112*0Sstevel@tonic-gate }
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate /* This is in lib/kdb/kdb_cpw.c, but is static */
115*0Sstevel@tonic-gate static void cleanup_key_data(context, count, data)
116*0Sstevel@tonic-gate    krb5_context	  context;
117*0Sstevel@tonic-gate    int			  count;
118*0Sstevel@tonic-gate    krb5_key_data	* data;
119*0Sstevel@tonic-gate {
120*0Sstevel@tonic-gate      int i, j;
121*0Sstevel@tonic-gate 
122*0Sstevel@tonic-gate      for (i = 0; i < count; i++)
123*0Sstevel@tonic-gate 	  for (j = 0; j < data[i].key_data_ver; j++)
124*0Sstevel@tonic-gate 	       if (data[i].key_data_length[j])
125*0Sstevel@tonic-gate 		    free(data[i].key_data_contents[j]);
126*0Sstevel@tonic-gate      free(data);
127*0Sstevel@tonic-gate }
128*0Sstevel@tonic-gate 
129*0Sstevel@tonic-gate kadm5_ret_t
130*0Sstevel@tonic-gate kadm5_create_principal(void *server_handle,
131*0Sstevel@tonic-gate 			    kadm5_principal_ent_t entry, long mask,
132*0Sstevel@tonic-gate 			    char *password)
133*0Sstevel@tonic-gate {
134*0Sstevel@tonic-gate 	/*
135*0Sstevel@tonic-gate 	 * Default to using the new API with the default set of
136*0Sstevel@tonic-gate 	 * key/salt combinations.
137*0Sstevel@tonic-gate 	 */
138*0Sstevel@tonic-gate 	return (kadm5_create_principal_3(server_handle, entry, mask,
139*0Sstevel@tonic-gate 			0, NULL, password));
140*0Sstevel@tonic-gate }
141*0Sstevel@tonic-gate kadm5_ret_t
142*0Sstevel@tonic-gate kadm5_create_principal_3(void *server_handle,
143*0Sstevel@tonic-gate 			 kadm5_principal_ent_t entry, long mask,
144*0Sstevel@tonic-gate 			 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
145*0Sstevel@tonic-gate 			 char *password)
146*0Sstevel@tonic-gate {
147*0Sstevel@tonic-gate     krb5_db_entry		kdb;
148*0Sstevel@tonic-gate     osa_princ_ent_rec		adb;
149*0Sstevel@tonic-gate     kadm5_policy_ent_rec	polent;
150*0Sstevel@tonic-gate     krb5_int32			now;
151*0Sstevel@tonic-gate     krb5_tl_data		*tl_data_orig, *tl_data_tail;
152*0Sstevel@tonic-gate     unsigned int		ret;
153*0Sstevel@tonic-gate     kadm5_server_handle_t handle = server_handle;
154*0Sstevel@tonic-gate 
155*0Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate     /*
158*0Sstevel@tonic-gate      * Argument sanity checking, and opening up the DB
159*0Sstevel@tonic-gate      */
160*0Sstevel@tonic-gate     if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
161*0Sstevel@tonic-gate        (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
162*0Sstevel@tonic-gate        (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
163*0Sstevel@tonic-gate        (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
164*0Sstevel@tonic-gate        (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
165*0Sstevel@tonic-gate        (mask & KADM5_FAIL_AUTH_COUNT))
166*0Sstevel@tonic-gate 	return KADM5_BAD_MASK;
167*0Sstevel@tonic-gate     if((mask & ~ALL_PRINC_MASK))
168*0Sstevel@tonic-gate 	return KADM5_BAD_MASK;
169*0Sstevel@tonic-gate     if (entry == (kadm5_principal_ent_t) NULL || password == NULL)
170*0Sstevel@tonic-gate 	return EINVAL;
171*0Sstevel@tonic-gate 
172*0Sstevel@tonic-gate     /*
173*0Sstevel@tonic-gate      * Check to see if the principal exists
174*0Sstevel@tonic-gate      */
175*0Sstevel@tonic-gate     ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
176*0Sstevel@tonic-gate 
177*0Sstevel@tonic-gate     switch(ret) {
178*0Sstevel@tonic-gate     case KADM5_UNK_PRINC:
179*0Sstevel@tonic-gate 	break;
180*0Sstevel@tonic-gate     case 0:
181*0Sstevel@tonic-gate 	kdb_free_entry(handle, &kdb, &adb);
182*0Sstevel@tonic-gate 	return KADM5_DUP;
183*0Sstevel@tonic-gate     default:
184*0Sstevel@tonic-gate 	return ret;
185*0Sstevel@tonic-gate     }
186*0Sstevel@tonic-gate 
187*0Sstevel@tonic-gate     memset(&kdb, 0, sizeof(krb5_db_entry));
188*0Sstevel@tonic-gate     memset(&adb, 0, sizeof(osa_princ_ent_rec));
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate     /*
191*0Sstevel@tonic-gate      * If a policy was specified, load it.
192*0Sstevel@tonic-gate      * If we can not find the one specified return an error
193*0Sstevel@tonic-gate      */
194*0Sstevel@tonic-gate     if ((mask & KADM5_POLICY)) {
195*0Sstevel@tonic-gate 	 if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
196*0Sstevel@tonic-gate 				     &polent)) != KADM5_OK) {
197*0Sstevel@tonic-gate 	    if(ret == EINVAL)
198*0Sstevel@tonic-gate 		return KADM5_BAD_POLICY;
199*0Sstevel@tonic-gate 	    else
200*0Sstevel@tonic-gate 		return ret;
201*0Sstevel@tonic-gate 	}
202*0Sstevel@tonic-gate     }
203*0Sstevel@tonic-gate     if (ret = passwd_check(handle, password, (mask & KADM5_POLICY),
204*0Sstevel@tonic-gate 			   &polent, entry->principal)) {
205*0Sstevel@tonic-gate 	if (mask & KADM5_POLICY)
206*0Sstevel@tonic-gate 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
207*0Sstevel@tonic-gate 	return ret;
208*0Sstevel@tonic-gate     }
209*0Sstevel@tonic-gate     /*
210*0Sstevel@tonic-gate      * Start populating the various DB fields, using the
211*0Sstevel@tonic-gate      * "defaults" for fields that were not specified by the
212*0Sstevel@tonic-gate      * mask.
213*0Sstevel@tonic-gate      */
214*0Sstevel@tonic-gate     if (ret = krb5_timeofday(handle->context, &now)) {
215*0Sstevel@tonic-gate 	if (mask & KADM5_POLICY)
216*0Sstevel@tonic-gate 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
217*0Sstevel@tonic-gate 	return ret;
218*0Sstevel@tonic-gate     }
219*0Sstevel@tonic-gate 
220*0Sstevel@tonic-gate     kdb.magic = KRB5_KDB_MAGIC_NUMBER;
221*0Sstevel@tonic-gate     kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate     /*
224*0Sstevel@tonic-gate      * If KADM5_ATTRIBUTES is set, we want to rope in not only
225*0Sstevel@tonic-gate      * entry->attributes, but also the generic params.flags
226*0Sstevel@tonic-gate      * obtained previously via kadm5_get_config_params.
227*0Sstevel@tonic-gate      */
228*0Sstevel@tonic-gate     if ((mask & KADM5_ATTRIBUTES)) {
229*0Sstevel@tonic-gate 	kdb.attributes = handle->params.flags;
230*0Sstevel@tonic-gate 	kdb.attributes |= entry->attributes;
231*0Sstevel@tonic-gate     } else {
232*0Sstevel@tonic-gate 	kdb.attributes = handle->params.flags;
233*0Sstevel@tonic-gate     }
234*0Sstevel@tonic-gate 
235*0Sstevel@tonic-gate     if ((mask & KADM5_MAX_LIFE))
236*0Sstevel@tonic-gate 	kdb.max_life = entry->max_life;
237*0Sstevel@tonic-gate     else
238*0Sstevel@tonic-gate 	kdb.max_life = handle->params.max_life;
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate     if (mask & KADM5_MAX_RLIFE)
241*0Sstevel@tonic-gate 	 kdb.max_renewable_life = entry->max_renewable_life;
242*0Sstevel@tonic-gate     else
243*0Sstevel@tonic-gate 	 kdb.max_renewable_life = handle->params.max_rlife;
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate     if ((mask & KADM5_PRINC_EXPIRE_TIME))
246*0Sstevel@tonic-gate 	kdb.expiration = entry->princ_expire_time;
247*0Sstevel@tonic-gate     else
248*0Sstevel@tonic-gate 	kdb.expiration = handle->params.expiration;
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate     kdb.pw_expiration = 0;
251*0Sstevel@tonic-gate     if ((mask & KADM5_POLICY)) {
252*0Sstevel@tonic-gate 	if(polent.pw_max_life)
253*0Sstevel@tonic-gate 	    kdb.pw_expiration = now + polent.pw_max_life;
254*0Sstevel@tonic-gate 	else
255*0Sstevel@tonic-gate 	    kdb.pw_expiration = 0;
256*0Sstevel@tonic-gate     }
257*0Sstevel@tonic-gate     if ((mask & KADM5_PW_EXPIRATION))
258*0Sstevel@tonic-gate 	 kdb.pw_expiration = entry->pw_expiration;
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate     kdb.last_success = 0;
261*0Sstevel@tonic-gate     kdb.last_failed = 0;
262*0Sstevel@tonic-gate     kdb.fail_auth_count = 0;
263*0Sstevel@tonic-gate 
264*0Sstevel@tonic-gate     /* this is kind of gross, but in order to free the tl data, I need
265*0Sstevel@tonic-gate        to free the entire kdb entry, and that will try to free the
266*0Sstevel@tonic-gate        principal. */
267*0Sstevel@tonic-gate 
268*0Sstevel@tonic-gate     if (ret = krb5_copy_principal(handle->context,
269*0Sstevel@tonic-gate 				  entry->principal, &(kdb.princ))) {
270*0Sstevel@tonic-gate 	if (mask & KADM5_POLICY)
271*0Sstevel@tonic-gate 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
272*0Sstevel@tonic-gate 	return(ret);
273*0Sstevel@tonic-gate     }
274*0Sstevel@tonic-gate 
275*0Sstevel@tonic-gate     if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)) {
276*0Sstevel@tonic-gate 	krb5_dbe_free_contents(handle->context, &kdb);
277*0Sstevel@tonic-gate 	if (mask & KADM5_POLICY)
278*0Sstevel@tonic-gate 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
279*0Sstevel@tonic-gate 	return(ret);
280*0Sstevel@tonic-gate     }
281*0Sstevel@tonic-gate 
282*0Sstevel@tonic-gate     /* initialize the keys */
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate     if (ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock,
285*0Sstevel@tonic-gate 			   n_ks_tuple?ks_tuple:handle->params.keysalts,
286*0Sstevel@tonic-gate 			   n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
287*0Sstevel@tonic-gate 			   password,
288*0Sstevel@tonic-gate 			   (mask & KADM5_KVNO)?entry->kvno:1,
289*0Sstevel@tonic-gate 			   FALSE, &kdb)) {
290*0Sstevel@tonic-gate 	krb5_dbe_free_contents(handle->context, &kdb);
291*0Sstevel@tonic-gate 	if (mask & KADM5_POLICY)
292*0Sstevel@tonic-gate 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
293*0Sstevel@tonic-gate 	return(ret);
294*0Sstevel@tonic-gate     }
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate     /* populate the admin-server-specific fields.  In the OV server,
297*0Sstevel@tonic-gate        this used to be in a separate database.  Since there's already
298*0Sstevel@tonic-gate        marshalling code for the admin fields, to keep things simple,
299*0Sstevel@tonic-gate        I'm going to keep it, and make all the admin stuff occupy a
300*0Sstevel@tonic-gate        single tl_data record, */
301*0Sstevel@tonic-gate 
302*0Sstevel@tonic-gate     adb.admin_history_kvno = hist_kvno;
303*0Sstevel@tonic-gate     if ((mask & KADM5_POLICY)) {
304*0Sstevel@tonic-gate 	adb.aux_attributes = KADM5_POLICY;
305*0Sstevel@tonic-gate 
306*0Sstevel@tonic-gate 	/* this does *not* need to be strdup'ed, because adb is xdr */
307*0Sstevel@tonic-gate 	/* encoded in osa_adb_create_princ, and not ever freed */
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate 	adb.policy = entry->policy;
310*0Sstevel@tonic-gate     }
311*0Sstevel@tonic-gate 
312*0Sstevel@tonic-gate     /* increment the policy ref count, if any */
313*0Sstevel@tonic-gate 
314*0Sstevel@tonic-gate     if ((mask & KADM5_POLICY)) {
315*0Sstevel@tonic-gate 	polent.policy_refcnt++;
316*0Sstevel@tonic-gate 	if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
317*0Sstevel@tonic-gate 						    KADM5_REF_COUNT))
318*0Sstevel@tonic-gate 	    != KADM5_OK) {
319*0Sstevel@tonic-gate 	    krb5_dbe_free_contents(handle->context, &kdb);
320*0Sstevel@tonic-gate 	    if (mask & KADM5_POLICY)
321*0Sstevel@tonic-gate 		 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
322*0Sstevel@tonic-gate 	    return(ret);
323*0Sstevel@tonic-gate 	}
324*0Sstevel@tonic-gate     }
325*0Sstevel@tonic-gate 
326*0Sstevel@tonic-gate     if (mask & KADM5_TL_DATA) {
327*0Sstevel@tonic-gate 	 /* splice entry->tl_data onto the front of kdb.tl_data */
328*0Sstevel@tonic-gate 	 tl_data_orig = kdb.tl_data;
329*0Sstevel@tonic-gate 	 for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next;
330*0Sstevel@tonic-gate 	      tl_data_tail = tl_data_tail->tl_data_next)
331*0Sstevel@tonic-gate 	      ;
332*0Sstevel@tonic-gate 	 tl_data_tail->tl_data_next = kdb.tl_data;
333*0Sstevel@tonic-gate 	 kdb.tl_data = entry->tl_data;
334*0Sstevel@tonic-gate     }
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate     /* store the new db entry */
337*0Sstevel@tonic-gate     ret = kdb_put_entry(handle, &kdb, &adb);
338*0Sstevel@tonic-gate 
339*0Sstevel@tonic-gate     if (mask & KADM5_TL_DATA) {
340*0Sstevel@tonic-gate 	 /* remove entry->tl_data from the front of kdb.tl_data */
341*0Sstevel@tonic-gate 	 tl_data_tail->tl_data_next = NULL;
342*0Sstevel@tonic-gate 	 kdb.tl_data = tl_data_orig;
343*0Sstevel@tonic-gate     }
344*0Sstevel@tonic-gate 
345*0Sstevel@tonic-gate     krb5_dbe_free_contents(handle->context, &kdb);
346*0Sstevel@tonic-gate 
347*0Sstevel@tonic-gate     if (ret) {
348*0Sstevel@tonic-gate 	if ((mask & KADM5_POLICY)) {
349*0Sstevel@tonic-gate 	    /* decrement the policy ref count */
350*0Sstevel@tonic-gate 
351*0Sstevel@tonic-gate 	    polent.policy_refcnt--;
352*0Sstevel@tonic-gate 	    /*
353*0Sstevel@tonic-gate 	     * if this fails, there's nothing we can do anyway.  the
354*0Sstevel@tonic-gate 	     * policy refcount wil be too high.
355*0Sstevel@tonic-gate 	     */
356*0Sstevel@tonic-gate 	    (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
357*0Sstevel@tonic-gate 						     KADM5_REF_COUNT);
358*0Sstevel@tonic-gate 	}
359*0Sstevel@tonic-gate 
360*0Sstevel@tonic-gate 	if (mask & KADM5_POLICY)
361*0Sstevel@tonic-gate 	     (void) kadm5_free_policy_ent(handle->lhandle, &polent);
362*0Sstevel@tonic-gate 	return(ret);
363*0Sstevel@tonic-gate     }
364*0Sstevel@tonic-gate 
365*0Sstevel@tonic-gate     if (mask & KADM5_POLICY)
366*0Sstevel@tonic-gate 	 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
367*0Sstevel@tonic-gate 
368*0Sstevel@tonic-gate     return KADM5_OK;
369*0Sstevel@tonic-gate }
370*0Sstevel@tonic-gate 
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate kadm5_ret_t
373*0Sstevel@tonic-gate kadm5_delete_principal(void *server_handle, krb5_principal principal)
374*0Sstevel@tonic-gate {
375*0Sstevel@tonic-gate     unsigned int		ret;
376*0Sstevel@tonic-gate     kadm5_policy_ent_rec	polent;
377*0Sstevel@tonic-gate     krb5_db_entry		kdb;
378*0Sstevel@tonic-gate     osa_princ_ent_rec		adb;
379*0Sstevel@tonic-gate     kadm5_server_handle_t handle = server_handle;
380*0Sstevel@tonic-gate 
381*0Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
382*0Sstevel@tonic-gate 
383*0Sstevel@tonic-gate     if (principal == NULL)
384*0Sstevel@tonic-gate 	return EINVAL;
385*0Sstevel@tonic-gate 
386*0Sstevel@tonic-gate     if (ret = kdb_get_entry(handle, principal, &kdb, &adb))
387*0Sstevel@tonic-gate 	return(ret);
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate     if ((adb.aux_attributes & KADM5_POLICY)) {
390*0Sstevel@tonic-gate 	if ((ret = kadm5_get_policy(handle->lhandle,
391*0Sstevel@tonic-gate 				    adb.policy, &polent))
392*0Sstevel@tonic-gate 	    == KADM5_OK) {
393*0Sstevel@tonic-gate 	    polent.policy_refcnt--;
394*0Sstevel@tonic-gate 	    if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
395*0Sstevel@tonic-gate 							 KADM5_REF_COUNT))
396*0Sstevel@tonic-gate 		!= KADM5_OK) {
397*0Sstevel@tonic-gate 		(void) kadm5_free_policy_ent(handle->lhandle, &polent);
398*0Sstevel@tonic-gate 		kdb_free_entry(handle, &kdb, &adb);
399*0Sstevel@tonic-gate 		return(ret);
400*0Sstevel@tonic-gate 	    }
401*0Sstevel@tonic-gate 	}
402*0Sstevel@tonic-gate 	if (ret = kadm5_free_policy_ent(handle->lhandle, &polent)) {
403*0Sstevel@tonic-gate 	    kdb_free_entry(handle, &kdb, &adb);
404*0Sstevel@tonic-gate 	    return ret;
405*0Sstevel@tonic-gate 	}
406*0Sstevel@tonic-gate     }
407*0Sstevel@tonic-gate 
408*0Sstevel@tonic-gate     ret = kdb_delete_entry(handle, principal);
409*0Sstevel@tonic-gate 
410*0Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
411*0Sstevel@tonic-gate 
412*0Sstevel@tonic-gate     return ret;
413*0Sstevel@tonic-gate }
414*0Sstevel@tonic-gate 
415*0Sstevel@tonic-gate kadm5_ret_t
416*0Sstevel@tonic-gate kadm5_modify_principal(void *server_handle,
417*0Sstevel@tonic-gate 			    kadm5_principal_ent_t entry, long mask)
418*0Sstevel@tonic-gate {
419*0Sstevel@tonic-gate     int			    ret, ret2, i;
420*0Sstevel@tonic-gate     kadm5_policy_ent_rec    npol, opol;
421*0Sstevel@tonic-gate     int			    have_npol = 0, have_opol = 0;
422*0Sstevel@tonic-gate     krb5_db_entry	    kdb;
423*0Sstevel@tonic-gate     krb5_tl_data	    *tl_data_orig, *tl_data_tail;
424*0Sstevel@tonic-gate     osa_princ_ent_rec	    adb;
425*0Sstevel@tonic-gate     kadm5_server_handle_t handle = server_handle;
426*0Sstevel@tonic-gate 
427*0Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
428*0Sstevel@tonic-gate 
429*0Sstevel@tonic-gate     if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
430*0Sstevel@tonic-gate        (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
431*0Sstevel@tonic-gate        (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
432*0Sstevel@tonic-gate        (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
433*0Sstevel@tonic-gate        (mask & KADM5_LAST_FAILED))
434*0Sstevel@tonic-gate 	return KADM5_BAD_MASK;
435*0Sstevel@tonic-gate     if((mask & ~ALL_PRINC_MASK))
436*0Sstevel@tonic-gate 	return KADM5_BAD_MASK;
437*0Sstevel@tonic-gate     if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
438*0Sstevel@tonic-gate 	return KADM5_BAD_MASK;
439*0Sstevel@tonic-gate     if(entry == (kadm5_principal_ent_t) NULL)
440*0Sstevel@tonic-gate 	return EINVAL;
441*0Sstevel@tonic-gate     if (mask & KADM5_TL_DATA) {
442*0Sstevel@tonic-gate 	 tl_data_orig = entry->tl_data;
443*0Sstevel@tonic-gate 	 while (tl_data_orig) {
444*0Sstevel@tonic-gate 	      if (tl_data_orig->tl_data_type < 256)
445*0Sstevel@tonic-gate 		   return KADM5_BAD_TL_TYPE;
446*0Sstevel@tonic-gate 	      tl_data_orig = tl_data_orig->tl_data_next;
447*0Sstevel@tonic-gate 	 }
448*0Sstevel@tonic-gate     }
449*0Sstevel@tonic-gate 
450*0Sstevel@tonic-gate     if (ret = kdb_get_entry(handle, entry->principal, &kdb, &adb))
451*0Sstevel@tonic-gate 	return(ret);
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate     /*
454*0Sstevel@tonic-gate      * This is pretty much the same as create ...
455*0Sstevel@tonic-gate      */
456*0Sstevel@tonic-gate 
457*0Sstevel@tonic-gate     if ((mask & KADM5_POLICY)) {
458*0Sstevel@tonic-gate 	 /* get the new policy */
459*0Sstevel@tonic-gate 	 ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
460*0Sstevel@tonic-gate 	 if (ret) {
461*0Sstevel@tonic-gate 	      switch (ret) {
462*0Sstevel@tonic-gate 	      case EINVAL:
463*0Sstevel@tonic-gate 		   ret = KADM5_BAD_POLICY;
464*0Sstevel@tonic-gate 		   break;
465*0Sstevel@tonic-gate 	      case KADM5_UNK_POLICY:
466*0Sstevel@tonic-gate 	      case KADM5_BAD_POLICY:
467*0Sstevel@tonic-gate 		   ret =  KADM5_UNK_POLICY;
468*0Sstevel@tonic-gate 		   break;
469*0Sstevel@tonic-gate 	      }
470*0Sstevel@tonic-gate 	      goto done;
471*0Sstevel@tonic-gate 	 }
472*0Sstevel@tonic-gate 	 have_npol = 1;
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate 	 /* if we already have a policy, get it to decrement the refcnt */
475*0Sstevel@tonic-gate 	 if(adb.aux_attributes & KADM5_POLICY) {
476*0Sstevel@tonic-gate 	      /* ... but not if the old and new are the same */
477*0Sstevel@tonic-gate 	      if(strcmp(adb.policy, entry->policy)) {
478*0Sstevel@tonic-gate 		   ret = kadm5_get_policy(handle->lhandle,
479*0Sstevel@tonic-gate 					  adb.policy, &opol);
480*0Sstevel@tonic-gate 		   switch(ret) {
481*0Sstevel@tonic-gate 		   case EINVAL:
482*0Sstevel@tonic-gate 		   case KADM5_BAD_POLICY:
483*0Sstevel@tonic-gate 		   case KADM5_UNK_POLICY:
484*0Sstevel@tonic-gate 			break;
485*0Sstevel@tonic-gate 		   case KADM5_OK:
486*0Sstevel@tonic-gate 			have_opol = 1;
487*0Sstevel@tonic-gate 			opol.policy_refcnt--;
488*0Sstevel@tonic-gate 			break;
489*0Sstevel@tonic-gate 		   default:
490*0Sstevel@tonic-gate 			goto done;
491*0Sstevel@tonic-gate 		   }
492*0Sstevel@tonic-gate 		   npol.policy_refcnt++;
493*0Sstevel@tonic-gate 	      }
494*0Sstevel@tonic-gate 	 } else npol.policy_refcnt++;
495*0Sstevel@tonic-gate 
496*0Sstevel@tonic-gate 	 /* set us up to use the new policy */
497*0Sstevel@tonic-gate 	 adb.aux_attributes |= KADM5_POLICY;
498*0Sstevel@tonic-gate 	 if (adb.policy)
499*0Sstevel@tonic-gate 	      free(adb.policy);
500*0Sstevel@tonic-gate 	 adb.policy = strdup(entry->policy);
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate 	 /* set pw_max_life based on new policy */
503*0Sstevel@tonic-gate 	 if (npol.pw_max_life) {
504*0Sstevel@tonic-gate 	      if (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
505*0Sstevel@tonic-gate 							&(kdb.pw_expiration)))
506*0Sstevel@tonic-gate 		   goto done;
507*0Sstevel@tonic-gate 	      kdb.pw_expiration += npol.pw_max_life;
508*0Sstevel@tonic-gate 	 } else {
509*0Sstevel@tonic-gate 	      kdb.pw_expiration = 0;
510*0Sstevel@tonic-gate 	 }
511*0Sstevel@tonic-gate     }
512*0Sstevel@tonic-gate 
513*0Sstevel@tonic-gate     if ((mask & KADM5_POLICY_CLR) &&
514*0Sstevel@tonic-gate 	(adb.aux_attributes & KADM5_POLICY)) {
515*0Sstevel@tonic-gate 	 ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
516*0Sstevel@tonic-gate 	 switch(ret) {
517*0Sstevel@tonic-gate 	 case EINVAL:
518*0Sstevel@tonic-gate 	 case KADM5_BAD_POLICY:
519*0Sstevel@tonic-gate 	 case KADM5_UNK_POLICY:
520*0Sstevel@tonic-gate 	      ret = KADM5_BAD_DB;
521*0Sstevel@tonic-gate 	      goto done;
522*0Sstevel@tonic-gate 	 case KADM5_OK:
523*0Sstevel@tonic-gate 	      have_opol = 1;
524*0Sstevel@tonic-gate 	      if (adb.policy)
525*0Sstevel@tonic-gate 		   free(adb.policy);
526*0Sstevel@tonic-gate 	      adb.policy = NULL;
527*0Sstevel@tonic-gate 	      adb.aux_attributes &= ~KADM5_POLICY;
528*0Sstevel@tonic-gate 	      kdb.pw_expiration = 0;
529*0Sstevel@tonic-gate 	      opol.policy_refcnt--;
530*0Sstevel@tonic-gate 	      break;
531*0Sstevel@tonic-gate 	 default:
532*0Sstevel@tonic-gate 	      goto done;
533*0Sstevel@tonic-gate 	 }
534*0Sstevel@tonic-gate     }
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate     if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) &&
537*0Sstevel@tonic-gate 	(((have_opol) &&
538*0Sstevel@tonic-gate 	  (ret =
539*0Sstevel@tonic-gate 	   kadm5_modify_policy_internal(handle->lhandle, &opol,
540*0Sstevel@tonic-gate 					     KADM5_REF_COUNT))) ||
541*0Sstevel@tonic-gate 	 ((have_npol) &&
542*0Sstevel@tonic-gate 	  (ret =
543*0Sstevel@tonic-gate 	   kadm5_modify_policy_internal(handle->lhandle, &npol,
544*0Sstevel@tonic-gate 					     KADM5_REF_COUNT)))))
545*0Sstevel@tonic-gate 	goto done;
546*0Sstevel@tonic-gate 
547*0Sstevel@tonic-gate     if ((mask & KADM5_ATTRIBUTES))
548*0Sstevel@tonic-gate 	kdb.attributes = entry->attributes;
549*0Sstevel@tonic-gate     if ((mask & KADM5_MAX_LIFE))
550*0Sstevel@tonic-gate 	kdb.max_life = entry->max_life;
551*0Sstevel@tonic-gate     if ((mask & KADM5_PRINC_EXPIRE_TIME))
552*0Sstevel@tonic-gate 	kdb.expiration = entry->princ_expire_time;
553*0Sstevel@tonic-gate     if (mask & KADM5_PW_EXPIRATION)
554*0Sstevel@tonic-gate 	 kdb.pw_expiration = entry->pw_expiration;
555*0Sstevel@tonic-gate     if (mask & KADM5_MAX_RLIFE)
556*0Sstevel@tonic-gate 	 kdb.max_renewable_life = entry->max_renewable_life;
557*0Sstevel@tonic-gate     if (mask & KADM5_FAIL_AUTH_COUNT)
558*0Sstevel@tonic-gate 	 kdb.fail_auth_count = entry->fail_auth_count;
559*0Sstevel@tonic-gate 
560*0Sstevel@tonic-gate     if((mask & KADM5_KVNO)) {
561*0Sstevel@tonic-gate 	 for (i = 0; i < kdb.n_key_data; i++)
562*0Sstevel@tonic-gate 	      kdb.key_data[i].key_data_kvno = entry->kvno;
563*0Sstevel@tonic-gate     }
564*0Sstevel@tonic-gate 
565*0Sstevel@tonic-gate     if (mask & KADM5_TL_DATA) {
566*0Sstevel@tonic-gate 	 krb5_tl_data *tl, *tl2;
567*0Sstevel@tonic-gate 	 /*
568*0Sstevel@tonic-gate 	  * Replace kdb.tl_data with what was passed in.  The
569*0Sstevel@tonic-gate 	  * KRB5_TL_KADM_DATA will be re-added (based on adb) by
570*0Sstevel@tonic-gate 	  * kdb_put_entry, below.
571*0Sstevel@tonic-gate 	  *
572*0Sstevel@tonic-gate 	  * Note that we have to duplicate the passed in tl_data
573*0Sstevel@tonic-gate 	  * before adding it to kdb.  The reason is that kdb_put_entry
574*0Sstevel@tonic-gate 	  * will add its own tl_data entries that we will need to
575*0Sstevel@tonic-gate 	  * free, but we cannot free the caller's tl_data (an
576*0Sstevel@tonic-gate 	  * alternative would be to scan the tl_data after put_entry
577*0Sstevel@tonic-gate 	  * and only free those entries that were not passed in).
578*0Sstevel@tonic-gate 	  */
579*0Sstevel@tonic-gate 	 while (kdb.tl_data) {
580*0Sstevel@tonic-gate 	      tl = kdb.tl_data->tl_data_next;
581*0Sstevel@tonic-gate 	      free(kdb.tl_data->tl_data_contents);
582*0Sstevel@tonic-gate 	      free(kdb.tl_data);
583*0Sstevel@tonic-gate 	      kdb.tl_data = tl;
584*0Sstevel@tonic-gate 	 }
585*0Sstevel@tonic-gate 
586*0Sstevel@tonic-gate 	 kdb.n_tl_data = entry->n_tl_data;
587*0Sstevel@tonic-gate 	 kdb.tl_data = NULL;
588*0Sstevel@tonic-gate 	 tl2 = entry->tl_data;
589*0Sstevel@tonic-gate 	 while (tl2) {
590*0Sstevel@tonic-gate 	      tl = dup_tl_data(tl2);
591*0Sstevel@tonic-gate 	      tl->tl_data_next = kdb.tl_data;
592*0Sstevel@tonic-gate 	      kdb.tl_data = tl;
593*0Sstevel@tonic-gate 	      tl2 = tl2->tl_data_next;
594*0Sstevel@tonic-gate 	 }
595*0Sstevel@tonic-gate     }
596*0Sstevel@tonic-gate 
597*0Sstevel@tonic-gate     ret = kdb_put_entry(handle, &kdb, &adb);
598*0Sstevel@tonic-gate     if (ret) goto done;
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate     ret = KADM5_OK;
601*0Sstevel@tonic-gate done:
602*0Sstevel@tonic-gate     if (have_opol) {
603*0Sstevel@tonic-gate 	 ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
604*0Sstevel@tonic-gate 	 ret = ret ? ret : ret2;
605*0Sstevel@tonic-gate     }
606*0Sstevel@tonic-gate     if (have_npol) {
607*0Sstevel@tonic-gate 	 ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
608*0Sstevel@tonic-gate 	 ret = ret ? ret : ret2;
609*0Sstevel@tonic-gate     }
610*0Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
611*0Sstevel@tonic-gate     return ret;
612*0Sstevel@tonic-gate }
613*0Sstevel@tonic-gate 
614*0Sstevel@tonic-gate kadm5_ret_t
615*0Sstevel@tonic-gate kadm5_rename_principal(void *server_handle,
616*0Sstevel@tonic-gate 			    krb5_principal source, krb5_principal target)
617*0Sstevel@tonic-gate {
618*0Sstevel@tonic-gate     krb5_db_entry	kdb;
619*0Sstevel@tonic-gate     osa_princ_ent_rec	adb;
620*0Sstevel@tonic-gate     int			ret, i;
621*0Sstevel@tonic-gate     kadm5_server_handle_t handle = server_handle;
622*0Sstevel@tonic-gate 
623*0Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
624*0Sstevel@tonic-gate 
625*0Sstevel@tonic-gate     if (source == NULL || target == NULL)
626*0Sstevel@tonic-gate 	return EINVAL;
627*0Sstevel@tonic-gate 
628*0Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
629*0Sstevel@tonic-gate 	kdb_free_entry(handle, &kdb, &adb);
630*0Sstevel@tonic-gate 	return(KADM5_DUP);
631*0Sstevel@tonic-gate     }
632*0Sstevel@tonic-gate 
633*0Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
634*0Sstevel@tonic-gate 	return ret;
635*0Sstevel@tonic-gate 
636*0Sstevel@tonic-gate     /* this is kinda gross, but unavoidable */
637*0Sstevel@tonic-gate 
638*0Sstevel@tonic-gate     for (i=0; i<kdb.n_key_data; i++) {
639*0Sstevel@tonic-gate 	if ((kdb.key_data[i].key_data_ver == 1) ||
640*0Sstevel@tonic-gate 	    (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
641*0Sstevel@tonic-gate 	    ret = KADM5_NO_RENAME_SALT;
642*0Sstevel@tonic-gate 	    goto done;
643*0Sstevel@tonic-gate 	}
644*0Sstevel@tonic-gate     }
645*0Sstevel@tonic-gate 
646*0Sstevel@tonic-gate     krb5_free_principal(handle->context, kdb.princ);
647*0Sstevel@tonic-gate     if (ret = krb5_copy_principal(handle->context, target, &kdb.princ)) {
648*0Sstevel@tonic-gate 	kdb.princ = NULL; /* so freeing the dbe doesn't lose */
649*0Sstevel@tonic-gate 	goto done;
650*0Sstevel@tonic-gate     }
651*0Sstevel@tonic-gate 
652*0Sstevel@tonic-gate     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
653*0Sstevel@tonic-gate 	goto done;
654*0Sstevel@tonic-gate 
655*0Sstevel@tonic-gate     ret = kdb_delete_entry(handle, source);
656*0Sstevel@tonic-gate 
657*0Sstevel@tonic-gate done:
658*0Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
659*0Sstevel@tonic-gate     return ret;
660*0Sstevel@tonic-gate }
661*0Sstevel@tonic-gate 
662*0Sstevel@tonic-gate kadm5_ret_t
663*0Sstevel@tonic-gate kadm5_get_principal(void *server_handle, krb5_principal principal,
664*0Sstevel@tonic-gate 		    kadm5_principal_ent_t entry,
665*0Sstevel@tonic-gate 		    long in_mask)
666*0Sstevel@tonic-gate {
667*0Sstevel@tonic-gate     krb5_db_entry		kdb;
668*0Sstevel@tonic-gate     osa_princ_ent_rec		adb;
669*0Sstevel@tonic-gate     osa_adb_ret_t		ret = 0;
670*0Sstevel@tonic-gate     long			mask;
671*0Sstevel@tonic-gate     int i;
672*0Sstevel@tonic-gate     kadm5_server_handle_t handle = server_handle;
673*0Sstevel@tonic-gate     kadm5_principal_ent_rec	entry_local, *entry_orig;
674*0Sstevel@tonic-gate 
675*0Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
676*0Sstevel@tonic-gate 
677*0Sstevel@tonic-gate     /*
678*0Sstevel@tonic-gate      * In version 1, all the defined fields are always returned.
679*0Sstevel@tonic-gate      * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
680*0Sstevel@tonic-gate      * filled with allocated memory.
681*0Sstevel@tonic-gate      */
682*0Sstevel@tonic-gate     if (handle->api_version == KADM5_API_VERSION_1) {
683*0Sstevel@tonic-gate 	 mask = KADM5_PRINCIPAL_NORMAL_MASK;
684*0Sstevel@tonic-gate 	 entry_orig = entry;
685*0Sstevel@tonic-gate 	 entry = &entry_local;
686*0Sstevel@tonic-gate     } else {
687*0Sstevel@tonic-gate 	 mask = in_mask;
688*0Sstevel@tonic-gate     }
689*0Sstevel@tonic-gate 
690*0Sstevel@tonic-gate     memset((char *) entry, 0, sizeof(*entry));
691*0Sstevel@tonic-gate 
692*0Sstevel@tonic-gate     if (principal == NULL)
693*0Sstevel@tonic-gate 	return EINVAL;
694*0Sstevel@tonic-gate 
695*0Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
696*0Sstevel@tonic-gate 	return ret;
697*0Sstevel@tonic-gate 
698*0Sstevel@tonic-gate     if ((mask & KADM5_POLICY) &&
699*0Sstevel@tonic-gate 	adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
700*0Sstevel@tonic-gate 	if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) {
701*0Sstevel@tonic-gate 	    ret = ENOMEM;
702*0Sstevel@tonic-gate 	    goto done;
703*0Sstevel@tonic-gate 	}
704*0Sstevel@tonic-gate 	strcpy(entry->policy, adb.policy);
705*0Sstevel@tonic-gate     }
706*0Sstevel@tonic-gate 
707*0Sstevel@tonic-gate     if (mask & KADM5_AUX_ATTRIBUTES)
708*0Sstevel@tonic-gate 	 entry->aux_attributes = adb.aux_attributes;
709*0Sstevel@tonic-gate 
710*0Sstevel@tonic-gate     if ((mask & KADM5_PRINCIPAL) &&
711*0Sstevel@tonic-gate 	(ret = krb5_copy_principal(handle->context, principal,
712*0Sstevel@tonic-gate 				   &entry->principal))) {
713*0Sstevel@tonic-gate 	goto done;
714*0Sstevel@tonic-gate     }
715*0Sstevel@tonic-gate 
716*0Sstevel@tonic-gate     if (mask & KADM5_PRINC_EXPIRE_TIME)
717*0Sstevel@tonic-gate 	 entry->princ_expire_time = kdb.expiration;
718*0Sstevel@tonic-gate 
719*0Sstevel@tonic-gate     if ((mask & KADM5_LAST_PWD_CHANGE) &&
720*0Sstevel@tonic-gate 	(ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
721*0Sstevel@tonic-gate 					       &(entry->last_pwd_change)))) {
722*0Sstevel@tonic-gate 	goto done;
723*0Sstevel@tonic-gate     }
724*0Sstevel@tonic-gate 
725*0Sstevel@tonic-gate     if (mask & KADM5_PW_EXPIRATION)
726*0Sstevel@tonic-gate 	 entry->pw_expiration = kdb.pw_expiration;
727*0Sstevel@tonic-gate     if (mask & KADM5_MAX_LIFE)
728*0Sstevel@tonic-gate 	 entry->max_life = kdb.max_life;
729*0Sstevel@tonic-gate 
730*0Sstevel@tonic-gate     /* this is a little non-sensical because the function returns two */
731*0Sstevel@tonic-gate     /* values that must be checked separately against the mask */
732*0Sstevel@tonic-gate     if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
733*0Sstevel@tonic-gate 	 if (ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
734*0Sstevel@tonic-gate 						  &(entry->mod_date),
735*0Sstevel@tonic-gate 						  &(entry->mod_name))) {
736*0Sstevel@tonic-gate 	      goto done;
737*0Sstevel@tonic-gate 	 }
738*0Sstevel@tonic-gate 	 if (! (mask & KADM5_MOD_TIME))
739*0Sstevel@tonic-gate 	      entry->mod_date = 0;
740*0Sstevel@tonic-gate 	 if (! (mask & KADM5_MOD_NAME)) {
741*0Sstevel@tonic-gate 	      krb5_free_principal(handle->context, entry->principal);
742*0Sstevel@tonic-gate 	      entry->principal = NULL;
743*0Sstevel@tonic-gate 	 }
744*0Sstevel@tonic-gate     }
745*0Sstevel@tonic-gate 
746*0Sstevel@tonic-gate     if (mask & KADM5_ATTRIBUTES)
747*0Sstevel@tonic-gate 	 entry->attributes = kdb.attributes;
748*0Sstevel@tonic-gate 
749*0Sstevel@tonic-gate     if (mask & KADM5_KVNO)
750*0Sstevel@tonic-gate 	 for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
751*0Sstevel@tonic-gate 	      if (kdb.key_data[i].key_data_kvno > entry->kvno)
752*0Sstevel@tonic-gate 		   entry->kvno = kdb.key_data[i].key_data_kvno;
753*0Sstevel@tonic-gate 
754*0Sstevel@tonic-gate     if (handle->api_version == KADM5_API_VERSION_2)
755*0Sstevel@tonic-gate 	 entry->mkvno = 0;
756*0Sstevel@tonic-gate     else {
757*0Sstevel@tonic-gate 	 /* XXX I'll be damned if I know how to deal with this one --marc */
758*0Sstevel@tonic-gate 	 entry->mkvno = 1;
759*0Sstevel@tonic-gate     }
760*0Sstevel@tonic-gate 
761*0Sstevel@tonic-gate     /*
762*0Sstevel@tonic-gate      * The new fields that only exist in version 2 start here
763*0Sstevel@tonic-gate      */
764*0Sstevel@tonic-gate     if (handle->api_version == KADM5_API_VERSION_2) {
765*0Sstevel@tonic-gate 	 if (mask & KADM5_MAX_RLIFE)
766*0Sstevel@tonic-gate 	      entry->max_renewable_life = kdb.max_renewable_life;
767*0Sstevel@tonic-gate 	 if (mask & KADM5_LAST_SUCCESS)
768*0Sstevel@tonic-gate 	      entry->last_success = kdb.last_success;
769*0Sstevel@tonic-gate 	 if (mask & KADM5_LAST_FAILED)
770*0Sstevel@tonic-gate 	      entry->last_failed = kdb.last_failed;
771*0Sstevel@tonic-gate 	 if (mask & KADM5_FAIL_AUTH_COUNT)
772*0Sstevel@tonic-gate 	      entry->fail_auth_count = kdb.fail_auth_count;
773*0Sstevel@tonic-gate 	 if (mask & KADM5_TL_DATA) {
774*0Sstevel@tonic-gate 	      krb5_tl_data td, *tl, *tl2;
775*0Sstevel@tonic-gate 
776*0Sstevel@tonic-gate 	      entry->tl_data = NULL;
777*0Sstevel@tonic-gate 
778*0Sstevel@tonic-gate 	      tl = kdb.tl_data;
779*0Sstevel@tonic-gate 	      while (tl) {
780*0Sstevel@tonic-gate 		   if (tl->tl_data_type > 255) {
781*0Sstevel@tonic-gate 			if ((tl2 = dup_tl_data(tl)) == NULL) {
782*0Sstevel@tonic-gate 			     ret = ENOMEM;
783*0Sstevel@tonic-gate 			     goto done;
784*0Sstevel@tonic-gate 			}
785*0Sstevel@tonic-gate 			tl2->tl_data_next = entry->tl_data;
786*0Sstevel@tonic-gate 			entry->tl_data = tl2;
787*0Sstevel@tonic-gate 			entry->n_tl_data++;
788*0Sstevel@tonic-gate 		   }
789*0Sstevel@tonic-gate 
790*0Sstevel@tonic-gate 		   tl = tl->tl_data_next;
791*0Sstevel@tonic-gate 	      }
792*0Sstevel@tonic-gate 	 }
793*0Sstevel@tonic-gate 	 if (mask & KADM5_KEY_DATA) {
794*0Sstevel@tonic-gate 	      entry->n_key_data = kdb.n_key_data;
795*0Sstevel@tonic-gate 	      if(entry->n_key_data) {
796*0Sstevel@tonic-gate 		      entry->key_data = (krb5_key_data *)
797*0Sstevel@tonic-gate 			      malloc(entry->n_key_data*sizeof(krb5_key_data));
798*0Sstevel@tonic-gate 		      if (entry->key_data == NULL) {
799*0Sstevel@tonic-gate 			      ret = ENOMEM;
800*0Sstevel@tonic-gate 			      goto done;
801*0Sstevel@tonic-gate 		      }
802*0Sstevel@tonic-gate 	      } else
803*0Sstevel@tonic-gate 		      entry->key_data = NULL;
804*0Sstevel@tonic-gate 
805*0Sstevel@tonic-gate 	      for (i = 0; i < entry->n_key_data; i++)
806*0Sstevel@tonic-gate 		   if (ret = krb5_copy_key_data_contents(handle->context,
807*0Sstevel@tonic-gate 							 &kdb.key_data[i],
808*0Sstevel@tonic-gate 							 &entry->key_data[i]))
809*0Sstevel@tonic-gate 			goto done;
810*0Sstevel@tonic-gate 	 }
811*0Sstevel@tonic-gate     }
812*0Sstevel@tonic-gate 
813*0Sstevel@tonic-gate     /*
814*0Sstevel@tonic-gate      * If KADM5_API_VERSION_1, we return an allocated structure, and
815*0Sstevel@tonic-gate      * we need to convert the new structure back into the format the
816*0Sstevel@tonic-gate      * caller is expecting.
817*0Sstevel@tonic-gate      */
818*0Sstevel@tonic-gate     if (handle->api_version == KADM5_API_VERSION_1) {
819*0Sstevel@tonic-gate 	 kadm5_principal_ent_t_v1 newv1;
820*0Sstevel@tonic-gate 
821*0Sstevel@tonic-gate 	 newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
822*0Sstevel@tonic-gate 	 if (newv1 == NULL) {
823*0Sstevel@tonic-gate 	      ret = ENOMEM;
824*0Sstevel@tonic-gate 	      goto done;
825*0Sstevel@tonic-gate 	 }
826*0Sstevel@tonic-gate 
827*0Sstevel@tonic-gate 	 newv1->principal = entry->principal;
828*0Sstevel@tonic-gate 	 newv1->princ_expire_time = entry->princ_expire_time;
829*0Sstevel@tonic-gate 	 newv1->last_pwd_change = entry->last_pwd_change;
830*0Sstevel@tonic-gate 	 newv1->pw_expiration = entry->pw_expiration;
831*0Sstevel@tonic-gate 	 newv1->max_life = entry->max_life;
832*0Sstevel@tonic-gate 	 newv1->mod_name = entry->mod_name;
833*0Sstevel@tonic-gate 	 newv1->mod_date = entry->mod_date;
834*0Sstevel@tonic-gate 	 newv1->attributes = entry->attributes;
835*0Sstevel@tonic-gate 	 newv1->kvno = entry->kvno;
836*0Sstevel@tonic-gate 	 newv1->mkvno = entry->mkvno;
837*0Sstevel@tonic-gate 	 newv1->policy = entry->policy;
838*0Sstevel@tonic-gate 	 newv1->aux_attributes = entry->aux_attributes;
839*0Sstevel@tonic-gate 
840*0Sstevel@tonic-gate 	 *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
841*0Sstevel@tonic-gate     }
842*0Sstevel@tonic-gate 
843*0Sstevel@tonic-gate     ret = KADM5_OK;
844*0Sstevel@tonic-gate 
845*0Sstevel@tonic-gate done:
846*0Sstevel@tonic-gate     if (ret && entry->principal)
847*0Sstevel@tonic-gate 	 krb5_free_principal(handle->context, entry->principal);
848*0Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
849*0Sstevel@tonic-gate 
850*0Sstevel@tonic-gate     return ret;
851*0Sstevel@tonic-gate }
852*0Sstevel@tonic-gate 
853*0Sstevel@tonic-gate /*
854*0Sstevel@tonic-gate  * Function: check_pw_reuse
855*0Sstevel@tonic-gate  *
856*0Sstevel@tonic-gate  * Purpose: Check if a key appears in a list of keys, in order to
857*0Sstevel@tonic-gate  * enforce password history.
858*0Sstevel@tonic-gate  *
859*0Sstevel@tonic-gate  * Arguments:
860*0Sstevel@tonic-gate  *
861*0Sstevel@tonic-gate  *	context			(r) the krb5 context
862*0Sstevel@tonic-gate  *	hist_keyblock		(r) the key that hist_key_data is
863*0Sstevel@tonic-gate  *				encrypted in
864*0Sstevel@tonic-gate  *	n_new_key_data		(r) length of new_key_data
865*0Sstevel@tonic-gate  *	new_key_data		(r) keys to check against
866*0Sstevel@tonic-gate  *				pw_hist_data, encrypted in hist_keyblock
867*0Sstevel@tonic-gate  *	n_pw_hist_data		(r) length of pw_hist_data
868*0Sstevel@tonic-gate  *	pw_hist_data		(r) passwords to check new_key_data against
869*0Sstevel@tonic-gate  *
870*0Sstevel@tonic-gate  * Effects:
871*0Sstevel@tonic-gate  * For each new_key in new_key_data:
872*0Sstevel@tonic-gate  * 	decrypt new_key with the master_keyblock
873*0Sstevel@tonic-gate  * 	for each password in pw_hist_data:
874*0Sstevel@tonic-gate  *		for each hist_key in password:
875*0Sstevel@tonic-gate  *			decrypt hist_key with hist_keyblock
876*0Sstevel@tonic-gate  *			compare the new_key and hist_key
877*0Sstevel@tonic-gate  *
878*0Sstevel@tonic-gate  * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
879*0Sstevel@tonic-gate  * new_key_data is the same as a key in pw_hist_data, or 0.
880*0Sstevel@tonic-gate  */
881*0Sstevel@tonic-gate static kadm5_ret_t
882*0Sstevel@tonic-gate check_pw_reuse(krb5_context context,
883*0Sstevel@tonic-gate 	       krb5_keyblock *master_keyblock,
884*0Sstevel@tonic-gate 	       krb5_keyblock *hist_keyblock,
885*0Sstevel@tonic-gate 	       int n_new_key_data, krb5_key_data *new_key_data,
886*0Sstevel@tonic-gate 	       int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
887*0Sstevel@tonic-gate {
888*0Sstevel@tonic-gate     int x, y, z;
889*0Sstevel@tonic-gate     krb5_keyblock newkey, histkey;
890*0Sstevel@tonic-gate     krb5_error_code ret;
891*0Sstevel@tonic-gate 
892*0Sstevel@tonic-gate     for (x = 0; x < n_new_key_data; x++) {
893*0Sstevel@tonic-gate 	 if (ret = krb5_dbekd_decrypt_key_data(context,
894*0Sstevel@tonic-gate 			master_keyblock,
895*0Sstevel@tonic-gate 			&(new_key_data[x]),
896*0Sstevel@tonic-gate 			&newkey, NULL))
897*0Sstevel@tonic-gate 	    return(ret);
898*0Sstevel@tonic-gate 	for (y = 0; y < n_pw_hist_data; y++) {
899*0Sstevel@tonic-gate 	     for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
900*0Sstevel@tonic-gate 		  if (ret =
901*0Sstevel@tonic-gate 		      krb5_dbekd_decrypt_key_data(context,
902*0Sstevel@tonic-gate 						  hist_keyblock,
903*0Sstevel@tonic-gate 						  &pw_hist_data[y].key_data[z],
904*0Sstevel@tonic-gate 						  &histkey, NULL))
905*0Sstevel@tonic-gate 		       return(ret);
906*0Sstevel@tonic-gate 
907*0Sstevel@tonic-gate 		  if ((newkey.length == histkey.length) &&
908*0Sstevel@tonic-gate 		      (newkey.enctype == histkey.enctype) &&
909*0Sstevel@tonic-gate 		      (memcmp(newkey.contents, histkey.contents,
910*0Sstevel@tonic-gate 			      histkey.length) == 0)) {
911*0Sstevel@tonic-gate 		       krb5_free_keyblock_contents(context, &histkey);
912*0Sstevel@tonic-gate 		       krb5_free_keyblock_contents(context, &newkey);
913*0Sstevel@tonic-gate 
914*0Sstevel@tonic-gate 		       return(KADM5_PASS_REUSE);
915*0Sstevel@tonic-gate 		  }
916*0Sstevel@tonic-gate 		  krb5_free_keyblock_contents(context, &histkey);
917*0Sstevel@tonic-gate 	     }
918*0Sstevel@tonic-gate 	}
919*0Sstevel@tonic-gate 	krb5_free_keyblock_contents(context, &newkey);
920*0Sstevel@tonic-gate     }
921*0Sstevel@tonic-gate 
922*0Sstevel@tonic-gate     return(0);
923*0Sstevel@tonic-gate }
924*0Sstevel@tonic-gate 
925*0Sstevel@tonic-gate /*
926*0Sstevel@tonic-gate  * Function: create_history_entry
927*0Sstevel@tonic-gate  *
928*0Sstevel@tonic-gate  * Purpose: Creates a password history entry from an array of
929*0Sstevel@tonic-gate  * key_data.
930*0Sstevel@tonic-gate  *
931*0Sstevel@tonic-gate  * Arguments:
932*0Sstevel@tonic-gate  *
933*0Sstevel@tonic-gate  *	context		(r) krb5_context to use
934*0Sstevel@tonic-gate  *      master_keyblcok (r) master key block
935*0Sstevel@tonic-gate  *	n_key_data	(r) number of elements in key_data
936*0Sstevel@tonic-gate  *	key_data	(r) keys to add to the history entry
937*0Sstevel@tonic-gate  *	hist		(w) history entry to fill in
938*0Sstevel@tonic-gate  *
939*0Sstevel@tonic-gate  * Effects:
940*0Sstevel@tonic-gate  *
941*0Sstevel@tonic-gate  * hist->key_data is allocated to store n_key_data key_datas.  Each
942*0Sstevel@tonic-gate  * element of key_data is decrypted with master_keyblock, re-encrypted
943*0Sstevel@tonic-gate  * in hist_key, and added to hist->key_data.  hist->n_key_data is
944*0Sstevel@tonic-gate  * set to n_key_data.
945*0Sstevel@tonic-gate  */
946*0Sstevel@tonic-gate static
947*0Sstevel@tonic-gate int create_history_entry(krb5_context context,
948*0Sstevel@tonic-gate 	krb5_keyblock *master_keyblock,	int n_key_data,
949*0Sstevel@tonic-gate 	krb5_key_data *key_data, osa_pw_hist_ent *hist)
950*0Sstevel@tonic-gate {
951*0Sstevel@tonic-gate      int i, ret;
952*0Sstevel@tonic-gate      krb5_keyblock key;
953*0Sstevel@tonic-gate      krb5_keysalt salt;
954*0Sstevel@tonic-gate 
955*0Sstevel@tonic-gate      hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
956*0Sstevel@tonic-gate      if (hist->key_data == NULL)
957*0Sstevel@tonic-gate 	  return ENOMEM;
958*0Sstevel@tonic-gate      memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
959*0Sstevel@tonic-gate 
960*0Sstevel@tonic-gate      for (i = 0; i < n_key_data; i++) {
961*0Sstevel@tonic-gate 	  if (ret = krb5_dbekd_decrypt_key_data(context,
962*0Sstevel@tonic-gate 						master_keyblock,
963*0Sstevel@tonic-gate 						&key_data[i],
964*0Sstevel@tonic-gate 						&key, &salt))
965*0Sstevel@tonic-gate 	       return ret;
966*0Sstevel@tonic-gate 	  if (ret = krb5_dbekd_encrypt_key_data(context,
967*0Sstevel@tonic-gate 						&hist_key,
968*0Sstevel@tonic-gate 						&key, &salt,
969*0Sstevel@tonic-gate 						key_data[i].key_data_kvno,
970*0Sstevel@tonic-gate 						&hist->key_data[i]))
971*0Sstevel@tonic-gate 	       return ret;
972*0Sstevel@tonic-gate 	  krb5_free_keyblock_contents(context, &key);
973*0Sstevel@tonic-gate 	  /* krb5_free_keysalt(context, &salt); */
974*0Sstevel@tonic-gate      }
975*0Sstevel@tonic-gate 
976*0Sstevel@tonic-gate      hist->n_key_data = n_key_data;
977*0Sstevel@tonic-gate      return 0;
978*0Sstevel@tonic-gate }
979*0Sstevel@tonic-gate 
980*0Sstevel@tonic-gate void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
981*0Sstevel@tonic-gate {
982*0Sstevel@tonic-gate      int i;
983*0Sstevel@tonic-gate 
984*0Sstevel@tonic-gate      for (i = 0; i < hist->n_key_data; i++)
985*0Sstevel@tonic-gate 	  krb5_free_key_data_contents(context, &hist->key_data[i]);
986*0Sstevel@tonic-gate      free(hist->key_data);
987*0Sstevel@tonic-gate }
988*0Sstevel@tonic-gate 
989*0Sstevel@tonic-gate /*
990*0Sstevel@tonic-gate  * Function: add_to_history
991*0Sstevel@tonic-gate  *
992*0Sstevel@tonic-gate  * Purpose: Adds a password to a principal's password history.
993*0Sstevel@tonic-gate  *
994*0Sstevel@tonic-gate  * Arguments:
995*0Sstevel@tonic-gate  *
996*0Sstevel@tonic-gate  *	context		(r) krb5_context to use
997*0Sstevel@tonic-gate  *	adb		(r/w) admin principal entry to add keys to
998*0Sstevel@tonic-gate  *	pol		(r) adb's policy
999*0Sstevel@tonic-gate  *	pw		(r) keys for the password to add to adb's key history
1000*0Sstevel@tonic-gate  *
1001*0Sstevel@tonic-gate  * Effects:
1002*0Sstevel@tonic-gate  *
1003*0Sstevel@tonic-gate  * add_to_history adds a single password to adb's password history.
1004*0Sstevel@tonic-gate  * pw contains n_key_data keys in its key_data, in storage should be
1005*0Sstevel@tonic-gate  * allocated but not freed by the caller (XXX blech!).
1006*0Sstevel@tonic-gate  *
1007*0Sstevel@tonic-gate  * This function maintains adb->old_keys as a circular queue.  It
1008*0Sstevel@tonic-gate  * starts empty, and grows each time this function is called until it
1009*0Sstevel@tonic-gate  * is pol->pw_history_num items long.  adb->old_key_len holds the
1010*0Sstevel@tonic-gate  * number of allocated entries in the array, and must therefore be [0,
1011*0Sstevel@tonic-gate  * pol->pw_history_num).  adb->old_key_next is the index into the
1012*0Sstevel@tonic-gate  * array where the next element should be written, and must be [0,
1013*0Sstevel@tonic-gate  * adb->old_key_len).
1014*0Sstevel@tonic-gate  */
1015*0Sstevel@tonic-gate #define	KADM_MOD(x)	(x + adb->old_key_next) % adb->old_key_len
1016*0Sstevel@tonic-gate static kadm5_ret_t add_to_history(
1017*0Sstevel@tonic-gate 					krb5_context context,
1018*0Sstevel@tonic-gate 					osa_princ_ent_t adb,
1019*0Sstevel@tonic-gate 					kadm5_policy_ent_t pol,
1020*0Sstevel@tonic-gate 					osa_pw_hist_ent *pw)
1021*0Sstevel@tonic-gate {
1022*0Sstevel@tonic-gate 	osa_pw_hist_ent *histp;
1023*0Sstevel@tonic-gate 	int i;
1024*0Sstevel@tonic-gate 
1025*0Sstevel@tonic-gate 	/* A history of 1 means just check the current password */
1026*0Sstevel@tonic-gate 	if (pol->pw_history_num == 1)
1027*0Sstevel@tonic-gate 		return (0);
1028*0Sstevel@tonic-gate 
1029*0Sstevel@tonic-gate 	/* resize the adb->old_keys array if necessary */
1030*0Sstevel@tonic-gate 	if (adb->old_key_len < pol->pw_history_num-1) {
1031*0Sstevel@tonic-gate 		if (adb->old_keys == NULL) {
1032*0Sstevel@tonic-gate 			adb->old_keys = (osa_pw_hist_ent *)
1033*0Sstevel@tonic-gate 						malloc((adb->old_key_len + 1) *
1034*0Sstevel@tonic-gate 						sizeof (osa_pw_hist_ent));
1035*0Sstevel@tonic-gate 		} else {
1036*0Sstevel@tonic-gate 			adb->old_keys = (osa_pw_hist_ent *)
1037*0Sstevel@tonic-gate 			realloc(adb->old_keys,
1038*0Sstevel@tonic-gate 				(adb->old_key_len + 1) *
1039*0Sstevel@tonic-gate 				sizeof (osa_pw_hist_ent));
1040*0Sstevel@tonic-gate 		}
1041*0Sstevel@tonic-gate 		if (adb->old_keys == NULL)
1042*0Sstevel@tonic-gate 			return (ENOMEM);
1043*0Sstevel@tonic-gate 
1044*0Sstevel@tonic-gate 		memset(&adb->old_keys[adb->old_key_len], 0,
1045*0Sstevel@tonic-gate 					sizeof (osa_pw_hist_ent));
1046*0Sstevel@tonic-gate 		adb->old_key_len++;
1047*0Sstevel@tonic-gate 		for (i = adb->old_key_len - 1; i > adb->old_key_next; i--)
1048*0Sstevel@tonic-gate 			adb->old_keys[i] = adb->old_keys[i - 1];
1049*0Sstevel@tonic-gate 		memset(&adb->old_keys[adb->old_key_next], 0,
1050*0Sstevel@tonic-gate 					sizeof (osa_pw_hist_ent));
1051*0Sstevel@tonic-gate 	} else if (adb->old_key_len > pol->pw_history_num-1) {
1052*0Sstevel@tonic-gate 		/*
1053*0Sstevel@tonic-gate 		 * The policy must have changed!  Shrink the array.
1054*0Sstevel@tonic-gate 		 * Can't simply realloc() down, since it might be wrapped.
1055*0Sstevel@tonic-gate 		 * To understand the arithmetic below, note that we are
1056*0Sstevel@tonic-gate 		 * copying into new positions 0 .. N-1 from old positions
1057*0Sstevel@tonic-gate 		 * old_key_next-N .. old_key_next-1, modulo old_key_len,
1058*0Sstevel@tonic-gate 		 * where N = pw_history_num - 1 is the length of the
1059*0Sstevel@tonic-gate 		 * shortened list.	Matt Crawford, FNAL
1060*0Sstevel@tonic-gate 		 */
1061*0Sstevel@tonic-gate 		int j;
1062*0Sstevel@tonic-gate 		histp = (osa_pw_hist_ent *)
1063*0Sstevel@tonic-gate 		malloc((pol->pw_history_num - 1) * sizeof (osa_pw_hist_ent));
1064*0Sstevel@tonic-gate 		if (histp) {
1065*0Sstevel@tonic-gate 			for (i = 0; i < pol->pw_history_num - 1; i++) {
1066*0Sstevel@tonic-gate 				/*
1067*0Sstevel@tonic-gate 				 * We need the number we use the modulus
1068*0Sstevel@tonic-gate 				 * operator on to be positive, so after
1069*0Sstevel@tonic-gate 				 * subtracting pol->pw_history_num-1, we
1070*0Sstevel@tonic-gate 				 * add back adb->old_key_len.
1071*0Sstevel@tonic-gate 				 */
1072*0Sstevel@tonic-gate 				j = KADM_MOD(i - (pol->pw_history_num - 1) +
1073*0Sstevel@tonic-gate 							adb->old_key_len);
1074*0Sstevel@tonic-gate 				histp[i] = adb->old_keys[j];
1075*0Sstevel@tonic-gate 			}
1076*0Sstevel@tonic-gate 			/* Now free the ones we don't keep (the oldest ones) */
1077*0Sstevel@tonic-gate 			for (i = 0; i < adb->old_key_len -   \
1078*0Sstevel@tonic-gate 					(pol->pw_history_num-1); i++) {
1079*0Sstevel@tonic-gate 				for (j = 0; j <   \
1080*0Sstevel@tonic-gate 				    adb->old_keys[KADM_MOD(i)].n_key_data; j++)
1081*0Sstevel@tonic-gate 					krb5_free_key_data_contents(context,
1082*0Sstevel@tonic-gate 					    &adb->old_keys[KADM_MOD(i)].
1083*0Sstevel@tonic-gate 					    key_data[j]);
1084*0Sstevel@tonic-gate 				free(adb->old_keys[KADM_MOD(i)].key_data);
1085*0Sstevel@tonic-gate 			}
1086*0Sstevel@tonic-gate 			free((void *)adb->old_keys);
1087*0Sstevel@tonic-gate 			adb->old_keys = histp;
1088*0Sstevel@tonic-gate 			adb->old_key_len = pol->pw_history_num - 1;
1089*0Sstevel@tonic-gate 			adb->old_key_next = 0;
1090*0Sstevel@tonic-gate 		} else {
1091*0Sstevel@tonic-gate 			return (ENOMEM);
1092*0Sstevel@tonic-gate 		}
1093*0Sstevel@tonic-gate 	}
1094*0Sstevel@tonic-gate 
1095*0Sstevel@tonic-gate 	if (adb->old_key_next + 1 > adb->old_key_len)
1096*0Sstevel@tonic-gate 		adb->old_key_next = 0;
1097*0Sstevel@tonic-gate 
1098*0Sstevel@tonic-gate 	/* free the old pw history entry if it contains data */
1099*0Sstevel@tonic-gate 	histp = &adb->old_keys[adb->old_key_next];
1100*0Sstevel@tonic-gate 	for (i = 0; i < histp->n_key_data; i++)
1101*0Sstevel@tonic-gate 		krb5_free_key_data_contents(context, &histp->key_data[i]);
1102*0Sstevel@tonic-gate 	free(histp->key_data);
1103*0Sstevel@tonic-gate 
1104*0Sstevel@tonic-gate 	/* store the new entry */
1105*0Sstevel@tonic-gate 	adb->old_keys[adb->old_key_next] = *pw;
1106*0Sstevel@tonic-gate 
1107*0Sstevel@tonic-gate 	/* update the next pointer */
1108*0Sstevel@tonic-gate 	if (++adb->old_key_next == pol->pw_history_num-1)
1109*0Sstevel@tonic-gate 		adb->old_key_next = 0;
1110*0Sstevel@tonic-gate 
1111*0Sstevel@tonic-gate 	return (0);
1112*0Sstevel@tonic-gate }
1113*0Sstevel@tonic-gate #undef KADM_MOD
1114*0Sstevel@tonic-gate 
1115*0Sstevel@tonic-gate kadm5_ret_t
1116*0Sstevel@tonic-gate kadm5_chpass_principal(void *server_handle,
1117*0Sstevel@tonic-gate 			    krb5_principal principal, char *password)
1118*0Sstevel@tonic-gate {
1119*0Sstevel@tonic-gate 	/*
1120*0Sstevel@tonic-gate 	 * Default to using the new API with the default set of
1121*0Sstevel@tonic-gate 	 * key/salt combinations.
1122*0Sstevel@tonic-gate 	 */
1123*0Sstevel@tonic-gate 	return (kadm5_chpass_principal_3(server_handle, principal, FALSE,
1124*0Sstevel@tonic-gate 			0, NULL, password));
1125*0Sstevel@tonic-gate }
1126*0Sstevel@tonic-gate 
1127*0Sstevel@tonic-gate kadm5_ret_t
1128*0Sstevel@tonic-gate kadm5_chpass_principal_3(void *server_handle,
1129*0Sstevel@tonic-gate 			 krb5_principal principal, krb5_boolean keepold,
1130*0Sstevel@tonic-gate 			 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1131*0Sstevel@tonic-gate 			 char *password)
1132*0Sstevel@tonic-gate {
1133*0Sstevel@tonic-gate     krb5_int32			now;
1134*0Sstevel@tonic-gate     kadm5_policy_ent_rec	pol;
1135*0Sstevel@tonic-gate     osa_princ_ent_rec		adb;
1136*0Sstevel@tonic-gate     krb5_db_entry		kdb, kdb_save;
1137*0Sstevel@tonic-gate     int				ret, ret2, last_pwd, i, hist_added;
1138*0Sstevel@tonic-gate     int				have_pol = 0;
1139*0Sstevel@tonic-gate     kadm5_server_handle_t	handle = server_handle;
1140*0Sstevel@tonic-gate     osa_pw_hist_ent		hist;
1141*0Sstevel@tonic-gate 
1142*0Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
1143*0Sstevel@tonic-gate 
1144*0Sstevel@tonic-gate     hist_added = 0;
1145*0Sstevel@tonic-gate     memset(&hist, 0, sizeof(hist));
1146*0Sstevel@tonic-gate 
1147*0Sstevel@tonic-gate     if (principal == NULL || password == NULL)
1148*0Sstevel@tonic-gate 	return EINVAL;
1149*0Sstevel@tonic-gate     if ((krb5_principal_compare(handle->context,
1150*0Sstevel@tonic-gate 				principal, hist_princ)) == TRUE)
1151*0Sstevel@tonic-gate 	return KADM5_PROTECT_PRINCIPAL;
1152*0Sstevel@tonic-gate 
1153*0Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1154*0Sstevel@tonic-gate        return(ret);
1155*0Sstevel@tonic-gate 
1156*0Sstevel@tonic-gate     /* we are going to need the current keys after the new keys are set */
1157*0Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
1158*0Sstevel@tonic-gate 	 kdb_free_entry(handle, &kdb, &adb);
1159*0Sstevel@tonic-gate 	 return(ret);
1160*0Sstevel@tonic-gate     }
1161*0Sstevel@tonic-gate 
1162*0Sstevel@tonic-gate     if ((adb.aux_attributes & KADM5_POLICY)) {
1163*0Sstevel@tonic-gate 	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1164*0Sstevel@tonic-gate 	     goto done;
1165*0Sstevel@tonic-gate 	have_pol = 1;
1166*0Sstevel@tonic-gate     }
1167*0Sstevel@tonic-gate 
1168*0Sstevel@tonic-gate     if ((ret = passwd_check(handle, password, adb.aux_attributes &
1169*0Sstevel@tonic-gate 			    KADM5_POLICY, &pol, principal)))
1170*0Sstevel@tonic-gate 	 goto done;
1171*0Sstevel@tonic-gate 
1172*0Sstevel@tonic-gate     if (ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock,
1173*0Sstevel@tonic-gate 			   n_ks_tuple?ks_tuple:handle->params.keysalts,
1174*0Sstevel@tonic-gate 			   n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1175*0Sstevel@tonic-gate 			   password, 0 /* increment kvno */,
1176*0Sstevel@tonic-gate 			   keepold, &kdb))
1177*0Sstevel@tonic-gate 	goto done;
1178*0Sstevel@tonic-gate 
1179*0Sstevel@tonic-gate     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1180*0Sstevel@tonic-gate 
1181*0Sstevel@tonic-gate     if (ret = krb5_timeofday(handle->context, &now))
1182*0Sstevel@tonic-gate 	 goto done;
1183*0Sstevel@tonic-gate 
1184*0Sstevel@tonic-gate     if ((adb.aux_attributes & KADM5_POLICY)) {
1185*0Sstevel@tonic-gate        /* the policy was loaded before */
1186*0Sstevel@tonic-gate 
1187*0Sstevel@tonic-gate 	if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1188*0Sstevel@tonic-gate 						  &kdb, &last_pwd))
1189*0Sstevel@tonic-gate 	     goto done;
1190*0Sstevel@tonic-gate 
1191*0Sstevel@tonic-gate #if 0
1192*0Sstevel@tonic-gate 	 /*
1193*0Sstevel@tonic-gate 	  * The spec says this check is overridden if the caller has
1194*0Sstevel@tonic-gate 	  * modify privilege.  The admin server therefore makes this
1195*0Sstevel@tonic-gate 	  * check itself (in chpass_principal_wrapper, misc.c). A
1196*0Sstevel@tonic-gate 	  * local caller implicitly has all authorization bits.
1197*0Sstevel@tonic-gate 	  */
1198*0Sstevel@tonic-gate 	if ((now - last_pwd) < pol.pw_min_life &&
1199*0Sstevel@tonic-gate 	    !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1200*0Sstevel@tonic-gate 	     ret = KADM5_PASS_TOOSOON;
1201*0Sstevel@tonic-gate 	     goto done;
1202*0Sstevel@tonic-gate 	}
1203*0Sstevel@tonic-gate #endif
1204*0Sstevel@tonic-gate 
1205*0Sstevel@tonic-gate 	if (ret = create_history_entry(handle->context,
1206*0Sstevel@tonic-gate 		&handle->master_keyblock, kdb_save.n_key_data,
1207*0Sstevel@tonic-gate 		kdb_save.key_data, &hist))
1208*0Sstevel@tonic-gate 	     goto done;
1209*0Sstevel@tonic-gate 
1210*0Sstevel@tonic-gate 	if (ret = check_pw_reuse(handle->context,
1211*0Sstevel@tonic-gate 				&handle->master_keyblock,
1212*0Sstevel@tonic-gate 				 &hist_key,
1213*0Sstevel@tonic-gate 				 kdb.n_key_data, kdb.key_data,
1214*0Sstevel@tonic-gate 				 1, &hist))
1215*0Sstevel@tonic-gate 	     goto done;
1216*0Sstevel@tonic-gate 
1217*0Sstevel@tonic-gate 	if (pol.pw_history_num > 1) {
1218*0Sstevel@tonic-gate 	    if (adb.admin_history_kvno != hist_kvno) {
1219*0Sstevel@tonic-gate 		ret = KADM5_BAD_HIST_KEY;
1220*0Sstevel@tonic-gate 		goto done;
1221*0Sstevel@tonic-gate 	    }
1222*0Sstevel@tonic-gate 
1223*0Sstevel@tonic-gate 	    if (ret = check_pw_reuse(handle->context,
1224*0Sstevel@tonic-gate 				&handle->master_keyblock,
1225*0Sstevel@tonic-gate 				     &hist_key,
1226*0Sstevel@tonic-gate 				     kdb.n_key_data, kdb.key_data,
1227*0Sstevel@tonic-gate 				     adb.old_key_len, adb.old_keys))
1228*0Sstevel@tonic-gate 		goto done;
1229*0Sstevel@tonic-gate 
1230*0Sstevel@tonic-gate 	    if (ret = add_to_history(handle->context, &adb, &pol, &hist))
1231*0Sstevel@tonic-gate 		 goto done;
1232*0Sstevel@tonic-gate 	    hist_added = 1;
1233*0Sstevel@tonic-gate        }
1234*0Sstevel@tonic-gate 
1235*0Sstevel@tonic-gate 	if (pol.pw_max_life)
1236*0Sstevel@tonic-gate 	   kdb.pw_expiration = now + pol.pw_max_life;
1237*0Sstevel@tonic-gate 	else
1238*0Sstevel@tonic-gate 	   kdb.pw_expiration = 0;
1239*0Sstevel@tonic-gate     } else {
1240*0Sstevel@tonic-gate 	kdb.pw_expiration = 0;
1241*0Sstevel@tonic-gate     }
1242*0Sstevel@tonic-gate 
1243*0Sstevel@tonic-gate     if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1244*0Sstevel@tonic-gate 	goto done;
1245*0Sstevel@tonic-gate 
1246*0Sstevel@tonic-gate     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1247*0Sstevel@tonic-gate 	goto done;
1248*0Sstevel@tonic-gate 
1249*0Sstevel@tonic-gate     ret = KADM5_OK;
1250*0Sstevel@tonic-gate done:
1251*0Sstevel@tonic-gate     if (!hist_added && hist.key_data)
1252*0Sstevel@tonic-gate 	 free_history_entry(handle->context, &hist);
1253*0Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
1254*0Sstevel@tonic-gate     kdb_free_entry(handle, &kdb_save, NULL);
1255*0Sstevel@tonic-gate     krb5_dbe_free_contents(handle->context, &kdb);
1256*0Sstevel@tonic-gate 
1257*0Sstevel@tonic-gate     if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1258*0Sstevel@tonic-gate 	&& !ret)
1259*0Sstevel@tonic-gate 	 ret = ret2;
1260*0Sstevel@tonic-gate 
1261*0Sstevel@tonic-gate     return ret;
1262*0Sstevel@tonic-gate }
1263*0Sstevel@tonic-gate 
1264*0Sstevel@tonic-gate kadm5_ret_t
1265*0Sstevel@tonic-gate kadm5_randkey_principal(void *server_handle,
1266*0Sstevel@tonic-gate 			krb5_principal principal,
1267*0Sstevel@tonic-gate 			krb5_keyblock **keyblocks,
1268*0Sstevel@tonic-gate 			int *n_keys)
1269*0Sstevel@tonic-gate {
1270*0Sstevel@tonic-gate 	krb5_key_salt_tuple keysalts[2];
1271*0Sstevel@tonic-gate 
1272*0Sstevel@tonic-gate 	/*
1273*0Sstevel@tonic-gate 	 * Anyone calling this routine is forced to use only DES
1274*0Sstevel@tonic-gate 	 * enctypes to be compatible with earlier releases that
1275*0Sstevel@tonic-gate 	 * did not support stronger crypto.
1276*0Sstevel@tonic-gate 	 *
1277*0Sstevel@tonic-gate 	 * S10 (and later) kadmin clients will not use this API,
1278*0Sstevel@tonic-gate 	 * so we can assume the request is from an older version.
1279*0Sstevel@tonic-gate 	 */
1280*0Sstevel@tonic-gate 	keysalts[0].ks_enctype = ENCTYPE_DES_CBC_MD5;
1281*0Sstevel@tonic-gate 	keysalts[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
1282*0Sstevel@tonic-gate 	keysalts[1].ks_enctype = ENCTYPE_DES_CBC_CRC;
1283*0Sstevel@tonic-gate 	keysalts[1].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
1284*0Sstevel@tonic-gate 
1285*0Sstevel@tonic-gate 	return (kadm5_randkey_principal_3(server_handle, principal,
1286*0Sstevel@tonic-gate 			FALSE, 2, keysalts, keyblocks, n_keys));
1287*0Sstevel@tonic-gate }
1288*0Sstevel@tonic-gate 
1289*0Sstevel@tonic-gate kadm5_ret_t
1290*0Sstevel@tonic-gate kadm5_randkey_principal_3(void *server_handle,
1291*0Sstevel@tonic-gate 			krb5_principal principal,
1292*0Sstevel@tonic-gate 			krb5_boolean keepold,
1293*0Sstevel@tonic-gate 			int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1294*0Sstevel@tonic-gate 			krb5_keyblock **keyblocks,
1295*0Sstevel@tonic-gate 			int *n_keys)
1296*0Sstevel@tonic-gate {
1297*0Sstevel@tonic-gate     krb5_db_entry		kdb;
1298*0Sstevel@tonic-gate     osa_princ_ent_rec		adb;
1299*0Sstevel@tonic-gate     krb5_int32			now;
1300*0Sstevel@tonic-gate     kadm5_policy_ent_rec	pol;
1301*0Sstevel@tonic-gate     krb5_key_data		*key_data;
1302*0Sstevel@tonic-gate     krb5_keyblock		*keyblock;
1303*0Sstevel@tonic-gate     int				ret, last_pwd, have_pol = 0;
1304*0Sstevel@tonic-gate     kadm5_server_handle_t	handle = server_handle;
1305*0Sstevel@tonic-gate 
1306*0Sstevel@tonic-gate     if (keyblocks)
1307*0Sstevel@tonic-gate 	 *keyblocks = NULL;
1308*0Sstevel@tonic-gate 
1309*0Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
1310*0Sstevel@tonic-gate 
1311*0Sstevel@tonic-gate     if (principal == NULL)
1312*0Sstevel@tonic-gate 	return EINVAL;
1313*0Sstevel@tonic-gate     if (hist_princ && /* this will be NULL when initializing the databse */
1314*0Sstevel@tonic-gate 	((krb5_principal_compare(handle->context,
1315*0Sstevel@tonic-gate 				 principal, hist_princ)) == TRUE))
1316*0Sstevel@tonic-gate 	return KADM5_PROTECT_PRINCIPAL;
1317*0Sstevel@tonic-gate 
1318*0Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1319*0Sstevel@tonic-gate        return(ret);
1320*0Sstevel@tonic-gate 
1321*0Sstevel@tonic-gate     if (ret = krb5_dbe_crk(handle->context, &handle->master_keyblock,
1322*0Sstevel@tonic-gate 			   n_ks_tuple?ks_tuple:handle->params.keysalts,
1323*0Sstevel@tonic-gate 			   n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
1324*0Sstevel@tonic-gate 			   keepold,
1325*0Sstevel@tonic-gate 			   &kdb))
1326*0Sstevel@tonic-gate        goto done;
1327*0Sstevel@tonic-gate 
1328*0Sstevel@tonic-gate     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1329*0Sstevel@tonic-gate 
1330*0Sstevel@tonic-gate     if (ret = krb5_timeofday(handle->context, &now))
1331*0Sstevel@tonic-gate 	goto done;
1332*0Sstevel@tonic-gate 
1333*0Sstevel@tonic-gate     if ((adb.aux_attributes & KADM5_POLICY)) {
1334*0Sstevel@tonic-gate 	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1335*0Sstevel@tonic-gate 				    &pol)) != KADM5_OK)
1336*0Sstevel@tonic-gate 	   goto done;
1337*0Sstevel@tonic-gate 	have_pol = 1;
1338*0Sstevel@tonic-gate 
1339*0Sstevel@tonic-gate 	if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1340*0Sstevel@tonic-gate 						  &kdb, &last_pwd))
1341*0Sstevel@tonic-gate 	     goto done;
1342*0Sstevel@tonic-gate 
1343*0Sstevel@tonic-gate #if 0
1344*0Sstevel@tonic-gate 	 /*
1345*0Sstevel@tonic-gate 	  * The spec says this check is overridden if the caller has
1346*0Sstevel@tonic-gate 	  * modify privilege.  The admin server therefore makes this
1347*0Sstevel@tonic-gate 	  * check itself (in chpass_principal_wrapper, misc.c).  A
1348*0Sstevel@tonic-gate 	  * local caller implicitly has all authorization bits.
1349*0Sstevel@tonic-gate 	  */
1350*0Sstevel@tonic-gate 	if((now - last_pwd) < pol.pw_min_life &&
1351*0Sstevel@tonic-gate 	   !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1352*0Sstevel@tonic-gate 	     ret = KADM5_PASS_TOOSOON;
1353*0Sstevel@tonic-gate 	     goto done;
1354*0Sstevel@tonic-gate 	}
1355*0Sstevel@tonic-gate #endif
1356*0Sstevel@tonic-gate 
1357*0Sstevel@tonic-gate 	if(pol.pw_history_num > 1) {
1358*0Sstevel@tonic-gate 	    if(adb.admin_history_kvno != hist_kvno) {
1359*0Sstevel@tonic-gate 		ret = KADM5_BAD_HIST_KEY;
1360*0Sstevel@tonic-gate 		goto done;
1361*0Sstevel@tonic-gate 	    }
1362*0Sstevel@tonic-gate 
1363*0Sstevel@tonic-gate 	    if (ret = check_pw_reuse(handle->context,
1364*0Sstevel@tonic-gate 			&handle->master_keyblock,
1365*0Sstevel@tonic-gate 			&hist_key,
1366*0Sstevel@tonic-gate 			kdb.n_key_data, kdb.key_data,
1367*0Sstevel@tonic-gate 			adb.old_key_len, adb.old_keys))
1368*0Sstevel@tonic-gate 		goto done;
1369*0Sstevel@tonic-gate 	}
1370*0Sstevel@tonic-gate 	if (pol.pw_max_life)
1371*0Sstevel@tonic-gate 	   kdb.pw_expiration = now + pol.pw_max_life;
1372*0Sstevel@tonic-gate 	else
1373*0Sstevel@tonic-gate 	   kdb.pw_expiration = 0;
1374*0Sstevel@tonic-gate     } else {
1375*0Sstevel@tonic-gate 	kdb.pw_expiration = 0;
1376*0Sstevel@tonic-gate     }
1377*0Sstevel@tonic-gate 
1378*0Sstevel@tonic-gate     if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1379*0Sstevel@tonic-gate 	 goto done;
1380*0Sstevel@tonic-gate 
1381*0Sstevel@tonic-gate     if (keyblocks) {
1382*0Sstevel@tonic-gate 	 if (handle->api_version == KADM5_API_VERSION_1) {
1383*0Sstevel@tonic-gate 	      /* Version 1 clients will expect to see a DES_CRC enctype. */
1384*0Sstevel@tonic-gate 	      if (ret = krb5_dbe_find_enctype(handle->context, &kdb,
1385*0Sstevel@tonic-gate 					      ENCTYPE_DES_CBC_CRC,
1386*0Sstevel@tonic-gate 					      -1, -1, &key_data))
1387*0Sstevel@tonic-gate 		   goto done;
1388*0Sstevel@tonic-gate 
1389*0Sstevel@tonic-gate 	      if (ret = decrypt_key_data(handle->context,
1390*0Sstevel@tonic-gate 				&handle->master_keyblock, 1, key_data,
1391*0Sstevel@tonic-gate 				 keyblocks, NULL))
1392*0Sstevel@tonic-gate 		   goto done;
1393*0Sstevel@tonic-gate 	 } else {
1394*0Sstevel@tonic-gate 	      ret = decrypt_key_data(handle->context,
1395*0Sstevel@tonic-gate 				&handle->master_keyblock,
1396*0Sstevel@tonic-gate 				kdb.n_key_data, kdb.key_data,
1397*0Sstevel@tonic-gate 				keyblocks, n_keys);
1398*0Sstevel@tonic-gate 	      if (ret)
1399*0Sstevel@tonic-gate 		   goto done;
1400*0Sstevel@tonic-gate 	 }
1401*0Sstevel@tonic-gate     }
1402*0Sstevel@tonic-gate 
1403*0Sstevel@tonic-gate     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1404*0Sstevel@tonic-gate 	goto done;
1405*0Sstevel@tonic-gate 
1406*0Sstevel@tonic-gate     ret = KADM5_OK;
1407*0Sstevel@tonic-gate done:
1408*0Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
1409*0Sstevel@tonic-gate     if (have_pol)
1410*0Sstevel@tonic-gate 	 kadm5_free_policy_ent(handle->lhandle, &pol);
1411*0Sstevel@tonic-gate 
1412*0Sstevel@tonic-gate     return ret;
1413*0Sstevel@tonic-gate }
1414*0Sstevel@tonic-gate 
1415*0Sstevel@tonic-gate kadm5_ret_t
1416*0Sstevel@tonic-gate kadm5_setkey_principal(void *server_handle,
1417*0Sstevel@tonic-gate 		       krb5_principal principal,
1418*0Sstevel@tonic-gate 		       krb5_keyblock *keyblocks,
1419*0Sstevel@tonic-gate 		       int n_keys)
1420*0Sstevel@tonic-gate {
1421*0Sstevel@tonic-gate 	return (kadm5_setkey_principal_3(server_handle, principal,
1422*0Sstevel@tonic-gate 			FALSE, 0, NULL, keyblocks, n_keys));
1423*0Sstevel@tonic-gate }
1424*0Sstevel@tonic-gate 
1425*0Sstevel@tonic-gate kadm5_ret_t
1426*0Sstevel@tonic-gate kadm5_setkey_principal_3(void *server_handle,
1427*0Sstevel@tonic-gate 			 krb5_principal principal,
1428*0Sstevel@tonic-gate 			 krb5_boolean keepold,
1429*0Sstevel@tonic-gate 			 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1430*0Sstevel@tonic-gate 			 krb5_keyblock *keyblocks,
1431*0Sstevel@tonic-gate 			 int n_keys)
1432*0Sstevel@tonic-gate {
1433*0Sstevel@tonic-gate     krb5_db_entry		kdb;
1434*0Sstevel@tonic-gate     osa_princ_ent_rec		adb;
1435*0Sstevel@tonic-gate     krb5_int32			now;
1436*0Sstevel@tonic-gate     kadm5_policy_ent_rec	pol;
1437*0Sstevel@tonic-gate     krb5_key_data		*old_key_data;
1438*0Sstevel@tonic-gate     int				n_old_keys;
1439*0Sstevel@tonic-gate     int				i, j, kvno, ret, last_pwd, have_pol = 0;
1440*0Sstevel@tonic-gate     kadm5_server_handle_t	handle = server_handle;
1441*0Sstevel@tonic-gate     krb5_boolean		similar;
1442*0Sstevel@tonic-gate     krb5_keysalt		keysalt;
1443*0Sstevel@tonic-gate 
1444*0Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
1445*0Sstevel@tonic-gate 
1446*0Sstevel@tonic-gate     if (principal == NULL || keyblocks == NULL)
1447*0Sstevel@tonic-gate 	return EINVAL;
1448*0Sstevel@tonic-gate     if (hist_princ && /* this will be NULL when initializing the databse */
1449*0Sstevel@tonic-gate 	((krb5_principal_compare(handle->context,
1450*0Sstevel@tonic-gate 				 principal, hist_princ)) == TRUE))
1451*0Sstevel@tonic-gate 	return KADM5_PROTECT_PRINCIPAL;
1452*0Sstevel@tonic-gate 
1453*0Sstevel@tonic-gate     for (i = 0; i < n_keys; i++) {
1454*0Sstevel@tonic-gate 	for (j = i+1; j < n_keys; j++) {
1455*0Sstevel@tonic-gate 	    if (ret = krb5_c_enctype_compare(handle->context,
1456*0Sstevel@tonic-gate 					     keyblocks[i].enctype,
1457*0Sstevel@tonic-gate 					     keyblocks[j].enctype,
1458*0Sstevel@tonic-gate 					     &similar))
1459*0Sstevel@tonic-gate 		return(ret);
1460*0Sstevel@tonic-gate 	    if (similar)
1461*0Sstevel@tonic-gate 		if (n_ks_tuple) {
1462*0Sstevel@tonic-gate 		    if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
1463*0Sstevel@tonic-gate 			return KADM5_SETKEY_DUP_ENCTYPES;
1464*0Sstevel@tonic-gate 		} else
1465*0Sstevel@tonic-gate 		    return KADM5_SETKEY_DUP_ENCTYPES;
1466*0Sstevel@tonic-gate 	}
1467*0Sstevel@tonic-gate     }
1468*0Sstevel@tonic-gate 
1469*0Sstevel@tonic-gate     if (n_ks_tuple != n_keys)
1470*0Sstevel@tonic-gate 	return KADM5_SETKEY3_ETYPE_MISMATCH;
1471*0Sstevel@tonic-gate 
1472*0Sstevel@tonic-gate     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1473*0Sstevel@tonic-gate        return(ret);
1474*0Sstevel@tonic-gate 
1475*0Sstevel@tonic-gate     for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1476*0Sstevel@tonic-gate 	 if (kdb.key_data[i].key_data_kvno > kvno)
1477*0Sstevel@tonic-gate 	      kvno = kdb.key_data[i].key_data_kvno;
1478*0Sstevel@tonic-gate 
1479*0Sstevel@tonic-gate     if (keepold) {
1480*0Sstevel@tonic-gate 	old_key_data = kdb.key_data;
1481*0Sstevel@tonic-gate 	n_old_keys = kdb.n_key_data;
1482*0Sstevel@tonic-gate     } else {
1483*0Sstevel@tonic-gate 	if (kdb.key_data != NULL)
1484*0Sstevel@tonic-gate 	    cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1485*0Sstevel@tonic-gate 	n_old_keys = 0;
1486*0Sstevel@tonic-gate 	old_key_data = NULL;
1487*0Sstevel@tonic-gate     }
1488*0Sstevel@tonic-gate 
1489*0Sstevel@tonic-gate     kdb.key_data = (krb5_key_data*)malloc((n_keys+n_old_keys)
1490*0Sstevel@tonic-gate 					  *sizeof(krb5_key_data));
1491*0Sstevel@tonic-gate     if (kdb.key_data == NULL)
1492*0Sstevel@tonic-gate 	 return ENOMEM;
1493*0Sstevel@tonic-gate     memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
1494*0Sstevel@tonic-gate     kdb.n_key_data = 0;
1495*0Sstevel@tonic-gate 
1496*0Sstevel@tonic-gate     for (i = 0; i < n_keys; i++) {
1497*0Sstevel@tonic-gate 	if (n_ks_tuple) {
1498*0Sstevel@tonic-gate 	    keysalt.type = ks_tuple[i].ks_salttype;
1499*0Sstevel@tonic-gate 	    keysalt.data.length = 0;
1500*0Sstevel@tonic-gate 	    keysalt.data.data = NULL;
1501*0Sstevel@tonic-gate 	    if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
1502*0Sstevel@tonic-gate 		cleanup_key_data(handle->context, kdb.n_key_data,
1503*0Sstevel@tonic-gate 				 kdb.key_data);
1504*0Sstevel@tonic-gate 		return KADM5_SETKEY3_ETYPE_MISMATCH;
1505*0Sstevel@tonic-gate 	    }
1506*0Sstevel@tonic-gate 	}
1507*0Sstevel@tonic-gate 	ret = krb5_dbekd_encrypt_key_data(handle->context,
1508*0Sstevel@tonic-gate 					  &handle->master_keyblock,
1509*0Sstevel@tonic-gate 					  &keyblocks[i],
1510*0Sstevel@tonic-gate 					  n_ks_tuple ? &keysalt : NULL,
1511*0Sstevel@tonic-gate 					  kvno + 1,
1512*0Sstevel@tonic-gate 					  &kdb.key_data[i]);
1513*0Sstevel@tonic-gate 	if (ret) {
1514*0Sstevel@tonic-gate 	    cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1515*0Sstevel@tonic-gate 	    return ret;
1516*0Sstevel@tonic-gate 	}
1517*0Sstevel@tonic-gate 	kdb.n_key_data++;
1518*0Sstevel@tonic-gate     }
1519*0Sstevel@tonic-gate 
1520*0Sstevel@tonic-gate     /* copy old key data if necessary */
1521*0Sstevel@tonic-gate     for (i = 0; i < n_old_keys; i++) {
1522*0Sstevel@tonic-gate 	kdb.key_data[i+n_keys] = old_key_data[i];
1523*0Sstevel@tonic-gate 	memset(&old_key_data[i], 0, sizeof (krb5_key_data));
1524*0Sstevel@tonic-gate 	kdb.n_key_data++;
1525*0Sstevel@tonic-gate     }
1526*0Sstevel@tonic-gate     /* assert(kdb.n_key_data == n_keys + n_old_keys) */
1527*0Sstevel@tonic-gate     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1528*0Sstevel@tonic-gate 
1529*0Sstevel@tonic-gate     if (ret = krb5_timeofday(handle->context, &now))
1530*0Sstevel@tonic-gate 	goto done;
1531*0Sstevel@tonic-gate 
1532*0Sstevel@tonic-gate     if ((adb.aux_attributes & KADM5_POLICY)) {
1533*0Sstevel@tonic-gate 	if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1534*0Sstevel@tonic-gate 				    &pol)) != KADM5_OK)
1535*0Sstevel@tonic-gate 	   goto done;
1536*0Sstevel@tonic-gate 	have_pol = 1;
1537*0Sstevel@tonic-gate 
1538*0Sstevel@tonic-gate #if 0
1539*0Sstevel@tonic-gate 	/*
1540*0Sstevel@tonic-gate 	  * The spec says this check is overridden if the caller has
1541*0Sstevel@tonic-gate 	  * modify privilege.  The admin server therefore makes this
1542*0Sstevel@tonic-gate 	  * check itself (in chpass_principal_wrapper, misc.c).  A
1543*0Sstevel@tonic-gate 	  * local caller implicitly has all authorization bits.
1544*0Sstevel@tonic-gate 	  */
1545*0Sstevel@tonic-gate 	if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1546*0Sstevel@tonic-gate 						  &kdb, &last_pwd))
1547*0Sstevel@tonic-gate 	     goto done;
1548*0Sstevel@tonic-gate 	if((now - last_pwd) < pol.pw_min_life &&
1549*0Sstevel@tonic-gate 	   !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1550*0Sstevel@tonic-gate 	     ret = KADM5_PASS_TOOSOON;
1551*0Sstevel@tonic-gate 	     goto done;
1552*0Sstevel@tonic-gate 	}
1553*0Sstevel@tonic-gate #endif
1554*0Sstevel@tonic-gate #if 0
1555*0Sstevel@tonic-gate 	/*
1556*0Sstevel@tonic-gate 	 * Should we be checking/updating pw history here?
1557*0Sstevel@tonic-gate 	 */
1558*0Sstevel@tonic-gate 	if(pol.pw_history_num > 1) {
1559*0Sstevel@tonic-gate 	    if(adb.admin_history_kvno != hist_kvno) {
1560*0Sstevel@tonic-gate 		ret = KADM5_BAD_HIST_KEY;
1561*0Sstevel@tonic-gate 		goto done;
1562*0Sstevel@tonic-gate 	    }
1563*0Sstevel@tonic-gate 
1564*0Sstevel@tonic-gate 	    if (ret = check_pw_reuse(handle->context,
1565*0Sstevel@tonic-gate 				&handle->master_keyblock,
1566*0Sstevel@tonic-gate 				     &hist_key,
1567*0Sstevel@tonic-gate 				     kdb.n_key_data, kdb.key_data,
1568*0Sstevel@tonic-gate 				     adb.old_key_len, adb.old_keys))
1569*0Sstevel@tonic-gate 		goto done;
1570*0Sstevel@tonic-gate 	}
1571*0Sstevel@tonic-gate #endif
1572*0Sstevel@tonic-gate 
1573*0Sstevel@tonic-gate 	if (pol.pw_max_life)
1574*0Sstevel@tonic-gate 	   kdb.pw_expiration = now + pol.pw_max_life;
1575*0Sstevel@tonic-gate 	else
1576*0Sstevel@tonic-gate 	   kdb.pw_expiration = 0;
1577*0Sstevel@tonic-gate     } else {
1578*0Sstevel@tonic-gate 	kdb.pw_expiration = 0;
1579*0Sstevel@tonic-gate     }
1580*0Sstevel@tonic-gate 
1581*0Sstevel@tonic-gate     if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1582*0Sstevel@tonic-gate 	 goto done;
1583*0Sstevel@tonic-gate 
1584*0Sstevel@tonic-gate     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1585*0Sstevel@tonic-gate 	goto done;
1586*0Sstevel@tonic-gate 
1587*0Sstevel@tonic-gate     ret = KADM5_OK;
1588*0Sstevel@tonic-gate done:
1589*0Sstevel@tonic-gate     kdb_free_entry(handle, &kdb, &adb);
1590*0Sstevel@tonic-gate     if (have_pol)
1591*0Sstevel@tonic-gate 	 kadm5_free_policy_ent(handle->lhandle, &pol);
1592*0Sstevel@tonic-gate 
1593*0Sstevel@tonic-gate     return ret;
1594*0Sstevel@tonic-gate }
1595*0Sstevel@tonic-gate 
1596*0Sstevel@tonic-gate /*
1597*0Sstevel@tonic-gate  * Allocate an array of n_key_data krb5_keyblocks, fill in each
1598*0Sstevel@tonic-gate  * element with the results of decrypting the nth key in key_data with
1599*0Sstevel@tonic-gate  * master_keyblock, and if n_keys is not NULL fill it in with the
1600*0Sstevel@tonic-gate  * number of keys decrypted.
1601*0Sstevel@tonic-gate  */
1602*0Sstevel@tonic-gate static int decrypt_key_data(krb5_context context,
1603*0Sstevel@tonic-gate 	krb5_keyblock *master_keyblock,
1604*0Sstevel@tonic-gate 	int n_key_data, krb5_key_data *key_data,
1605*0Sstevel@tonic-gate 	krb5_keyblock **keyblocks, int *n_keys)
1606*0Sstevel@tonic-gate {
1607*0Sstevel@tonic-gate      krb5_keyblock *keys;
1608*0Sstevel@tonic-gate      int ret, i;
1609*0Sstevel@tonic-gate 
1610*0Sstevel@tonic-gate      keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
1611*0Sstevel@tonic-gate      if (keys == NULL)
1612*0Sstevel@tonic-gate 	  return ENOMEM;
1613*0Sstevel@tonic-gate      memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1614*0Sstevel@tonic-gate 
1615*0Sstevel@tonic-gate      for (i = 0; i < n_key_data; i++) {
1616*0Sstevel@tonic-gate 	  if (ret = krb5_dbekd_decrypt_key_data(context,
1617*0Sstevel@tonic-gate 		master_keyblock, &key_data[i], &keys[i], NULL)) {
1618*0Sstevel@tonic-gate 
1619*0Sstevel@tonic-gate 	       memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1620*0Sstevel@tonic-gate 	       free(keys);
1621*0Sstevel@tonic-gate 	       return ret;
1622*0Sstevel@tonic-gate 	  }
1623*0Sstevel@tonic-gate      }
1624*0Sstevel@tonic-gate 
1625*0Sstevel@tonic-gate      *keyblocks = keys;
1626*0Sstevel@tonic-gate      if (n_keys)
1627*0Sstevel@tonic-gate 	  *n_keys = n_key_data;
1628*0Sstevel@tonic-gate 
1629*0Sstevel@tonic-gate      return 0;
1630*0Sstevel@tonic-gate }
1631*0Sstevel@tonic-gate 
1632*0Sstevel@tonic-gate /*
1633*0Sstevel@tonic-gate  * Function: kadm5_decrypt_key
1634*0Sstevel@tonic-gate  *
1635*0Sstevel@tonic-gate  * Purpose: Retrieves and decrypts a principal key.
1636*0Sstevel@tonic-gate  *
1637*0Sstevel@tonic-gate  * Arguments:
1638*0Sstevel@tonic-gate  *
1639*0Sstevel@tonic-gate  *	server_handle	(r) kadm5 handle
1640*0Sstevel@tonic-gate  *	entry		(r) principal retrieved with kadm5_get_principal
1641*0Sstevel@tonic-gate  *	ktype		(r) enctype to search for, or -1 to ignore
1642*0Sstevel@tonic-gate  *	stype		(r) salt type to search for, or -1 to ignore
1643*0Sstevel@tonic-gate  *	kvno		(r) kvno to search for, -1 for max, 0 for max
1644*0Sstevel@tonic-gate  *			only if it also matches ktype and stype
1645*0Sstevel@tonic-gate  *	keyblock	(w) keyblock to fill in
1646*0Sstevel@tonic-gate  *	keysalt		(w) keysalt to fill in, or NULL
1647*0Sstevel@tonic-gate  *	kvnop		(w) kvno to fill in, or NULL
1648*0Sstevel@tonic-gate  *
1649*0Sstevel@tonic-gate  * Effects: Searches the key_data array of entry, which must have been
1650*0Sstevel@tonic-gate  * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
1651*0Sstevel@tonic-gate  * find a key with a specified enctype, salt type, and kvno in a
1652*0Sstevel@tonic-gate  * principal entry.  If not found, return ENOENT.  Otherwise, decrypt
1653*0Sstevel@tonic-gate  * it with the master key, and return the key in keyblock, the salt
1654*0Sstevel@tonic-gate  * in salttype, and the key version number in kvno.
1655*0Sstevel@tonic-gate  *
1656*0Sstevel@tonic-gate  * If ktype or stype is -1, it is ignored for the search.  If kvno is
1657*0Sstevel@tonic-gate  * -1, ktype and stype are ignored and the key with the max kvno is
1658*0Sstevel@tonic-gate  * returned.  If kvno is 0, only the key with the max kvno is returned
1659*0Sstevel@tonic-gate  * and only if it matches the ktype and stype; otherwise, ENOENT is
1660*0Sstevel@tonic-gate  * returned.
1661*0Sstevel@tonic-gate  */
1662*0Sstevel@tonic-gate kadm5_ret_t kadm5_decrypt_key(void *server_handle,
1663*0Sstevel@tonic-gate 			      kadm5_principal_ent_t entry, krb5_int32
1664*0Sstevel@tonic-gate 			      ktype, krb5_int32 stype, krb5_int32
1665*0Sstevel@tonic-gate 			      kvno, krb5_keyblock *keyblock,
1666*0Sstevel@tonic-gate 			      krb5_keysalt *keysalt, int *kvnop)
1667*0Sstevel@tonic-gate {
1668*0Sstevel@tonic-gate     kadm5_server_handle_t handle = server_handle;
1669*0Sstevel@tonic-gate     krb5_db_entry dbent;
1670*0Sstevel@tonic-gate     krb5_key_data *key_data;
1671*0Sstevel@tonic-gate     int ret;
1672*0Sstevel@tonic-gate 
1673*0Sstevel@tonic-gate     CHECK_HANDLE(server_handle);
1674*0Sstevel@tonic-gate 
1675*0Sstevel@tonic-gate     if (entry->n_key_data == 0 || entry->key_data == NULL)
1676*0Sstevel@tonic-gate 	 return EINVAL;
1677*0Sstevel@tonic-gate 
1678*0Sstevel@tonic-gate     /* find_enctype only uses these two fields */
1679*0Sstevel@tonic-gate     dbent.n_key_data = entry->n_key_data;
1680*0Sstevel@tonic-gate     dbent.key_data = entry->key_data;
1681*0Sstevel@tonic-gate     if (ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
1682*0Sstevel@tonic-gate 				    stype, kvno, &key_data))
1683*0Sstevel@tonic-gate 	 return ret;
1684*0Sstevel@tonic-gate 
1685*0Sstevel@tonic-gate     if (ret = krb5_dbekd_decrypt_key_data(handle->context,
1686*0Sstevel@tonic-gate 			&handle->master_keyblock, key_data,
1687*0Sstevel@tonic-gate 			keyblock, keysalt))
1688*0Sstevel@tonic-gate 	 return ret;
1689*0Sstevel@tonic-gate 
1690*0Sstevel@tonic-gate     if (kvnop)
1691*0Sstevel@tonic-gate 	 *kvnop = key_data->key_data_kvno;
1692*0Sstevel@tonic-gate 
1693*0Sstevel@tonic-gate     return KADM5_OK;
1694*0Sstevel@tonic-gate }
1695