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