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