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