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