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