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