Novell Database Abstraction Layer merge.
[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        <kadm5/admin.h>
14 #include        "k5-int.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     {
1665         kdb.key_data->key_data_type[k] = tmp_key_data.key_data_type[k];
1666         kdb.key_data->key_data_length[k] = tmp_key_data.key_data_length[k];
1667         if( tmp_key_data.key_data_contents[k] )
1668         {
1669             kdb.key_data->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
1670             if( kdb.key_data->key_data_contents[k] == NULL )
1671             {
1672                 cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1673                 kdb.key_data = NULL;
1674                 kdb.n_key_data = 0;
1675                 ret = ENOMEM;
1676                 goto done;
1677             }
1678             memcpy( kdb.key_data->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
1679
1680             memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
1681             free( tmp_key_data.key_data_contents[k] );
1682             tmp_key_data.key_data_contents[k] = NULL;
1683         }
1684     }
1685
1686
1687
1688     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1689
1690     ret = krb5_timeofday(handle->context, &now);
1691     if (ret)
1692         goto done;
1693
1694     if ((adb.aux_attributes & KADM5_POLICY)) {
1695         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1696                                     &pol)) != KADM5_OK) 
1697            goto done;
1698         have_pol = 1;
1699
1700 #if 0
1701         /*
1702           * The spec says this check is overridden if the caller has
1703           * modify privilege.  The admin server therefore makes this
1704           * check itself (in chpass_principal_wrapper, misc.c).  A
1705           * local caller implicitly has all authorization bits.
1706           */
1707         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1708                                                   &kdb, &last_pwd))
1709              goto done;
1710         if((now - last_pwd) < pol.pw_min_life &&
1711            !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1712              ret = KADM5_PASS_TOOSOON;
1713              goto done;
1714         }
1715 #endif
1716 #if 0
1717         /*
1718          * Should we be checking/updating pw history here?
1719          */
1720         if(pol.pw_history_num > 1) {
1721             if(adb.admin_history_kvno != hist_kvno) {
1722                 ret = KADM5_BAD_HIST_KEY;
1723                 goto done;
1724             }
1725
1726             if (ret = check_pw_reuse(handle->context,
1727                                      &hist_key,
1728                                      kdb.n_key_data, kdb.key_data,
1729                                      adb.old_key_len, adb.old_keys))
1730                 goto done;
1731         }
1732 #endif
1733         
1734         if (pol.pw_max_life)
1735            kdb.pw_expiration = now + pol.pw_max_life;
1736         else
1737            kdb.pw_expiration = 0;
1738     } else {
1739         kdb.pw_expiration = 0;
1740     }
1741
1742     ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
1743     if (ret)
1744          goto done;
1745
1746     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1747         goto done;
1748
1749     ret = KADM5_OK;
1750 done:
1751     for( i = 0; i < tmp_key_data.key_data_ver; i++ )
1752     {
1753         if( tmp_key_data.key_data_contents[i] )
1754         {
1755             memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
1756             free( tmp_key_data.key_data_contents[i] );
1757         }
1758     }
1759
1760     kdb_free_entry(handle, &kdb, &adb);
1761     if (have_pol)
1762          kadm5_free_policy_ent(handle->lhandle, &pol);
1763
1764     return ret;
1765 }
1766
1767 kadm5_ret_t
1768 kadm5_setkey_principal(void *server_handle,
1769                        krb5_principal principal,
1770                        krb5_keyblock *keyblocks,
1771                        int n_keys)
1772 {
1773     return
1774         kadm5_setkey_principal_3(server_handle, principal,
1775                                  FALSE, 0, NULL,
1776                                  keyblocks, n_keys);
1777 }
1778
1779 kadm5_ret_t
1780 kadm5_setkey_principal_3(void *server_handle,
1781                          krb5_principal principal,
1782                          krb5_boolean keepold,
1783                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1784                          krb5_keyblock *keyblocks,
1785                          int n_keys)
1786 {
1787     krb5_db_entry               kdb;
1788     osa_princ_ent_rec           adb;
1789     krb5_int32                  now;
1790     kadm5_policy_ent_rec        pol;
1791     krb5_key_data               *old_key_data;
1792     int                         n_old_keys;
1793     int                         i, j, k, kvno, ret, have_pol = 0;
1794 #if 0
1795     int                         last_pwd;
1796 #endif
1797     kadm5_server_handle_t       handle = server_handle;
1798     krb5_boolean                similar;
1799     krb5_keysalt                keysalt;
1800     krb5_key_data         tmp_key_data;
1801     krb5_key_data        *tptr;
1802
1803     CHECK_HANDLE(server_handle);
1804
1805     krb5_db_clr_error();
1806
1807     if (principal == NULL || keyblocks == NULL)
1808         return EINVAL;
1809     if (hist_princ && /* this will be NULL when initializing the databse */
1810         ((krb5_principal_compare(handle->context,
1811                                  principal, hist_princ)) == TRUE))
1812         return KADM5_PROTECT_PRINCIPAL;
1813
1814     for (i = 0; i < n_keys; i++) {
1815         for (j = i+1; j < n_keys; j++) {
1816             if ((ret = krb5_c_enctype_compare(handle->context,
1817                                               keyblocks[i].enctype,
1818                                               keyblocks[j].enctype,
1819                                               &similar)))
1820                 return(ret);
1821             if (similar) {
1822                 if (n_ks_tuple) {
1823                     if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
1824                         return KADM5_SETKEY_DUP_ENCTYPES;
1825                 } else
1826                     return KADM5_SETKEY_DUP_ENCTYPES;
1827             }
1828         }
1829     }
1830
1831     if (n_ks_tuple && n_ks_tuple != n_keys)
1832         return KADM5_SETKEY3_ETYPE_MISMATCH;
1833
1834     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1835        return(ret);
1836     
1837     for (kvno = 0, i=0; i<kdb.n_key_data; i++)
1838          if (kdb.key_data[i].key_data_kvno > kvno)
1839               kvno = kdb.key_data[i].key_data_kvno;
1840
1841     if (keepold) {
1842         old_key_data = kdb.key_data;
1843         n_old_keys = kdb.n_key_data;
1844     } else {
1845         if (kdb.key_data != NULL)
1846             cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
1847         n_old_keys = 0;
1848         old_key_data = NULL;
1849     }
1850     
1851     kdb.key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, (n_keys+n_old_keys)
1852                                                  *sizeof(krb5_key_data));
1853     if (kdb.key_data == NULL)
1854     {
1855         ret= ENOMEM;
1856         goto done;
1857     }
1858
1859     memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
1860     kdb.n_key_data = 0;
1861
1862     for (i = 0; i < n_keys; i++) {
1863         if (n_ks_tuple) {
1864             keysalt.type = ks_tuple[i].ks_salttype;
1865             keysalt.data.length = 0;
1866             keysalt.data.data = NULL;
1867             if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
1868                 ret= KADM5_SETKEY3_ETYPE_MISMATCH;
1869                 goto done;
1870             }
1871         }
1872         memset( &tmp_key_data, 0, sizeof(tmp_key_data));
1873
1874         ret = krb5_dbekd_encrypt_key_data(handle->context,
1875                                           &master_keyblock,
1876                                           &keyblocks[i],
1877                                           n_ks_tuple ? &keysalt : NULL,
1878                                           kvno + 1,
1879                                           &tmp_key_data);
1880         if (ret) {
1881             goto done;
1882         }
1883         tptr = &kdb.key_data[i];
1884         for( k = 0; k < tmp_key_data.key_data_ver; k++ )
1885         {
1886             tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
1887             tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
1888             if( tmp_key_data.key_data_contents[k] )
1889             {
1890                 tptr->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
1891                 if( tptr->key_data_contents[k] == NULL )
1892                 {
1893                     int i1;
1894                     for( i1 = k; i1 < tmp_key_data.key_data_ver; i1++ )
1895                     {
1896                         if( tmp_key_data.key_data_contents[i1] )
1897                         {
1898                             memset( tmp_key_data.key_data_contents[i1], 0, tmp_key_data.key_data_length[i1]);
1899                             free( tmp_key_data.key_data_contents[i1] );
1900                         }
1901                     }
1902
1903                     ret =  ENOMEM;
1904                     goto done;
1905                 }
1906                 memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
1907
1908                 memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
1909                 free( tmp_key_data.key_data_contents[k] );
1910                 tmp_key_data.key_data_contents[k] = NULL;
1911             }
1912         }
1913         kdb.n_key_data++;
1914     }
1915
1916     /* copy old key data if necessary */
1917     for (i = 0; i < n_old_keys; i++) {
1918         kdb.key_data[i+n_keys] = old_key_data[i];
1919         memset(&old_key_data[i], 0, sizeof (krb5_key_data));
1920         kdb.n_key_data++;
1921     }
1922
1923     if( old_key_data )
1924         krb5_db_free(handle->context, old_key_data);
1925
1926     /* assert(kdb.n_key_data == n_keys + n_old_keys) */
1927     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1928
1929     if ((ret = krb5_timeofday(handle->context, &now)))
1930         goto done;
1931
1932     if ((adb.aux_attributes & KADM5_POLICY)) {
1933         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1934                                     &pol)) != KADM5_OK) 
1935            goto done;
1936         have_pol = 1;
1937
1938 #if 0
1939         /*
1940           * The spec says this check is overridden if the caller has
1941           * modify privilege.  The admin server therefore makes this
1942           * check itself (in chpass_principal_wrapper, misc.c).  A
1943           * local caller implicitly has all authorization bits.
1944           */
1945         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1946                                                   &kdb, &last_pwd))
1947              goto done;
1948         if((now - last_pwd) < pol.pw_min_life &&
1949            !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1950              ret = KADM5_PASS_TOOSOON;
1951              goto done;
1952         }
1953 #endif
1954 #if 0
1955         /*
1956          * Should we be checking/updating pw history here?
1957          */
1958         if(pol.pw_history_num > 1) {
1959             if(adb.admin_history_kvno != hist_kvno) {
1960                 ret = KADM5_BAD_HIST_KEY;
1961                 goto done;
1962             }
1963
1964             if (ret = check_pw_reuse(handle->context,
1965                                      &hist_key,
1966                                      kdb.n_key_data, kdb.key_data,
1967                                      adb.old_key_len, adb.old_keys))
1968                 goto done;
1969         }
1970 #endif
1971         
1972         if (pol.pw_max_life)
1973            kdb.pw_expiration = now + pol.pw_max_life;
1974         else
1975            kdb.pw_expiration = 0;
1976     } else {
1977         kdb.pw_expiration = 0;
1978     }
1979
1980     if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)))
1981         goto done;
1982
1983     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1984         goto done;
1985
1986     ret = KADM5_OK;
1987 done:
1988     kdb_free_entry(handle, &kdb, &adb);
1989     if (have_pol)
1990          kadm5_free_policy_ent(handle->lhandle, &pol);
1991
1992     return ret;
1993 }
1994
1995 /*
1996  * Allocate an array of n_key_data krb5_keyblocks, fill in each
1997  * element with the results of decrypting the nth key in key_data with
1998  * master_keyblock, and if n_keys is not NULL fill it in with the
1999  * number of keys decrypted.
2000  */
2001 static int decrypt_key_data(krb5_context context,
2002                             int n_key_data, krb5_key_data *key_data,
2003                             krb5_keyblock **keyblocks, int *n_keys)
2004 {
2005      krb5_keyblock *keys;
2006      int ret, i;
2007
2008      keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
2009      if (keys == NULL)
2010           return ENOMEM;
2011      memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
2012
2013      for (i = 0; i < n_key_data; i++) {
2014           ret = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
2015                                             &key_data[i], 
2016                                             &keys[i], NULL);
2017           if (ret) {
2018               for(; i >= 0; i-- )
2019               {
2020                   if( keys[i].contents )
2021                   {
2022                       memset( keys[i].contents, 0, keys[i].length );
2023                       free( keys[i].contents );
2024                   }
2025               }
2026
2027                memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
2028                free(keys);
2029                return ret;
2030           }
2031      }
2032
2033      *keyblocks = keys;
2034      if (n_keys)
2035           *n_keys = n_key_data;
2036
2037      return 0;
2038 }
2039
2040 /*
2041  * Function: kadm5_decrypt_key
2042  *
2043  * Purpose: Retrieves and decrypts a principal key.
2044  *
2045  * Arguments:
2046  *
2047  *      server_handle   (r) kadm5 handle
2048  *      entry           (r) principal retrieved with kadm5_get_principal
2049  *      ktype           (r) enctype to search for, or -1 to ignore
2050  *      stype           (r) salt type to search for, or -1 to ignore
2051  *      kvno            (r) kvno to search for, -1 for max, 0 for max
2052  *                      only if it also matches ktype and stype
2053  *      keyblock        (w) keyblock to fill in
2054  *      keysalt         (w) keysalt to fill in, or NULL
2055  *      kvnop           (w) kvno to fill in, or NULL
2056  *
2057  * Effects: Searches the key_data array of entry, which must have been
2058  * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
2059  * find a key with a specified enctype, salt type, and kvno in a
2060  * principal entry.  If not found, return ENOENT.  Otherwise, decrypt
2061  * it with the master key, and return the key in keyblock, the salt
2062  * in salttype, and the key version number in kvno.
2063  *
2064  * If ktype or stype is -1, it is ignored for the search.  If kvno is
2065  * -1, ktype and stype are ignored and the key with the max kvno is
2066  * returned.  If kvno is 0, only the key with the max kvno is returned
2067  * and only if it matches the ktype and stype; otherwise, ENOENT is
2068  * returned.
2069  */
2070 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
2071                               kadm5_principal_ent_t entry, krb5_int32
2072                               ktype, krb5_int32 stype, krb5_int32
2073                               kvno, krb5_keyblock *keyblock,
2074                               krb5_keysalt *keysalt, int *kvnop)
2075 {
2076     kadm5_server_handle_t handle = server_handle;
2077     krb5_db_entry dbent;
2078     krb5_key_data *key_data;
2079     int ret;
2080
2081     CHECK_HANDLE(server_handle);
2082
2083     if (entry->n_key_data == 0 || entry->key_data == NULL)
2084          return EINVAL;
2085
2086     /* find_enctype only uses these two fields */
2087     dbent.n_key_data = entry->n_key_data;
2088     dbent.key_data = entry->key_data;
2089     if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
2090                                     stype, kvno, &key_data)))
2091          return ret;
2092
2093     if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
2094                                            &master_keyblock, key_data,
2095                                            keyblock, keysalt)))
2096          return ret;
2097
2098     /*
2099      * Coerce the enctype of the output keyblock in case we got an
2100      * inexact match on the enctype; this behavior will go away when
2101      * the key storage architecture gets redesigned for 1.3.
2102      */
2103     keyblock->enctype = ktype;
2104
2105     if (kvnop)
2106          *kvnop = key_data->key_data_kvno;
2107
2108     return KADM5_OK;
2109 }
2110