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